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.jdk;
019
020import java.io.Serializable;
021import java.lang.reflect.InvocationHandler;
022import java.lang.reflect.InvocationTargetException;
023import java.lang.reflect.Method;
024import java.lang.reflect.Proxy;
025
026import org.apache.commons.lang3.ArrayUtils;
027import org.apache.commons.lang3.ObjectUtils;
028import org.apache.commons.proxy2.Interceptor;
029import org.apache.commons.proxy2.Invocation;
030import org.apache.commons.proxy2.Invoker;
031import org.apache.commons.proxy2.ObjectProvider;
032import org.apache.commons.proxy2.ProxyUtils;
033import org.apache.commons.proxy2.impl.AbstractProxyFactory;
034
035/**
036 * {@link org.apache.commons.proxy2.ProxyFactory ProxyFactory} implementation that uses {@link java.lang.reflect.Proxy} proxies.
037 */
038public class JdkProxyFactory extends AbstractProxyFactory
039{
040    //******************************************************************************************************************
041    // ProxyFactory Implementation
042    //******************************************************************************************************************
043
044    /**
045     * Creates a proxy2 which delegates to the object provided by <code>delegateProvider</code>.
046     * 
047     * @param classLoader
048     *            the class loader to use when generating the proxy2
049     * @param delegateProvider
050     *            the delegate provider
051     * @param proxyClasses
052     *            the interfaces that the proxy2 should implement
053     * @return a proxy2 which delegates to the object provided by the target <code>delegateProvider>
054     */
055    @Override
056    public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> delegateProvider,
057            Class<?>... proxyClasses)
058    {
059        @SuppressWarnings("unchecked") // type inference
060        final T result = (T) Proxy.newProxyInstance(classLoader, proxyClasses, new DelegatorInvocationHandler(
061                delegateProvider));
062        return result;
063    }
064
065    /**
066     * Creates a proxy2 which passes through a {@link Interceptor interceptor} before eventually reaching the
067     * <code>target</code> object.
068     * 
069     * @param classLoader
070     *            the class loader to use when generating the proxy2
071     * @param target
072     *            the target object
073     * @param interceptor
074     *            the method interceptor
075     * @param proxyClasses
076     *            the interfaces that the proxy2 should implement.
077     * @return a proxy2 which passes through a {@link Interceptor interceptor} before eventually reaching the
078     *         <code>target</code> object.
079     */
080    @Override
081    public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
082            Class<?>... proxyClasses)
083    {
084        @SuppressWarnings("unchecked") // type inference
085        final T result = (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(target,
086                interceptor));
087        return result;
088    }
089
090    /**
091     * Creates a proxy2 which uses the provided {@link Invoker} to handle all method invocations.
092     * 
093     * @param classLoader
094     *            the class loader to use when generating the proxy2
095     * @param invoker
096     *            the invoker
097     * @param proxyClasses
098     *            the interfaces that the proxy2 should implement
099     * @return a proxy2 which uses the provided {@link Invoker} to handle all method invocations
100     */
101    @Override
102    public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class<?>... proxyClasses)
103    {
104        @SuppressWarnings("unchecked") // type inference
105        final T result = (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InvokerInvocationHandler(invoker));
106        return result;
107    }
108
109    //******************************************************************************************************************
110    // Inner Classes
111    //******************************************************************************************************************
112
113    private abstract static class AbstractInvocationHandler implements InvocationHandler, Serializable
114    {
115        /** Serialization version */
116        private static final long serialVersionUID = 1L;
117
118        @Override
119        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
120        {
121            if (ProxyUtils.isHashCode(method))
122            {
123                return Integer.valueOf(System.identityHashCode(proxy));
124            }
125            else if (ProxyUtils.isEqualsMethod(method))
126            {
127                return Boolean.valueOf(proxy == args[0]);
128            }
129            else
130            {
131                return invokeImpl(proxy, method, args);
132            }
133        }
134
135        protected abstract Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable;
136    }
137
138    private static class DelegatorInvocationHandler extends AbstractInvocationHandler
139    {
140        /** Serialization version */
141        private static final long serialVersionUID = 1L;
142
143        private final ObjectProvider<?> delegateProvider;
144
145        protected DelegatorInvocationHandler(ObjectProvider<?> delegateProvider)
146        {
147            this.delegateProvider = delegateProvider;
148        }
149
150        @Override
151        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
152        {
153            try
154            {
155                return method.invoke(delegateProvider.getObject(), args);
156            }
157            catch (InvocationTargetException e)
158            {
159                throw e.getTargetException();
160            }
161        }
162    }
163
164    private static class InterceptorInvocationHandler extends AbstractInvocationHandler
165    {
166        /** Serialization version */
167        private static final long serialVersionUID = 1L;
168
169        private final Object target;
170        private final Interceptor methodInterceptor;
171
172        public InterceptorInvocationHandler(Object target, Interceptor methodInterceptor)
173        {
174            this.target = target;
175            this.methodInterceptor = methodInterceptor;
176        }
177
178        @Override
179        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
180        {
181            final ReflectionInvocation invocation = new ReflectionInvocation(proxy, target, method, args);
182            return methodInterceptor.intercept(invocation);
183        }
184    }
185
186    private static class InvokerInvocationHandler extends AbstractInvocationHandler
187    {
188        /** Serialization version */
189        private static final long serialVersionUID = 1L;
190
191        private final Invoker invoker;
192
193        public InvokerInvocationHandler(Invoker invoker)
194        {
195            this.invoker = invoker;
196        }
197
198        @Override
199        public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable
200        {
201            return invoker.invoke(proxy, method, args);
202        }
203    }
204
205    private static class ReflectionInvocation implements Invocation
206    {
207        private final Object proxy;
208        private final Object target;
209        private final Method method;
210        private final Object[] arguments;
211
212        public ReflectionInvocation(Object proxy, Object target, Method method, Object[] arguments)
213        {
214            this.proxy = proxy;
215            this.target = target;
216            this.method = method;
217            this.arguments = ObjectUtils.defaultIfNull(ArrayUtils.clone(arguments), ProxyUtils.EMPTY_ARGUMENTS);
218        }
219
220        @Override
221        public Object[] getArguments()
222        {
223            return arguments;
224        }
225
226        @Override
227        public Method getMethod()
228        {
229            return method;
230        }
231
232        @Override
233        public Object getProxy()
234        {
235            return proxy;
236        }
237
238        @Override
239        public Object proceed() throws Throwable
240        {
241            try
242            {
243                return method.invoke(target, arguments);
244            }
245            catch (InvocationTargetException e)
246            {
247                throw e.getTargetException();
248            }
249        }
250    }
251}