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 MetaClassImpl {
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       * Use the ProxyMetaClass for the given Closure.
39       * Cares for balanced register/unregister.
40       * @param closure piece of code to be executed with registered ProxyMetaClass
41       */
42      public void use(Closure closure){
43          registry.setMetaClass(theClass, this);
44          
45          try {
46              closure.call();
47          } finally {
48              registry.setMetaClass(theClass, adaptee);
49          }
50      }
51  
52       /***
53       * Use the ProxyMetaClass for the given Closure.
54       * Cares for balanced setting/unsetting ProxyMetaClass.
55       * @param closure piece of code to be executed with ProxyMetaClass
56       */
57      public void use(GroovyObject object, Closure closure){
58          object.setMetaClass(this);
59          
60          try {
61              closure.call();
62          } finally {
63              object.setMetaClass(adaptee);
64          }
65      }
66  
67      /***
68       * @return the interceptor in use or null if no interceptor is used
69       */
70      public Interceptor getInterceptor() {
71          return interceptor;
72      }
73  
74      /***
75       * @param interceptor may be null to reset any interception
76       */
77      public void setInterceptor(Interceptor interceptor) {
78          this.interceptor = interceptor;
79      }
80  
81      /***
82       * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
83       * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
84       * The method call is suppressed if Interceptor.doInvoke() returns false.
85       * See Interceptor for details.
86       */
87      public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
88          return doCall(object, methodName, arguments, new Callable(){
89              public Object call() {
90                  return adaptee.invokeMethod(object, methodName, arguments);
91              }
92          });
93      }
94      /***
95       * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
96       * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
97       * The method call is suppressed if Interceptor.doInvoke() returns false.
98       * See Interceptor for details.
99       */
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 }