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      }
    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     // since Java has no Closures...
    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 }