All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl Maven / Gradle / Ivy

There is a newer version: 1.5.8
Show newest version
/*
 * Copyright 2003-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.groovy.runtime.metaclass;

import groovy.lang.*;
import org.codehaus.groovy.classgen.ReflectorGenerator;
import org.codehaus.groovy.reflection.CachedClass;
import org.codehaus.groovy.reflection.CachedMethod;
import org.codehaus.groovy.reflection.FastArray;
import org.codehaus.groovy.reflection.ReflectionCache;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
import org.codehaus.groovy.runtime.Reflector;
import org.objectweb.asm.ClassWriter;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;

/**
 * A registry of MetaClass instances which caches introspection &
 * reflection information and allows methods to be dynamically added to
 * existing classes at runtime
 *
 * @author James Strachan
 * @author John Wilson
 * @author Jochen Theodorou
 * @author Graeme Rocher
 *
 * @version $Revision: 9161 $
 */
public class MetaClassRegistryImpl implements MetaClassRegistry{
    private volatile int constantMetaClassCount = 0;
    private ConcurrentReaderHashMap constantMetaClasses = new ConcurrentReaderHashMap();
    private MemoryAwareConcurrentReadMap weakMetaClasses = new MemoryAwareConcurrentReadMap();
    private MemoryAwareConcurrentReadMap loaderMap = new MemoryAwareConcurrentReadMap();
    private boolean useAccessible;
    
    private FastArray instanceMethods = new FastArray();
    private FastArray staticMethods = new FastArray();

    public static final int LOAD_DEFAULT = 0;
    public static final int DONT_LOAD_DEFAULT = 1;
    private static MetaClassRegistry instanceInclude;
    private static MetaClassRegistry instanceExclude;

    public MetaClassRegistryImpl() {
        this(LOAD_DEFAULT, true);
    }

    public MetaClassRegistryImpl(int loadDefault) {
        this(loadDefault, true);
    }

    /**
     * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
     *                      method will be called to enable access to all methods when using reflection
     */
    public MetaClassRegistryImpl(boolean useAccessible) {
        this(LOAD_DEFAULT, useAccessible);
    }
    
    public MetaClassRegistryImpl(final int loadDefault, final boolean useAccessible) {
        this.useAccessible = useAccessible;

        if (loadDefault == LOAD_DEFAULT) {
            HashMap map = new HashMap();

            // lets register the default methods
            registerMethods(DefaultGroovyMethods.class, true, map);
            registerMethods(DefaultGroovyStaticMethods.class, false, map);

            for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
                Map.Entry e = (Map.Entry) it.next();
                CachedClass cls = (CachedClass) e.getKey();
                ArrayList list = (ArrayList) e.getValue();
                cls.setNewMopMethods(list);
            }
        }

        installMetaClassCreationHandle();

