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