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 }