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 MetaClass { 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 closure.call(); 045 registry.setMetaClass(theClass, adaptee); 046 } 047 048 /** 049 * Use the ProxyMetaClass for the given Closure. 050 * Cares for balanced setting/unsetting ProxyMetaClass. 051 * @param closure piece of code to be executed with ProxyMetaClass 052 */ 053 public void use(GroovyObject object, Closure closure){ 054 object.setMetaClass(this); 055 closure.call(); 056 object.setMetaClass(adaptee); 057 } 058 059 /** 060 * @return the interceptor in use or null if no interceptor is used 061 */ 062 public Interceptor getInterceptor() { 063 return interceptor; 064 } 065 066 /** 067 * @param interceptor may be null to reset any interception 068 */ 069 public void setInterceptor(Interceptor interceptor) { 070 this.interceptor = interceptor; 071 } 072 073 /** 074 * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor. 075 * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods. 076 * The method call is suppressed if Interceptor.doInvoke() returns false. 077 * See Interceptor for details. 078 */ 079 public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) { 080 return doCall(object, methodName, arguments, new Callable(){ 081 public Object call() { 082 return adaptee.invokeMethod(object, methodName, arguments); 083 } 084 }); 085 } 086 /** 087 * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor. 088 * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods. 089 * The method call is suppressed if Interceptor.doInvoke() returns false. 090 * See Interceptor for details. 091 */ 092 public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) { 093 return doCall(object, methodName, arguments, new Callable(){ 094 public Object call() { 095 return adaptee.invokeStaticMethod(object, methodName, arguments); 096 } 097 }); 098 } 099 100 /** 101 * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor. 102 * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods. 103 * The method call is suppressed if Interceptor.doInvoke() returns false. 104 * See Interceptor for details. 105 */ 106 public Object invokeConstructor(final Object[] arguments) { 107 return doCall(theClass, "ctor", arguments, new Callable(){ 108 public Object call() { 109 return adaptee.invokeConstructor(arguments); 110 } 111 }); 112 } 113 114 // since Java has no Closures... 115 private interface Callable{ 116 Object call(); 117 } 118 private Object doCall(Object object, String methodName, Object[] arguments, Callable howToInvoke) { 119 if (null == interceptor) { 120 return howToInvoke.call(); 121 } 122 Object result = interceptor.beforeInvoke(object, methodName, arguments); 123 if (interceptor.doInvoke()) { 124 result = howToInvoke.call(); 125 } 126 result = interceptor.afterInvoke(object, methodName, arguments, result); 127 return result; 128 } 129 }