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 }