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, 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, 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, new Callable(){ 116 public Object call() { 117 return adaptee.invokeConstructor(arguments); 118 } 119 }); 120 } 121 122 public Object invokeConstructorAt(final Class at, final Object[] arguments) { 123 return doCall(theClass, "ctor", arguments, new Callable() { 124 public Object call() { 125 return adaptee.invokeConstructorAt(at, arguments); 126 } 127 }); 128 } 129 130 // since Java has no Closures... 131 private interface Callable{ 132 Object call(); 133 } 134 private Object doCall(Object object, String methodName, Object[] arguments, Callable howToInvoke) { 135 if (null == interceptor) { 136 return howToInvoke.call(); 137 } 138 Object result = interceptor.beforeInvoke(object, methodName, arguments); 139 if (interceptor.doInvoke()) { 140 result = howToInvoke.call(); 141 } 142 result = interceptor.afterInvoke(object, methodName, arguments, result); 143 return result; 144 } 145 }