org.codehaus.groovy.runtime.metaclass.ThreadManagedMetaBeanProperty 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.Closure;
import groovy.lang.MetaBeanProperty;
import groovy.lang.MetaMethod;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.WeakHashMap;
import org.codehaus.groovy.reflection.CachedClass;
import org.codehaus.groovy.reflection.ReflectionCache;
import org.codehaus.groovy.reflection.ParameterTypes;
/**
* This MetaBeanProperty will create a pseudo property whose value is bound to the current
* Thread using soft references. The values will go out of scope and be garabage collected when
* the Thread dies or when memory is required by the JVM
*
* The property uses an InheritableThreadLocal instance internally so child threads will still be able
* to see the property
*
* @author Graeme Rocher
* @since 1.1
*/
public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
private static final CachedClass[] ZERO_ARGUMENT_LIST = new CachedClass[0];
private static final ThreadLocal PROPERTY_INSTANCE_HOLDER = new InheritableThreadLocal();
private Class declaringClass;
private ThreadBoundGetter getter;
private ThreadBoundSetter setter;
private Object initialValue;
private static final String PROPERTY_SET_PREFIX = "set";
private Closure initialValueCreator;
/**
* Retrieves the initial value of the ThreadBound property
*
* @return The initial value
*/
public synchronized Object getInitialValue() {
return getInitialValue(null);
}
public synchronized Object getInitialValue(Object object) {
if (initialValueCreator != null) {
return initialValueCreator.call(object);
}
return initialValue;
}
/**
* Closure responsible for creating the initial value of thread-managed bean properties
*
* @param callable The closure responsible for creating the initial value
*/
public void setInitialValueCreator(Closure callable) {
this.initialValueCreator = callable;
}
/**
* Constructs a new ThreadManagedBeanProperty for the given arguments
*
* @param declaringClass The class that declares the property
* @param name The name of the property
* @param type The type of the property
* @param iv The properties initial value
*/
public ThreadManagedMetaBeanProperty(Class declaringClass, String name, Class type, Object iv) {
super(name, type, null, null);
this.type = type;
this.declaringClass = declaringClass;
this.getter = new ThreadBoundGetter(name);
this.setter = new ThreadBoundSetter(name);
initialValue = iv;
}
/**
* Constructs a new ThreadManagedBeanProperty for the given arguments
*
* @param declaringClass The class that declares the property
* @param name The name of the property
* @param type The type of the property
* @param initialValueCreator The closure responsible for creating the initial value
*/
public ThreadManagedMetaBeanProperty(Class declaringClass, String name, Class type, Closure initialValueCreator) {
super(name, type, null, null);
this.type = type;
this.declaringClass = declaringClass;
this.getter = new ThreadBoundGetter(name);
this.setter = new ThreadBoundSetter(name);
this.initialValueCreator = initialValueCreator;
}
private static Object getThreadBoundPropertyValue(Object obj, String name, Object initialValue) {
Map propertyMap = getThreadBoundPropertMap();
String key = System.identityHashCode(obj) + name;
if (propertyMap.containsKey(key)) {
return propertyMap.get(key);
} else {
propertyMap.put(key, initialValue);
return initialValue;
}
}
private static Map getThreadBoundPropertMap() {
Map propertyMap = (Map) PROPERTY_INSTANCE_HOLDER.get();
if (propertyMap == null) {
propertyMap = new WeakHashMap();
PROPERTY_INSTANCE_HOLDER.set(propertyMap);
}
return propertyMap;
}
private static Object setThreadBoundPropertyValue(Object obj, String name, Object value) {
Map propertyMap = getThreadBoundPropertMap();
String key = System.identityHashCode(obj) + name;
return propertyMap.put(key, value);
}
/* (non-Javadoc)
* @see groovy.lang.MetaBeanProperty#getGetter()
*/
public MetaMethod getGetter() {
return this.getter;
}
/* (non-Javadoc)
* @see groovy.lang.MetaBeanProperty#getSetter()
*/
public MetaMethod getSetter() {
return this.setter;
}
/**
* Accesses the ThreadBound state of the property as a getter
*
* @author Graeme Rocher
*/
class ThreadBoundGetter extends MetaMethod {
private final String name, name0;
private final ParameterTypes pt = new ParameterTypes(new CachedClass[0]);
public ThreadBoundGetter(String name) {
this.name = getGetterName(name, type);
this.name0 = name;
}
public int getModifiers() {
return Modifier.PUBLIC;
}
public String getName() {
return name;
}
public Class getReturnType() {
return type;
}
public CachedClass getDeclaringClass() {
return ReflectionCache.getCachedClass(declaringClass);
}
public ParameterTypes getParamTypes() {
return pt;
}
/* (non-Javadoc)
* @see groovy.lang.MetaMethod#invoke(java.lang.Object, java.lang.Object[])
*/
public Object invoke(Object object, Object[] arguments) {
return getThreadBoundPropertyValue(object, name0, getInitialValue());
}
}
/**
* Sets the ThreadBound state of the property like a setter
*
* @author Graeme Rocher
*/
private class ThreadBoundSetter extends MetaMethod {
private final String name, name0;
private ParameterTypes pt = new ParameterTypes(new CachedClass[]{ReflectionCache.getCachedClass(type)});
public ThreadBoundSetter(String name) {
this.name = getSetterName(name);
this.name0 = name;
}
public int getModifiers() {
return Modifier.PUBLIC;
}/* (non-Javadoc)
* @see groovy.lang.MetaMethod#getName()
*/
public String getName() {
return name;
}
public Class getReturnType() {
return type;
}
public CachedClass getDeclaringClass() {
return ReflectionCache.getCachedClass(declaringClass);
}
public ParameterTypes getParamTypes() {
return pt;
}
/* (non-Javadoc)
* @see groovy.lang.MetaMethod#invoke(java.lang.Object, java.lang.Object[])
*/
public Object invoke(Object object, Object[] arguments) {
return setThreadBoundPropertyValue(object, name0, arguments[0]);
}
}
private String getGetterName(String propertyName, Class type) {
String prefix = type == boolean.class || type == Boolean.class ? "is" : "get";
return prefix + Character.toUpperCase(propertyName.charAt(0))
+ propertyName.substring(1);
}
private String getSetterName(String propertyName) {
return PROPERTY_SET_PREFIX + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
}
}