/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package groovy.lang; /** * As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs. * It enriches MetaClass with the feature of making method invocations interceptable by * an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing * to add or withdraw this feature at runtime. * See groovy/lang/InterceptorTest.groovy for details. * <p> * WARNING: This implementation of ProxyMetaClass is NOT thread-safe and hence should only be used for * as a per-instance MetaClass running in a single thread. Do not place this MetaClass in the MetaClassRegistry * as it will result in unpredictable behaviour * * @author Dierk Koenig * @author Graeme Rocher * @see groovy.lang.MetaClassRegistry */ public class ProxyMetaClass extends MetaClassImpl implements AdaptingMetaClass { protected MetaClass adaptee = null; protected Interceptor interceptor = null; /** * convenience factory method for the most usual case. */ public static ProxyMetaClass getInstance(Class theClass) { MetaClassRegistry metaRegistry = GroovySystem.getMetaClassRegistry(); MetaClass meta = metaRegistry.getMetaClass(theClass); return new ProxyMetaClass(metaRegistry, theClass, meta); } /** * @param adaptee the MetaClass to decorate with interceptability */ public ProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) { super(registry, theClass); this.adaptee = adaptee; if (null == adaptee) throw new IllegalArgumentException("adaptee must not be null"); super.initialize(); } public synchronized void initialize() { this.adaptee.initialize(); } /** * Use the ProxyMetaClass for the given Closure. * Cares for balanced register/unregister. * * @param closure piece of code to be executed with registered ProxyMetaClass */ public Object use(Closure closure) { // grab existing meta (usually adaptee but we may have nested use calls) MetaClass origMetaClass = registry.getMetaClass(theClass); registry.setMetaClass(theClass, this); try { return closure.call(); } finally { registry.setMetaClass(theClass, origMetaClass); } } /** * Use the ProxyMetaClass for the given Closure. * Cares for balanced setting/unsetting ProxyMetaClass. * * @param closure piece of code to be executed with ProxyMetaClass */ public Object use(GroovyObject object, Closure closure) { // grab existing meta (usually adaptee but we may have nested use calls) MetaClass origMetaClass = object.getMetaClass(); object.setMetaClass(this); try { return closure.call(); } finally { object.setMetaClass(origMetaClass); } } /** * @return the interceptor in use or null if no interceptor is used */ public Interceptor getInterceptor() { return interceptor; } /** * @param interceptor may be null to reset any interception */ public void setInterceptor(Interceptor interceptor) { this.interceptor = interceptor; } /** * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor. * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods. * The method call is suppressed if Interceptor.doInvoke() returns false. * See Interceptor for details. */ public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) { return doCall(object, methodName, arguments, interceptor, new Callable() { public Object call() { return adaptee.invokeMethod(object, methodName, arguments); } }); } /** * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor. * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods. * The method call is suppressed if Interceptor.doInvoke() returns false. * See Interceptor for details. */ @Override public Object invokeMethod(final Class sender, final Object object, final String methodName, final Object[] arguments, final boolean isCallToSuper, final boolean fromInsideClass) { return doCall(object, methodName, arguments, interceptor, new Callable() { public Object call() { return adaptee.invokeMethod(sender, object, methodName, arguments, isCallToSuper, fromInsideClass); } }); } /** * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor. * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods. * The method call is suppressed if Interceptor.doInvoke() returns false. * See Interceptor for details. */ public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) { return doCall(object, methodName, arguments, interceptor, new Callable() { public Object call() { return adaptee.invokeStaticMethod(object, methodName, arguments); } }); } /** * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor. * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods. * The method call is suppressed if Interceptor.doInvoke() returns false. * See Interceptor for details. */ public Object invokeConstructor(final Object[] arguments) { return doCall(theClass, "ctor", arguments, interceptor, new Callable() { public Object call() { return adaptee.invokeConstructor(arguments); } }); } /** * Interceptors the call to getProperty if a PropertyAccessInterceptor is * available * * @param object the object to invoke the getter on * @param property the property name * @return the value of the property */ public Object getProperty(Class aClass, Object object, String property, boolean b, boolean b1) { if (null == interceptor) { return super.getProperty(aClass, object, property, b, b1); } if (interceptor instanceof PropertyAccessInterceptor) { PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor; Object result = pae.beforeGet(object, property); if (interceptor.doInvoke()) { result = super.getProperty(aClass, object, property, b, b1); } return result; } return super.getProperty(aClass, object, property, b, b1); } /** * Interceptors the call to a property setter if a PropertyAccessInterceptor * is available * * @param object The object to invoke the setter on * @param property The property name to set * @param newValue The new value of the property */ public void setProperty(Class aClass, Object object, String property, Object newValue, boolean b, boolean b1) { if (null == interceptor) { super.setProperty(aClass, object, property, newValue, b, b1); } if (interceptor instanceof PropertyAccessInterceptor) { PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor; pae.beforeSet(object, property, newValue); if (interceptor.doInvoke()) { super.setProperty(aClass, object, property, newValue, b, b1); } } else { super.setProperty(aClass, object, property, newValue, b, b1); } } public MetaClass getAdaptee() { return this.adaptee; } public void setAdaptee(MetaClass metaClass) { this.adaptee = metaClass; } // since Java has no Closures... private interface Callable { Object call(); } private Object doCall(Object object, String methodName, Object[] arguments, Interceptor interceptor, Callable howToInvoke) { if (null == interceptor) { return howToInvoke.call(); } Object result = interceptor.beforeInvoke(object, methodName, arguments); if (interceptor.doInvoke()) { result = howToInvoke.call(); } result = interceptor.afterInvoke(object, methodName, arguments, result); return result; } }