View Javadoc

1   /*
2    $Id: MetaClassRegistry.java 4554 2006-12-21 23:54:28Z blackdrag $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package groovy.lang;
47  
48  import java.lang.reflect.Constructor;
49  import java.lang.reflect.Method;
50  import java.security.AccessController;
51  import java.security.PrivilegedAction;
52  import java.util.LinkedList;
53  import java.util.List;
54  
55  import org.codehaus.groovy.classgen.ReflectorGenerator;
56  import org.codehaus.groovy.runtime.DefaultGroovyMethods;
57  import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
58  import org.codehaus.groovy.runtime.MethodHelper;
59  import org.codehaus.groovy.runtime.ReferenceMap;
60  import org.codehaus.groovy.runtime.Reflector;
61  import org.codehaus.groovy.runtime.ReflectorLoader;
62  import org.objectweb.asm.ClassWriter;
63  
64  /***
65   * A registery of MetaClass instances which caches introspection &
66   * reflection information and allows methods to be dynamically added to
67   * existing classes at runtime
68   *
69   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
70   * @author John Wilson
71   * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
72   * @version $Revision: 4554 $
73   */
74  public class MetaClassRegistry {
75      private ReferenceMap metaClasses = new ReferenceMap();
76      private ReferenceMap loaderMap = new ReferenceMap();
77      private boolean useAccessible;
78      
79      private LinkedList instanceMethods = new LinkedList();
80      private LinkedList staticMethods = new LinkedList();
81  
82      public static final int LOAD_DEFAULT = 0;
83      public static final int DONT_LOAD_DEFAULT = 1;
84      private static MetaClassRegistry instanceInclude;
85      private static MetaClassRegistry instanceExclude;
86  
87  
88      public MetaClassRegistry() {
89          this(LOAD_DEFAULT, true);
90      }
91  
92      public MetaClassRegistry(int loadDefault) {
93          this(loadDefault, true);
94      }
95  
96      /***
97       * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
98       *                      method will be called to enable access to all methods when using reflection
99       */
100     public MetaClassRegistry(boolean useAccessible) {
101         this(LOAD_DEFAULT, useAccessible);
102     }
103     
104     public MetaClassRegistry(final int loadDefault, final boolean useAccessible) {
105         this.useAccessible = useAccessible;
106         
107         if (loadDefault == LOAD_DEFAULT) {
108             // lets register the default methods
109             registerMethods(DefaultGroovyMethods.class, true);
110             registerMethods(DefaultGroovyStaticMethods.class, false);
111         }
112     }
113     
114     private void registerMethods(final Class theClass, final boolean useInstanceMethods) {
115         Method[] methods = theClass.getMethods();
116         for (int i = 0; i < methods.length; i++) {
117             Method method = methods[i];
118             if (MethodHelper.isStatic(method)) {
119                 Class[] paramTypes = method.getParameterTypes();
120                 if (paramTypes.length > 0) {
121                     if (useInstanceMethods) {
122                         instanceMethods.add(method);
123                     } else {
124                         staticMethods.add(method);
125                     }
126                 }
127             }
128         }
129     }
130 
131     public MetaClass getMetaClass(Class theClass) {
132         synchronized (theClass) {
133             MetaClass answer = (MetaClass) metaClasses.get(theClass);
134             if (answer == null) {
135                 answer = getMetaClassFor(theClass);
136                 answer.initialize();
137                 metaClasses.put(theClass, answer);
138             }
139             return answer;
140         }
141     }
142 
143     public void removeMetaClass(Class theClass) {
144         synchronized (theClass) {
145             metaClasses.remove(theClass);
146         }
147     }
148 
149 
150     /***
151      * Registers a new MetaClass in the registry to customize the type
152      *
153      * @param theClass
154      * @param theMetaClass
155      */
156     public void setMetaClass(Class theClass, MetaClass theMetaClass) {
157         synchronized(theClass) {
158             metaClasses.putStrong(theClass, theMetaClass);
159         }
160     }
161 
162     public boolean useAccessible() {
163         return useAccessible;
164     }
165 
166     private ReflectorLoader getReflectorLoader(final ClassLoader loader) {
167         synchronized (loaderMap) {
168             ReflectorLoader reflectorLoader = (ReflectorLoader) loaderMap.get(loader);
169             if (reflectorLoader == null) {
170                 reflectorLoader = (ReflectorLoader) AccessController.doPrivileged(new PrivilegedAction() {
171                     public Object run() {
172                         return new ReflectorLoader(loader);
173                     }
174                 }); 
175                 loaderMap.put(loader, reflectorLoader);
176             }
177             return reflectorLoader;
178         }
179     }
180 
181     /***
182      * Used by MetaClass when registering new methods which avoids initializing the MetaClass instances on lookup
183      */
184     MetaClass lookup(Class theClass) {
185         synchronized (theClass) {
186             MetaClass answer = (MetaClass) metaClasses.get(theClass);
187             if (answer == null) {
188                 answer = getMetaClassFor(theClass);
189                 metaClasses.put(theClass, answer);
190             }
191             return answer;
192         }
193     }
194 
195     /***
196      * Find a MetaClass for the class
197      * If there is a custom MetaClass then return an instance of that. Otherwise return an instance of the standard MetaClass
198      * 
199      * @param theClass
200      * @return An instace of the MetaClass which will handle this class
201      */
202     private MetaClass getMetaClassFor(final Class theClass) {
203         try {
204             final Class customMetaClass = Class.forName("groovy.runtime.metaclass." + theClass.getName() + "MetaClass");
205             final Constructor customMetaClassConstructor = customMetaClass.getConstructor(new Class[]{MetaClassRegistry.class, Class.class});
206             
207             return (MetaClass)customMetaClassConstructor.newInstance(new Object[]{this, theClass});
208         } catch (final ClassNotFoundException e) {
209             return new MetaClassImpl(this, theClass);
210         } catch (final Exception e) {
211             throw new GroovyRuntimeException("Could not instantiate custom Metaclass for class: " + theClass.getName() + ". Reason: " + e, e);
212         }
213     }
214 
215     /***
216      * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
217      *
218      * @param includeExtension
219      */
220     public static MetaClassRegistry getInstance(int includeExtension) {
221         if (includeExtension != DONT_LOAD_DEFAULT) {
222             if (instanceInclude == null) {
223                 instanceInclude = new MetaClassRegistry();
224             }
225             return instanceInclude;
226         }
227         else {
228             if (instanceExclude == null) {
229                 instanceExclude = new MetaClassRegistry(DONT_LOAD_DEFAULT);
230             }
231             return instanceExclude;
232         }
233     }
234 
235     public synchronized Reflector loadReflector(final Class theClass, List methods) {
236         final String name = getReflectorName(theClass);
237         ClassLoader loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
238             public Object run() {
239                 ClassLoader loader = theClass.getClassLoader();
240                 if (loader == null) loader = this.getClass().getClassLoader();
241                 return loader;
242             }
243         });
244         final ReflectorLoader rloader = getReflectorLoader(loader);
245         Class ref = rloader.getLoadedClass(name);
246         if (ref == null) {
247             /*
248              * Lets generate it && load it.
249              */                        
250             ReflectorGenerator generator = new ReflectorGenerator(methods);
251             ClassWriter cw = new ClassWriter(true);
252             generator.generate(cw, name);
253             final byte[] bytecode = cw.toByteArray();
254             ref = (Class) AccessController.doPrivileged(new PrivilegedAction() {
255                 public Object run() {
256                     return rloader.defineClass(name, bytecode, getClass().getProtectionDomain());
257                 }
258             }); 
259         }
260         try {
261             return (Reflector) ref.newInstance();
262         } catch (Exception e) {
263             throw new GroovyRuntimeException("Could not generate and load the reflector for class: " + name + ". Reason: " + e, e);
264         }
265     }
266     
267     private String getReflectorName(Class theClass) {
268         String className = theClass.getName();
269         String packagePrefix = "gjdk.";
270         String name = packagePrefix + className + "_GroovyReflector";
271         if (theClass.isArray()) {
272                Class clazz = theClass;
273                name = packagePrefix;
274                int level = 0;
275                while (clazz.isArray()) {
276                   clazz = clazz.getComponentType();
277                   level++;
278                }
279             String componentName = clazz.getName();
280             name = packagePrefix + componentName + "_GroovyReflectorArray";
281             if (level>1) name += level;
282         }
283         return name;
284     }
285 
286     List getInstanceMethods() {
287         return instanceMethods;
288     }
289 
290     List getStaticMethods() {
291         return staticMethods;
292     }
293 }