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 }