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