View Javadoc

1   package groovy.lang;
2   
3   import org.codehaus.groovy.runtime.InvokerHelper;
4   
5   import java.beans.IntrospectionException;
6   
7   /***
8    * As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs.
9    * It enriches MetaClass with the feature of making method invokations interceptable by
10   * an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing
11   * to add or withdraw this feature at runtime.
12   * See groovy/lang/InterceptorTest.groovy for details.
13   * @author Dierk Koenig
14   */
15  public class ProxyMetaClass extends MetaClass {
16  
17      protected MetaClass adaptee = null;
18      protected Interceptor interceptor = null;
19  
20      /***
21       * convenience factory method for the most usual case.
22       */
23      public static ProxyMetaClass getInstance(Class theClass) throws IntrospectionException {
24          MetaClassRegistry metaRegistry = InvokerHelper.getInstance().getMetaRegistry();
25          MetaClass meta = metaRegistry.getMetaClass(theClass);
26          return new ProxyMetaClass(metaRegistry, theClass, meta);
27      }
28      /***
29       * @param adaptee   the MetaClass to decorate with interceptability
30       */
31      public ProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) throws IntrospectionException {
32          super(registry, theClass);
33          this.adaptee = adaptee;
34          if (null == adaptee) throw new IllegalArgumentException("adaptee must not be null");
35      }
36  
37      /***
38       * Make this ProxyMetaClass the funnel for all method calls, thus enabling interceptions.
39       */
40      public void register() {
41          registry.setMetaClass(theClass, this);
42      }
43  
44      /***
45       * Reset to using the decorated adaptee, disable interception.
46       */
47      public void unRegister() {
48          registry.setMetaClass(theClass, adaptee);
49      }
50  
51      /***
52       * @return the interceptor in use or null if no interceptor is used
53       */
54      public Interceptor getInterceptor() {
55          return interceptor;
56      }
57  
58      /***
59       * @param interceptor may be null to reset any interception
60       */
61      public void setInterceptor(Interceptor interceptor) {
62          this.interceptor = interceptor;
63      }
64  
65      /***
66       * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
67       * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
68       * The method call is suppressed if Interceptor.doInvoke() returns false.
69       * See Interceptor for details.
70       */
71      public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
72          return doCall(object, methodName, arguments, new Callable(){
73              public Object call() {
74                  return adaptee.invokeMethod(object, methodName, arguments);
75              }
76          });
77      }
78      /***
79       * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
80       * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
81       * The method call is suppressed if Interceptor.doInvoke() returns false.
82       * See Interceptor for details.
83       */
84      public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
85          return doCall(object, methodName, arguments, new Callable(){
86              public Object call() {
87                  return adaptee.invokeStaticMethod(object, methodName, arguments);
88              }
89          });
90      }
91  
92      /***
93       * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
94       * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
95       * The method call is suppressed if Interceptor.doInvoke() returns false.
96       * See Interceptor for details.
97       */
98      public Object invokeConstructor(final Object[] arguments) {
99          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 }