001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.proxy2.cglib;
019
020import java.io.Serializable;
021import java.lang.reflect.Method;
022
023import net.sf.cglib.proxy.Callback;
024import net.sf.cglib.proxy.CallbackFilter;
025import net.sf.cglib.proxy.Dispatcher;
026import net.sf.cglib.proxy.Enhancer;
027import net.sf.cglib.proxy.MethodInterceptor;
028import net.sf.cglib.proxy.MethodProxy;
029
030import org.apache.commons.lang3.ArrayUtils;
031import org.apache.commons.lang3.ObjectUtils;
032import org.apache.commons.proxy2.Interceptor;
033import org.apache.commons.proxy2.Invocation;
034import org.apache.commons.proxy2.Invoker;
035import org.apache.commons.proxy2.ObjectProvider;
036import org.apache.commons.proxy2.ProxyUtils;
037import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory;
038
039/**
040 * Cglib-based {@link org.apache.commons.proxy2.ProxyFactory ProxyFactory} implementation.
041 */
042public class CglibProxyFactory extends AbstractSubclassingProxyFactory
043{
044    //******************************************************************************************************************
045    // Fields
046    //******************************************************************************************************************
047
048    private static final CallbackFilter CALLBACKFILTER = new CglibProxyFactoryCallbackFilter();
049
050    //******************************************************************************************************************
051    // ProxyFactory Implementation
052    //******************************************************************************************************************
053
054    /**
055     * {@inheritDoc}
056     */
057    @Override
058    public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> targetProvider,
059            Class<?>... proxyClasses)
060    {
061        final Enhancer enhancer = new Enhancer();
062        enhancer.setClassLoader(classLoader);
063        enhancer.setInterfaces(toInterfaces(proxyClasses));
064        enhancer.setSuperclass(getSuperclass(proxyClasses));
065        enhancer.setCallbackFilter(CALLBACKFILTER);
066        enhancer.setCallbacks(new Callback[] { new ObjectProviderDispatcher(targetProvider), new EqualsHandler(),
067                new HashCodeHandler() });
068        @SuppressWarnings("unchecked") // type inference
069        final T result = (T) enhancer.create();
070        return result;
071    }
072
073    /**
074     * {@inheritDoc}
075     */
076    @Override
077    public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
078            Class<?>... proxyClasses)
079    {
080        final Enhancer enhancer = new Enhancer();
081        enhancer.setClassLoader(classLoader);
082        enhancer.setInterfaces(toInterfaces(proxyClasses));
083        enhancer.setSuperclass(getSuperclass(proxyClasses));
084        enhancer.setCallbackFilter(CALLBACKFILTER);
085        enhancer.setCallbacks(new Callback[] { new InterceptorBridge(target, interceptor), new EqualsHandler(),
086                new HashCodeHandler() });
087        @SuppressWarnings("unchecked") // type inference
088        final T result = (T) enhancer.create();
089        return result;
090    }
091
092    /**
093     * {@inheritDoc}
094     */
095    @Override
096    public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class<?>... proxyClasses)
097    {
098        final Enhancer enhancer = new Enhancer();
099        enhancer.setClassLoader(classLoader);
100        enhancer.setInterfaces(toInterfaces(proxyClasses));
101        enhancer.setSuperclass(getSuperclass(proxyClasses));
102        enhancer.setCallbackFilter(CALLBACKFILTER);
103        enhancer.setCallbacks(
104                new Callback[] { new InvokerBridge(invoker), new EqualsHandler(), new HashCodeHandler() });
105        @SuppressWarnings("unchecked") // type inference
106        final T result = (T) enhancer.create();
107        return result;
108    }
109
110    //******************************************************************************************************************
111    // Inner Classes
112    //******************************************************************************************************************
113
114    private static class CglibProxyFactoryCallbackFilter implements CallbackFilter
115    {
116        @Override
117        public int accept(Method method)
118        {
119            if (ProxyUtils.isEqualsMethod(method))
120            {
121                return 1;
122            }
123            else if (ProxyUtils.isHashCode(method))
124            {
125                return 2;
126            }
127            else
128            {
129                return 0;
130            }
131        }
132    }
133
134    private static class EqualsHandler implements MethodInterceptor, Serializable
135    {
136        /** Serialization version */
137        private static final long serialVersionUID = 1L;
138
139        @Override
140        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
141        {
142            return Boolean.valueOf(o == objects[0]);
143        }
144    }
145
146    private static class HashCodeHandler implements MethodInterceptor, Serializable
147    {
148        /** Serialization version */
149        private static final long serialVersionUID = 1L;
150
151        @Override
152        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
153        {
154            return Integer.valueOf(System.identityHashCode(o));
155        }
156    }
157
158    private static class InterceptorBridge implements MethodInterceptor, Serializable
159    {
160        /** Serialization version */
161        private static final long serialVersionUID = 1L;
162
163        private final Object target;
164        private final Interceptor inner;
165
166        public InterceptorBridge(Object target, Interceptor inner)
167        {
168            this.inner = inner;
169            this.target = target;
170        }
171
172        @Override
173        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
174        {
175            return inner.intercept(new MethodProxyInvocation(object, target, method, args, methodProxy));
176        }
177    }
178
179    private static class InvokerBridge implements net.sf.cglib.proxy.InvocationHandler, Serializable
180    {
181        /** Serialization version */
182        private static final long serialVersionUID = 1L;
183
184        private final Invoker original;
185
186        public InvokerBridge(Invoker original)
187        {
188            this.original = original;
189        }
190
191        @Override
192        public Object invoke(Object object, Method method, Object[] objects) throws Throwable
193        {
194            return original.invoke(object, method, objects);
195        }
196    }
197
198    private static class MethodProxyInvocation implements Invocation
199    {
200        private final Object proxy;
201        private final Object target;
202        private final Method method;
203        private final Object[] args;
204        private final MethodProxy methodProxy;
205
206        public MethodProxyInvocation(Object proxy, Object target, Method method, Object[] args, MethodProxy methodProxy)
207        {
208            this.proxy = proxy;
209            this.target = target;
210            this.method = method;
211            this.methodProxy = methodProxy;
212            this.args = ObjectUtils.defaultIfNull(ArrayUtils.clone(args), ProxyUtils.EMPTY_ARGUMENTS);
213        }
214
215        @Override
216        public Method getMethod()
217        {
218            return method;
219        }
220
221        @Override
222        public Object[] getArguments()
223        {
224            return args;
225        }
226
227        @Override
228        public Object proceed() throws Throwable
229        {
230            return methodProxy.invoke(target, args);
231        }
232
233        @Override
234        public Object getProxy()
235        {
236            return proxy;
237        }
238    }
239
240    private static class ObjectProviderDispatcher implements Dispatcher, Serializable
241    {
242        /** Serialization version */
243        private static final long serialVersionUID = 1L;
244
245        private final ObjectProvider<?> delegateProvider;
246
247        public ObjectProviderDispatcher(ObjectProvider<?> delegateProvider)
248        {
249            this.delegateProvider = delegateProvider;
250        }
251
252        @Override
253        public Object loadObject()
254        {
255            return delegateProvider.getObject();
256        }
257    }
258}