001 package groovy.lang;
002
003 import org.codehaus.groovy.runtime.InvokerHelper;
004
005 import java.beans.IntrospectionException;
006
007 /**
008 * As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs.
009 * It enriches MetaClass with the feature of making method invokations interceptable by
010 * an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing
011 * to add or withdraw this feature at runtime.
012 * See groovy/lang/InterceptorTest.groovy for details.
013 * @author Dierk Koenig
014 */
015 public class ProxyMetaClass extends MetaClassImpl {
016
017 protected MetaClass adaptee = null;
018 protected Interceptor interceptor = null;
019
020 /**
021 * convenience factory method for the most usual case.
022 */
023 public static ProxyMetaClass getInstance(Class theClass) throws IntrospectionException {
024 MetaClassRegistry metaRegistry = InvokerHelper.getInstance().getMetaRegistry();
025 MetaClass meta = metaRegistry.getMetaClass(theClass);
026 return new ProxyMetaClass(metaRegistry, theClass, meta);
027 }
028 /**
029 * @param adaptee the MetaClass to decorate with interceptability
030 */
031 public ProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) throws IntrospectionException {
032 super(registry, theClass);
033 this.adaptee = adaptee;
034 if (null == adaptee) throw new IllegalArgumentException("adaptee must not be null");
035 }
036
037 /**
038 * Use the ProxyMetaClass for the given Closure.
039 * Cares for balanced register/unregister.
040 * @param closure piece of code to be executed with registered ProxyMetaClass
041 */
042 public void use(Closure closure){
043 registry.setMetaClass(theClass, this);
044
045 try {
046 closure.call();
047 } finally {
048 registry.setMetaClass(theClass, adaptee);
049 }
050 }
051
052 /**
053 * Use the ProxyMetaClass for the given Closure.
054 * Cares for balanced setting/unsetting ProxyMetaClass.
055 * @param closure piece of code to be executed with ProxyMetaClass
056 */
057 public void use(GroovyObject object, Closure closure){
058 object.setMetaClass(this);
059
060 try {
061 closure.call();
062 } finally {
063 object.setMetaClass(adaptee);
064 }
065 }
066
067 /**
068 * @return the interceptor in use or null if no interceptor is used
069 */
070 public Interceptor getInterceptor() {
071 return interceptor;
072 }
073
074 /**
075 * @param interceptor may be null to reset any interception
076 */
077 public void setInterceptor(Interceptor interceptor) {
078 this.interceptor = interceptor;
079 }
080
081 /**
082 * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
083 * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
084 * The method call is suppressed if Interceptor.doInvoke() returns false.
085 * See Interceptor for details.
086 */
087 public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
088 return doCall(object, methodName, arguments, interceptor, new Callable(){
089 public Object call() {
090 return adaptee.invokeMethod(object, methodName, arguments);
091 }
092 });
093 }
094 /**
095 * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
096 * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
097 * The method call is suppressed if Interceptor.doInvoke() returns false.
098 * See Interceptor for details.
099 */
100 public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
101 return doCall(object, methodName, arguments, interceptor, new Callable(){
102 public Object call() {
103 return adaptee.invokeStaticMethod(object, methodName, arguments);
104 }
105 });
106 }
107
108 /**
109 * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
110 * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
111 * The method call is suppressed if Interceptor.doInvoke() returns false.
112 * See Interceptor for details.
113 */
114 public Object invokeConstructor(final Object[] arguments) {
115 return doCall(theClass, "ctor", arguments, interceptor, new Callable(){
116 public Object call() {
117 return adaptee.invokeConstructor(arguments);
118 }
119 });
120 }
121
122 // since Java has no Closures...
123 private interface Callable{
124 Object call();
125 }
126 private Object doCall(Object object, String methodName, Object[] arguments, Interceptor interceptor, Callable howToInvoke) {
127 if (null == interceptor) {
128 return howToInvoke.call();
129 }
130 Object result = interceptor.beforeInvoke(object, methodName, arguments);
131 if (interceptor.doInvoke()) {
132 result = howToInvoke.call();
133 }
134 result = interceptor.afterInvoke(object, methodName, arguments, result);
135 return result;
136 }
137 }