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