001: package groovy.lang;
002:
003: import org.codehaus.groovy.runtime.InvokerHelper;
004:
005: import java.beans.IntrospectionException;
006:
007: /**
008: * As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs.
009: * It enriches MetaClass with the feature of making method invokations interceptable by
010: * an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing
011: * to add or withdraw this feature at runtime.
012: * See groovy/lang/InterceptorTest.groovy for details.
013: * @author Dierk Koenig
014: */
015: public class ProxyMetaClass extends MetaClassImpl {
016:
017: protected MetaClass adaptee = null;
018: protected Interceptor interceptor = null;
019:
020: /**
021: * convenience factory method for the most usual case.
022: */
023: public static ProxyMetaClass getInstance(Class theClass)
024: throws IntrospectionException {
025: MetaClassRegistry metaRegistry = InvokerHelper.getInstance()
026: .getMetaRegistry();
027: MetaClass meta = metaRegistry.getMetaClass(theClass);
028: return new ProxyMetaClass(metaRegistry, theClass, meta);
029: }
030:
031: /**
032: * @param adaptee the MetaClass to decorate with interceptability
033: */
034: public ProxyMetaClass(MetaClassRegistry registry, Class theClass,
035: MetaClass adaptee) throws IntrospectionException {
036: super (registry, theClass);
037: this .adaptee = adaptee;
038: if (null == adaptee)
039: throw new IllegalArgumentException(
040: "adaptee must not be null");
041: super .initialize();
042: }
043:
044: public synchronized void initialize() {
045: this .adaptee.initialize();
046: }
047:
048: /**
049: * Use the ProxyMetaClass for the given Closure.
050: * Cares for balanced register/unregister.
051: * @param closure piece of code to be executed with registered ProxyMetaClass
052: */
053: public void use(Closure closure) {
054: registry.setMetaClass(theClass, this );
055:
056: try {
057: closure.call();
058: } finally {
059: registry.setMetaClass(theClass, adaptee);
060: }
061: }
062:
063: /**
064: * Use the ProxyMetaClass for the given Closure.
065: * Cares for balanced setting/unsetting ProxyMetaClass.
066: * @param closure piece of code to be executed with ProxyMetaClass
067: */
068: public void use(GroovyObject object, Closure closure) {
069: object.setMetaClass(this );
070:
071: try {
072: closure.call();
073: } finally {
074: object.setMetaClass(adaptee);
075: }
076: }
077:
078: /**
079: * @return the interceptor in use or null if no interceptor is used
080: */
081: public Interceptor getInterceptor() {
082: return interceptor;
083: }
084:
085: /**
086: * @param interceptor may be null to reset any interception
087: */
088: public void setInterceptor(Interceptor interceptor) {
089: this .interceptor = interceptor;
090: }
091:
092: /**
093: * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
094: * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
095: * The method call is suppressed if Interceptor.doInvoke() returns false.
096: * See Interceptor for details.
097: */
098: public Object invokeMethod(final Object object,
099: final String methodName, final Object[] arguments) {
100: return doCall(object, methodName, arguments, interceptor,
101: new Callable() {
102: public Object call() {
103: return adaptee.invokeMethod(object, methodName,
104: arguments);
105: }
106: });
107: }
108:
109: /**
110: * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
111: * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
112: * The method call is suppressed if Interceptor.doInvoke() returns false.
113: * See Interceptor for details.
114: */
115: public Object invokeStaticMethod(final Object object,
116: final String methodName, final Object[] arguments) {
117: return doCall(object, methodName, arguments, interceptor,
118: new Callable() {
119: public Object call() {
120: return adaptee.invokeStaticMethod(object,
121: methodName, arguments);
122: }
123: });
124: }
125:
126: /**
127: * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
128: * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
129: * The method call is suppressed if Interceptor.doInvoke() returns false.
130: * See Interceptor for details.
131: */
132: public Object invokeConstructor(final Object[] arguments) {
133: return doCall(theClass, "ctor", arguments, interceptor,
134: new Callable() {
135: public Object call() {
136: return adaptee.invokeConstructor(arguments);
137: }
138: });
139: }
140:
141: // since Java has no Closures...
142: private interface Callable {
143: Object call();
144: }
145:
146: private Object doCall(Object object, String methodName,
147: Object[] arguments, Interceptor interceptor,
148: Callable howToInvoke) {
149: if (null == interceptor) {
150: return howToInvoke.call();
151: }
152: Object result = interceptor.beforeInvoke(object, methodName,
153: arguments);
154: if (interceptor.doInvoke()) {
155: result = howToInvoke.call();
156: }
157: result = interceptor.afterInvoke(object, methodName, arguments,
158: result);
159: return result;
160: }
161: }
|