org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of groovy-all-minimal Show documentation
Show all versions of groovy-all-minimal Show documentation
Groovy: A powerful, dynamic language for the JVM
/*
* 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.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 java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
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: 9852 $
*/
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();
private volatile Integer version = new Integer(0);
/*
We keep references to meta classes already known to this thread.
It allows us to avoid synchronization. When we need to ask global registry
we do sync but usually it is enough to check if global registry has the
same version as when we asked last time (neither removeMetaClass
nor setMetaClass were called), if version changed we prefer to forget
everything we know in the thread and start again (most likely it happens not too often).
Unfortunately, we have to keep it in weak map to avoid possible leak of classes.
*/
private class LocalMap extends WeakHashMap {
int version;
public MetaClass getMetaClass(Class theClass) {
final Integer regVer = MetaClassRegistryImpl.this.version;
final int regv = regVer.intValue();
if (version != regv) {
clear ();
}
else {
final SoftReference ref = (SoftReference) super.get(theClass);
MetaClass mc;
if (ref != null && (mc = (MetaClass) ref.get()) != null) {
return mc;
}
}
MetaClass answer = getGlobalMetaClass(theClass);
put(theClass, new SoftReference(answer));
version = MetaClassRegistryImpl.this.version.intValue();
return answer;
}
}
private ThreadLocal locallyKnown = new ThreadLocal() {
protected Object initialValue() {
return new LocalMap();
}
};
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 = metaClassCreationHandle.create(ExpandoMetaClass.class, this);
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);
}
}
}
}
}
private MetaClass getGlobalMetaClass (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 = metaClassCreationHandle.create(theClass, this);
answer.initialize();
if (GroovySystem.isKeepJavaMetaClasses()) {
constantMetaClassCount++;
constantMetaClasses.put(theClass,answer);
} else {
weakMetaClasses.put(theClass, answer);
}
}
return answer;
}
public MetaClass getMetaClass(Class theClass) {
return ((LocalMap) locallyKnown.get()).getMetaClass(theClass);
}
public synchronized void removeMetaClass(Class theClass) {
version = new Integer (version.intValue()+1);
Object answer=null;
if (constantMetaClassCount!=0) answer = constantMetaClasses.remove(theClass);
if (answer==null) {
weakMetaClasses.remove(theClass);
} else {
constantMetaClassCount--;
}
}
/**
* Registers a new MetaClass in the registry to customize the type
*
* @param theClass
* @param theMetaClass
*/
public synchronized void setMetaClass(Class theClass, MetaClass theMetaClass) {
version = new Integer (version.intValue()+1);
constantMetaClassCount++;
constantMetaClasses.put(theClass, theMetaClass);
}
public boolean useAccessible() {
return useAccessible;
}
// 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 FastArray getInstanceMethods() {
return instanceMethods;
}
public FastArray getStaticMethods() {
return staticMethods;
}
}