groovy.lang.ProxyMetaClass Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 groovy.lang;
/**
* As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs.
* It enriches MetaClass with the feature of making method invocations interceptable by
* an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing
* to add or withdraw this feature at runtime.
* See groovy/lang/InterceptorTest.groovy for details.
*
* WARNING: This implementation of ProxyMetaClass is NOT thread-safe and hence should only be used for
* as a per-instance MetaClass running in a single thread. Do not place this MetaClass in the MetaClassRegistry
* as it will result in unpredictable behaviour
*
* @author Dierk Koenig
* @author Graeme Rocher
* @see groovy.lang.MetaClassRegistry
*/
public class ProxyMetaClass extends MetaClassImpl implements AdaptingMetaClass {
protected MetaClass adaptee = null;
protected Interceptor interceptor = null;
/**
* convenience factory method for the most usual case.
*/
public static ProxyMetaClass getInstance(Class theClass) {
MetaClassRegistry metaRegistry = GroovySystem.getMetaClassRegistry();
MetaClass meta = metaRegistry.getMetaClass(theClass);
return new ProxyMetaClass(metaRegistry, theClass, meta);
}
/**
* @param adaptee the MetaClass to decorate with interceptability
*/
public ProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) {
super(registry, theClass);
this.adaptee = adaptee;
if (null == adaptee) throw new IllegalArgumentException("adaptee must not be null");
super.initialize();
}
public synchronized void initialize() {
this.adaptee.initialize();
}
/**
* Use the ProxyMetaClass for the given Closure.
* Cares for balanced register/unregister.
*
* @param closure piece of code to be executed with registered ProxyMetaClass
*/
public Object use(Closure closure) {
// grab existing meta (usually adaptee but we may have nested use calls)
MetaClass origMetaClass = registry.getMetaClass(theClass);
registry.setMetaClass(theClass, this);
try {
return closure.call();
} finally {
registry.setMetaClass(theClass, origMetaClass);
}
}
/**
* Use the ProxyMetaClass for the given Closure.
* Cares for balanced setting/unsetting ProxyMetaClass.
*
* @param closure piece of code to be executed with ProxyMetaClass
*/
public Object use(GroovyObject object, Closure closure) {
// grab existing meta (usually adaptee but we may have nested use calls)
MetaClass origMetaClass = object.getMetaClass();
object.setMetaClass(this);
try {
return closure.call();
} finally {
object.setMetaClass(origMetaClass);
}
}
/**
* @return the interceptor in use or null if no interceptor is used
*/
public Interceptor getInterceptor() {
return interceptor;
}
/**
* @param interceptor may be null to reset any interception
*/
public void setInterceptor(Interceptor interceptor) {
this.interceptor = interceptor;
}
/**
* Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
* With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
* The method call is suppressed if Interceptor.doInvoke() returns false.
* See Interceptor for details.
*/
public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
return doCall(object, methodName, arguments, interceptor, new Callable() {
public Object call() {
return adaptee.invokeMethod(object, methodName, arguments);
}
});
}
/**
* Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
* With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
* The method call is suppressed if Interceptor.doInvoke() returns false.
* See Interceptor for details.
*/
public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
return doCall(object, methodName, arguments, interceptor, new Callable() {
public Object call() {
return adaptee.invokeStaticMethod(object, methodName, arguments);
}
});
}
/**
* Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
* With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
* The method call is suppressed if Interceptor.doInvoke() returns false.
* See Interceptor for details.
*/
public Object invokeConstructor(final Object[] arguments) {
return doCall(theClass, "ctor", arguments, interceptor, new Callable() {
public Object call() {
return adaptee.invokeConstructor(arguments);
}
});
}
/**
* Interceptors the call to getProperty if a PropertyAccessInterceptor is
* available
*
* @param object the object to invoke the getter on
* @param property the property name
* @return the value of the property
*/
public Object getProperty(Class aClass, Object object, String property, boolean b, boolean b1) {
if (null == interceptor) {
return super.getProperty(aClass, object, property, b, b1);
}
if (interceptor instanceof PropertyAccessInterceptor) {
PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor;
Object result = pae.beforeGet(object, property);
if (interceptor.doInvoke()) {
result = super.getProperty(aClass, object, property, b, b1);
}
return result;
}
return super.getProperty(aClass, object, property, b, b1);
}
/**
* Interceptors the call to a property setter if a PropertyAccessInterceptor
* is available
*
* @param object The object to invoke the setter on
* @param property The property name to set
* @param newValue The new value of the property
*/
public void setProperty(Class aClass, Object object, String property, Object newValue, boolean b, boolean b1) {
if (null == interceptor) {
super.setProperty(aClass, object, property, newValue, b, b1);
}
if (interceptor instanceof PropertyAccessInterceptor) {
PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor;
pae.beforeSet(object, property, newValue);
if (interceptor.doInvoke()) {
super.setProperty(aClass, object, property, newValue, b, b1);
}
} else {
super.setProperty(aClass, object, property, newValue, b, b1);
}
}
public MetaClass getAdaptee() {
return this.adaptee;
}
public void setAdaptee(MetaClass metaClass) {
this.adaptee = metaClass;
}
// since Java has no Closures...
private interface Callable {
Object call();
}
private Object doCall(Object object, String methodName, Object[] arguments, Interceptor interceptor, Callable howToInvoke) {
if (null == interceptor) {
return howToInvoke.call();
}
Object result = interceptor.beforeInvoke(object, methodName, arguments);
if (interceptor.doInvoke()) {
result = howToInvoke.call();
}
result = interceptor.afterInvoke(object, methodName, arguments, result);
return result;
}
}