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          super.initialize();
36      }
37      
38      public synchronized void initialize() {
39          this.adaptee.initialize();
40      }
41  
42      /***
43       * Use the ProxyMetaClass for the given Closure.
44       * Cares for balanced register/unregister.
45       * @param closure piece of code to be executed with registered ProxyMetaClass
46       */
47      public void use(Closure closure){
48          registry.setMetaClass(theClass, this);
49          
50          try {
51              closure.call();
52          } finally {
53              registry.setMetaClass(theClass, adaptee);
54          }
55      }
56  
57       /***
58       * Use the ProxyMetaClass for the given Closure.
59       * Cares for balanced setting/unsetting ProxyMetaClass.
60       * @param closure piece of code to be executed with ProxyMetaClass
61       */
62      public void use(GroovyObject object, Closure closure){
63          object.setMetaClass(this);
64          
65          try {
66              closure.call();
67          } finally {
68              object.setMetaClass(adaptee);
69          }
70      }
71  
72      /***
73       * @return the interceptor in use or null if no interceptor is used
74       */
75      public Interceptor getInterceptor() {
76          return interceptor;
77      }
78  
79      /***
80       * @param interceptor may be null to reset any interception
81       */
82      public void setInterceptor(Interceptor interceptor) {
83          this.interceptor = interceptor;
84      }
85  
86      /***
87       * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
88       * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
89       * The method call is suppressed if Interceptor.doInvoke() returns false.
90       * See Interceptor for details.
91       */
92      public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
93          return doCall(object, methodName, arguments, interceptor, new Callable(){
94              public Object call() {
95                  return adaptee.invokeMethod(object, methodName, arguments);
96              }
97          });
98      }
99      /***
100      * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
101      * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
102      * The method call is suppressed if Interceptor.doInvoke() returns false.
103      * See Interceptor for details.
104      */
105     public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
106         return doCall(object, methodName, arguments, interceptor, new Callable(){
107             public Object call() {
108                 return adaptee.invokeStaticMethod(object, methodName, arguments);
109             }
110         });
111     }
112 
113     /***
114      * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
115      * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
116      * The method call is suppressed if Interceptor.doInvoke() returns false.
117      * See Interceptor for details.
118      */
119     public Object invokeConstructor(final Object[] arguments) {
120         return doCall(theClass, "ctor", arguments, interceptor, new Callable(){
121             public Object call() {
122                 return adaptee.invokeConstructor(arguments);
123             }
124         });
125     }
126 
127     // since Java has no Closures...
128     private interface Callable{
129         Object call();
130     }
131     private Object doCall(Object object, String methodName, Object[] arguments, Interceptor interceptor, Callable howToInvoke) {
132         if (null == interceptor) {
133             return howToInvoke.call();
134         }
135         Object result = interceptor.beforeInvoke(object, methodName, arguments);
136         if (interceptor.doInvoke()) {
137             result = howToInvoke.call();
138         }
139         result = interceptor.afterInvoke(object, methodName, arguments, result);
140         return result;
141     }
142 }