        final MetaClass emcMetaClass = getMetaClassFor(ExpandoMetaClass.class);
        emcMetaClass.initialize();
        constantMetaClasses.put(ExpandoMetaClass.class,emcMetaClass);
        constantMetaClassCount = 1;


   }

    /**
     * Looks for a class called 'groovy.runtime.metaclass.CustomMetaClassCreationHandle' and if it exists uses it as the MetaClassCreationHandle
     * otherwise uses the default
     *
     * @see groovy.lang.MetaClassRegistry.MetaClassCreationHandle
     */
    private void installMetaClassCreationHandle() {
	       try {
	           final Class customMetaClassHandle = Class.forName("groovy.runtime.metaclass.CustomMetaClassCreationHandle");
	           final Constructor customMetaClassHandleConstructor = customMetaClassHandle.getConstructor(new Class[]{});
				 this.metaClassCreationHandle = (MetaClassCreationHandle)customMetaClassHandleConstructor.newInstance(new Object[]{});
	       } catch (final ClassNotFoundException e) {
	           this.metaClassCreationHandle = new MetaClassCreationHandle();
	       } catch (final Exception e) {
	           throw new GroovyRuntimeException("Could not instantiate custom Metaclass creation handle: "+ e, e);
	       }
    }
    
    private void registerMethods(final Class theClass, final boolean useInstanceMethods, Map map) {
        CachedMethod[] methods = ReflectionCache.getCachedClass(theClass).getMethods();

        for (int i = 0; i < methods.length; i++) {
            CachedMethod method = methods[i];
            final int mod = method.getModifiers();
            if (Modifier.isStatic(mod) && Modifier.isPublic(mod)) {
                CachedClass[] paramTypes = method.getParameterTypes();
                if (paramTypes.length > 0) {
                    ArrayList arr = (ArrayList) map.get(paramTypes[0]);
                    if (arr == null) {
                        arr = new ArrayList(4);
                        map.put(paramTypes[0],arr);
                    }
                    if (useInstanceMethods) {
                        final NewInstanceMetaMethod metaMethod = new NewInstanceMetaMethod(method);
                        arr.add(metaMethod);
                        instanceMethods.add(metaMethod);
                    } else {
                        final NewStaticMetaMethod metaMethod = new NewStaticMetaMethod(method);
                        arr.add(metaMethod);
                        staticMethods.add(metaMethod);
                    }
                }
            }
        }
    }

    public MetaClass getMetaClass(Class theClass) {
        MetaClass answer=null;
        if (constantMetaClassCount!=0) answer = (MetaClass) constantMetaClasses.get(theClass);
        if (answer!=null) return answer;
        answer = (MetaClass) weakMetaClasses.get(theClass);
        if (answer!=null) return answer;

        synchronized (theClass) {
            answer = (MetaClass) weakMetaClasses.get(theClass);
            if (answer!=null) return answer;
            
            answer = getMetaClassFor(theClass);
            answer.initialize();
            if (GroovySystem.isKeepJavaMetaClasses()) {
                constantMetaClassCount++;
                constantMetaClasses.put(theClass,answer);
            } else {
                weakMetaClasses.put(theClass, answer);
            }
            return answer;
        }
    }

    public void removeMetaClass(Class theClass) {
        Object answer=null;
        if (constantMetaClassCount!=0) answer = constantMetaClasses.remove(theClass);
        if (answer==null) {
            weakMetaClasses.remove(theClass);
        } else {
            synchronized(theClass) {
                constantMetaClassCount--;
            }
        }
    }

    /**
     * Registers a new MetaClass in the registry to customize the type
     *
     * @param theClass
     * @param theMetaClass
     */
    public void setMetaClass(Class theClass, MetaClass theMetaClass) {
        synchronized(theClass) {
            constantMetaClassCount++;
            constantMetaClasses.put(theClass, theMetaClass);
        }
    }

    public boolean useAccessible() {
        return useAccessible;
    }

    /**
     * create Reflector loader instance if not in map. This method
     * is only used with a lock on "this" and since loaderMap is not
     * used anywhere else no sync is needed here
     */
    private ReflectorLoader getReflectorLoader(final ClassLoader loader) {
        ReflectorLoader reflectorLoader = (ReflectorLoader) loaderMap.get(loader);
        if (reflectorLoader == null) {
            reflectorLoader = (ReflectorLoader) AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    return new ReflectorLoader(loader);
                }
            }); 
            loaderMap.put(loader, reflectorLoader);
        }
        return reflectorLoader;
    }

    /**
     * Find a MetaClass for the class
     * Use the MetaClass of the superclass of the class to create the MetaClass
     * 
     * @param theClass
     * @return An instance of the MetaClass which will handle this class
     */
    private MetaClass getMetaClassFor(final Class theClass) {
        return metaClassCreationHandle.create(theClass,this);
    }

    // the following is experimental code, not intended for stable use yet
    private MetaClassCreationHandle metaClassCreationHandle = new MetaClassCreationHandle();
    /**
     * Gets a handle internally used to create MetaClass implementations
     * WARNING: experimental code, likely to change soon
     * @return the handle
     */
    public MetaClassCreationHandle getMetaClassCreationHandler() {
        return metaClassCreationHandle;
    }
    /**
     * Sets a handle internally used to create MetaClass implementations.
     * When replacing the handle with a custom version, you should
     * resuse the old handle to keep custom logic and to use the
     * default logic as fallback.
     * WARNING: experimental code, likely to change soon
     * @param handle the handle
     */
    public void setMetaClassCreationHandle(MetaClassCreationHandle handle) {
		if(handle == null) throw new IllegalArgumentException("Cannot set MetaClassCreationHandle to null value!");
        metaClassCreationHandle = handle;
    }    

    /**
     * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
     *
     * @param includeExtension
     */
    public static MetaClassRegistry getInstance(int includeExtension) {
        if (includeExtension != DONT_LOAD_DEFAULT) {
            if (instanceInclude == null) {
                instanceInclude = new MetaClassRegistryImpl();
            }
            return instanceInclude;
        }
        else {
            if (instanceExclude == null) {
                instanceExclude = new MetaClassRegistryImpl(DONT_LOAD_DEFAULT);
            }
            return instanceExclude;
        }
    }

    public synchronized Reflector loadReflector(final Class theClass, List methods) {
        final String name = getReflectorName(theClass);
        ClassLoader loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
        ClassLoader loader = theClass.getClassLoader();
        if (loader == null) loader = this.getClass().getClassLoader();
                return loader;
            }
        });
        final ReflectorLoader rloader = getReflectorLoader(loader);
        Class ref = rloader.getLoadedClass(name);
        if (ref == null) {
            /*
             * Lets generate it && load it.
             */                        
            ReflectorGenerator generator = new ReflectorGenerator(methods);
            ClassWriter cw = new ClassWriter(true);
            generator.generate(cw, name);
            final byte[] bytecode = cw.toByteArray();
            ref = (Class) AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    return rloader.defineClass(name, bytecode, getClass().getProtectionDomain());
        }
            });
        }
        try {
          return (Reflector) ref.newInstance();
        } catch (Exception e) {
            return null;
        }
    }

    private String getReflectorName(Class theClass) {
        String className = theClass.getName();
        String packagePrefix = "gjdk.";
        String name = packagePrefix + className + "_GroovyReflector";
        if (theClass.isArray()) {
               Class clazz = theClass;
               name = packagePrefix;
               int level = 0;
               while (clazz.isArray()) {
                  clazz = clazz.getComponentType();
                  level++;
               }
            String componentName = clazz.getName();
            name = packagePrefix + componentName + "_GroovyReflectorArray";
            if (level>1) name += level;
        }
        return name;
    }

    public FastArray getInstanceMethods() {
        return instanceMethods;
    }

    public FastArray getStaticMethods() {
        return staticMethods;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy