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
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 }