KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > fortress > impl > factory > BCELWrapperGenerator


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.avalon.fortress.impl.factory;
19
20 import org.apache.bcel.Constants;
21 import org.apache.bcel.classfile.JavaClass;
22 import org.apache.bcel.classfile.Method;
23 import org.apache.bcel.generic.ClassGen;
24 import org.apache.bcel.generic.Type;
25 import org.apache.bcel.util.ClassLoaderRepository;
26 import org.apache.bcel.util.Repository;
27
28 /**
29  * Create the BCELWrapper for the component
30  *
31  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
32  */

33
34
35 final class BCELWrapperGenerator
36
37 {
38     /**
39      * The BCEL util.Repository instance to use when loading JavaClass instances.
40      * Default is to use util.ClassLoaderRepository with thread context classloader.
41      */

42     private Repository m_repository = null;
43
44     /**
45      * The suffix to be appended to the name of the wrapped class when creating
46      * the name of the wrapper class.
47      */

48     private static final String JavaDoc WRAPPER_CLASS_SUFFIX = "$BCELWrapper";
49
50     /**
51      * The name of the superclass of the wrapper class to be generated.
52      */

53     private static final String JavaDoc WRAPPER_SUPERCLASS_NAME = "java.lang.Object";
54
55     /**
56      * The name of the interface each generated wrapper class has to implement.
57      */

58     private static final String JavaDoc WRAPPER_CLASS_INTERFACE_NAME =
59         WrapperClass.class.getName();
60
61     /**
62      * The <code>BCELCodeGenerator</code> to use for
63      * byte code generation.
64      */

65     private final BCELCodeGenerator m_codeGenerator;
66
67     /**
68      * The <code>ClassGen</code> instance to use for byte code generation.
69      */

70     private ClassGen m_classGenerator = null;
71
72     /**
73      * The <code>ClassLoader</code> to use when loading a class generated by this
74      * <code>BCELWrapperGenerator</code>.
75      */

76     private final BCELClassLoader m_bcelClassLoader;
77
78     /**
79      * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
80      */

81     private final class BCELClassLoader extends ClassLoader JavaDoc
82
83     {
84         /**
85          * The <i>byte code</i> representing the wrapper class created by the
86          * enclosing <code>BCELWrapperGenerated</code>. This field will be
87          * managed by the <code>BCELWrapperGenerator</code>.
88          */

89         private byte[] m_byteCode = null;
90
91         /**
92          * Constructs a <code>BCELClassLoader</code> with the specified class
93          * loader as its parent.
94          *
95          * @param parent The parent <code>ClassLoader</code>
96          */

97         public BCELClassLoader( final ClassLoader JavaDoc parent )
98         {
99             super( parent );
100         }
101
102         /**
103          * Constructs a <code>BCELClassLoader</code> with no parent.
104          *
105          */

106         public BCELClassLoader()
107         {
108             super();
109         }
110
111         /**
112          *
113          * @see java.lang.ClassLoader#findClass(String)
114          */

115         protected Class JavaDoc findClass( final String JavaDoc name ) throws ClassNotFoundException JavaDoc
116         {
117             // Check if the requested class falls within the domain of
118
// the BCELWrapperGenerator
119
if ( name.endsWith( WRAPPER_CLASS_SUFFIX ) )
120             {
121                 return super.defineClass(
122                     name,
123                     getByteCode(),
124                     0,
125                     getByteCode().length );
126             }
127
128             return super.findClass( name );
129         }
130
131         /**
132          * Passes in the <code>byte code</code> to use when loading a class
133          * created by the <code>BCELWrapperGenerator</code>.
134          * This method will be called by the <code>BCELWrapperGenerator</code>
135          * prior to asking this class loader for the generated wrapper class.
136          *
137          * @param byteCode The <code>byte code</code> to use when loading
138          * a generated class
139          *
140          * @throws IllegalArgumentException If <code>byteCode</code> is null or
141          * empty
142          */

143         private void setByteCode( final byte[] byteCode )
144             throws IllegalArgumentException JavaDoc
145         {
146             if ( byteCode == null || byteCode.length == 0 )
147             {
148                 final String JavaDoc message =
149                     "Parameter byteCode must neither be <null> nor empty.";
150                 throw new IllegalArgumentException JavaDoc( message );
151             }
152
153             m_byteCode = byteCode;
154         }
155
156         /**
157          * Clears the <code>byte code</code>, setting it to <code>null</code>.
158          * This method will be called by the <code>BCELWrapperGenerator</code>
159          * immediately after this class loader has returned the generated wrapper
160          * class.
161          */

162         private void clearByteCode()
163         {
164             m_byteCode = null;
165         }
166
167         /**
168          * Returns the <code>byte code</code> to use when loading a generated
169          * class.
170          *
171          * @return The <code>byte code</code> for defining the generated class
172          */

173         private byte[] getByteCode()
174         {
175             return m_byteCode;
176         }
177     } // End BCELClassLoader
178

179     /**
180      * No-args default constructor.
181      */

182     public BCELWrapperGenerator()
183     {
184         m_codeGenerator = new BCELCodeGenerator();
185         ClassLoader JavaDoc contextClassLoader =
186                 Thread.currentThread().getContextClassLoader();
187         m_repository = new ClassLoaderRepository( contextClassLoader );
188         m_bcelClassLoader =
189             new BCELClassLoader( contextClassLoader );
190     }
191
192     /**
193      */

194     public synchronized Class JavaDoc createWrapper( final Class JavaDoc classToWrap ) throws Exception JavaDoc
195     {
196         if ( classToWrap == null )
197         {
198             final String JavaDoc message = "Class to wrap must not be <null>.";
199             throw new IllegalArgumentException JavaDoc( message );
200         }
201
202         // Guess work interfaces ...
203
final Class JavaDoc[] interfacesToImplement =
204             AbstractObjectFactory.guessWorkInterfaces( classToWrap );
205
206         // Get JavaClasses as required by BCEL for the wrapped class and its interfaces
207
final JavaClass javaClassToWrap = lookupClass( classToWrap );
208         final JavaClass[] javaInterfacesToImplement =
209             lookupClasses( interfacesToImplement );
210
211         // The name of the wrapper class to be generated
212
final String JavaDoc wrapperClassName =
213             classToWrap.getName() + WRAPPER_CLASS_SUFFIX;
214
215         Class JavaDoc generatedClass;
216         synchronized ( Type.class )
217         {
218             // Create BCEL class generator
219
m_classGenerator =
220                 new ClassGen(
221                     wrapperClassName,
222                     WRAPPER_SUPERCLASS_NAME,
223                     null,
224                     Constants.ACC_FINAL
225                 |Constants.ACC_PUBLIC
226                 |Constants.ACC_SUPER,
227                     extractInterfaceNames( interfacesToImplement ) );
228
229             // Initialize method-field generator
230
m_codeGenerator.init(
231                 wrapperClassName,
232                 WRAPPER_SUPERCLASS_NAME,
233                 javaClassToWrap,
234                 m_classGenerator );
235
236             final byte[] byteCode = buildWrapper( javaInterfacesToImplement );
237             m_bcelClassLoader.setByteCode( byteCode );
238             generatedClass = m_bcelClassLoader.loadClass( wrapperClassName );
239             m_bcelClassLoader.clearByteCode();
240         }
241
242         return generatedClass;
243     }
244
245     /**
246      * Takes a <code>Class</code> instance as a its parameter and returns corresponding
247      * the <code>JavaClass</code> instance as used by <b>BCEL</b>.
248      *
249      * @param clazz The <code>Class</code> instance we want to turn into a
250      * <code>JavaClass</code>
251      * @return The <code>JavaClass</code> representing the given <code>Class</code>
252      * instance
253      */

254     private JavaClass lookupClass( final Class JavaDoc clazz ) throws Exception JavaDoc
255     {
256         String JavaDoc className = clazz.getName();
257         try
258         {
259             JavaClass jClazz = m_repository.findClass( className );
260             if ( jClazz == null )
261             return m_repository.loadClass( className );
262             else
263             return jClazz;
264         } catch ( ClassNotFoundException JavaDoc e ) { return null; }
265     }
266
267     /**
268      * Takes an array of <code>Class</code> instances and returns an array holding
269      * the corresponding <code>JavaClass</code> instances as used by <b>BCEL</b>.
270      *
271      * @param classes An array holding <code>Class</code> instances we
272      * want to turn into <code>JavaClass</code> instances
273      * @return JavaClass[] An array of <code>JavaClass</code> instances representing
274      * the given <code>Class</code> instances
275      */

276     private JavaClass[] lookupClasses( final Class JavaDoc[] classes ) throws Exception JavaDoc
277     {
278         final JavaClass[] javaClasses = new JavaClass[classes.length];
279         for ( int i = 0; i < classes.length; ++i )
280         {
281             javaClasses[i] = lookupClass( classes[i] );
282         }
283
284         return javaClasses;
285     }
286
287     /**
288      * Takes an array of <code>Class</code> instances supposed to represent
289      * interfaces and returns a list of the names of those interfaces.
290      *
291      * @param interfaces An array of <code>Class</code> instances
292      * @return String[] An array of the names of those <code>Class</code> instances
293      */

294     private String JavaDoc[] extractInterfaceNames( final Class JavaDoc[] interfaces )
295     {
296         final String JavaDoc[] ifaceNames = new String JavaDoc[interfaces.length + 1];
297         for ( int i = 0; i < interfaces.length; ++i )
298         {
299             ifaceNames[i] = interfaces[i].getName();
300         }
301         // Add interface WrapperClass to the list of interfaces to be implemented
302
ifaceNames[ifaceNames.length - 1] = WRAPPER_CLASS_INTERFACE_NAME;
303
304         return ifaceNames;
305     }
306
307     /**
308      * Generates the wrapper byte code for a given interface.
309      *
310      * @param interfacesToImplement The interfaces we want to generate wrapper
311      * byte code for
312      * @return byte[] The generated byte code
313      */

314     private byte[] buildWrapper( final JavaClass[] interfacesToImplement ) throws Exception JavaDoc
315     {
316         // Create field for the wrapped class
317
m_classGenerator.addField( m_codeGenerator.createWrappedClassField() );
318
319         // Create default constructor
320
m_classGenerator.addMethod( m_codeGenerator.createDefaultConstructor() );
321
322         //Create field accessor for wrapped class instance
323
m_classGenerator.addMethod(
324             m_codeGenerator.createWrappedClassAccessor() );
325
326         // Implement interfaces
327
Method[] interfaceMethods = m_codeGenerator.createImplementation( interfacesToImplement );
328
329         for ( int j = 0; j < interfaceMethods.length; ++j )
330         {
331             m_classGenerator.addMethod( interfaceMethods[j] );
332         }
333
334         return m_classGenerator.getJavaClass().getBytes();
335     }
336 }
337
Popular Tags