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;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotNull;
023import static org.junit.Assert.assertNotSame;
024import static org.junit.Assert.assertSame;
025import static org.junit.Assert.assertTrue;
026
027import java.io.IOException;
028import java.io.Serializable;
029import java.lang.reflect.Method;
030import java.util.Arrays;
031import java.util.Date;
032import java.util.Iterator;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.ServiceLoader;
036import java.util.SortedSet;
037import java.util.TreeSet;
038
039import org.apache.commons.proxy2.provider.BeanProvider;
040import org.apache.commons.proxy2.provider.ConstantProvider;
041import org.apache.commons.proxy2.provider.SingletonProvider;
042import org.apache.commons.proxy2.util.AbstractTestCase;
043import org.apache.commons.proxy2.util.DuplicateEcho;
044import org.apache.commons.proxy2.util.Echo;
045import org.apache.commons.proxy2.util.EchoImpl;
046import org.apache.commons.proxy2.util.SuffixInterceptor;
047import org.junit.Test;
048
049@SuppressWarnings("serial")
050public abstract class AbstractProxyFactoryTestCase extends AbstractTestCase
051{
052    //**********************************************************************************************************************
053    // Fields
054    //**********************************************************************************************************************
055
056    private static final Class<?>[] ECHO_ONLY = new Class[] { Echo.class };
057    protected final ProxyFactory factory;
058    private static final Class<?>[] COMPARABLE_ONLY = new Class[] { Comparable.class };
059
060    //**********************************************************************************************************************
061    // Constructors
062    //**********************************************************************************************************************
063
064    protected AbstractProxyFactoryTestCase()
065    {
066        final ServiceLoader<ProxyFactory> serviceLoader = ServiceLoader.load(ProxyFactory.class);
067        Iterator<ProxyFactory> iter = serviceLoader.iterator();
068        if (iter.hasNext())
069        {
070            this.factory = iter.next();
071        }
072        else
073        {
074            throw new RuntimeException("Unable to find proxy factory implementation.");
075        }
076
077    }
078
079    //**********************************************************************************************************************
080    // Other Methods
081    //**********************************************************************************************************************
082
083    private ObjectProvider<Echo> createSingletonEcho()
084    {
085        return new SingletonProvider<Echo>(new BeanProvider<Echo>(EchoImpl.class));
086    }
087
088    @Test
089    public void testInterceptorHashCode()
090    {
091        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
092        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));
093    }
094
095    @Test
096    public void testInvokerHashCode() throws Exception
097    {
098        final Echo proxy = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
099        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));
100    }
101
102    @Test
103    public void testDelegatorHashCode() throws Exception
104    {
105        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);
106        assertEquals(proxy.hashCode(), System.identityHashCode(proxy));
107    }
108
109    @Test
110    public void testInterceptorEquals()
111    {
112        final Date date = new Date();
113        final Comparable<?> proxy1 = factory.createInterceptorProxy(date, new NoOpMethodInterceptor(), COMPARABLE_ONLY);
114        final Comparable<?> proxy2 = factory.createInterceptorProxy(date, new NoOpMethodInterceptor(), COMPARABLE_ONLY);
115        assertEquals(proxy1, proxy1);
116        assertFalse(proxy1.equals(proxy2));
117        assertFalse(proxy2.equals(proxy1));
118    }
119
120    @Test
121    public void testInvokerEquals() throws Exception
122    {
123        final Comparable<?> proxy1 = factory.createInvokerProxy(new InvokerTester(), COMPARABLE_ONLY);
124        final Comparable<?> proxy2 = factory.createInvokerProxy(new InvokerTester(), COMPARABLE_ONLY);
125        assertEquals(proxy1, proxy1);
126        assertFalse(proxy1.equals(proxy2));
127        assertFalse(proxy2.equals(proxy1));
128    }
129
130    @Test
131    public void testDelegatorEquals() throws Exception
132    {
133        final Date date = new Date();
134        final Comparable<?> proxy1 = factory.createDelegatorProxy(new ConstantProvider<Date>(date), COMPARABLE_ONLY);
135        final Comparable<?> proxy2 = factory.createDelegatorProxy(new ConstantProvider<Date>(date), COMPARABLE_ONLY);
136        assertEquals(proxy1, proxy1);
137        assertFalse(proxy1.equals(proxy2));
138        assertFalse(proxy2.equals(proxy1));
139    }
140
141    @Test
142    public void testBooleanInterceptorParameter()
143    {
144        final Echo echo = factory.createInterceptorProxy(new EchoImpl(), new InterceptorTester(), ECHO_ONLY);
145        assertFalse(echo.echoBack(false));
146        assertTrue(echo.echoBack(true));
147    }
148
149    @Test
150    public void testCanProxy()
151    {
152        assertTrue(factory.canProxy(Echo.class));
153        assertFalse(factory.canProxy(EchoImpl.class));
154    }
155
156    @Test
157    public void testChangingArguments()
158    {
159        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new ChangeArgumentInterceptor(), ECHO_ONLY);
160        assertEquals("something different", proxy.echoBack("whatever"));
161    }
162
163    @Test
164    public void testCreateDelegatingProxy()
165    {
166        final Echo echo = factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);
167        echo.echo();
168        assertEquals("message", echo.echoBack("message"));
169        assertEquals("ab", echo.echoBack("a", "b"));
170    }
171
172    @Test
173    public void testCreateInterceptorProxy()
174    {
175        final Echo target = factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);
176        final Echo proxy = factory.createInterceptorProxy(target, new SuffixInterceptor(" suffix"), ECHO_ONLY);
177        proxy.echo();
178        assertEquals("message suffix", proxy.echoBack("message"));
179    }
180
181    @Test
182    public void testDelegatingProxyClassCaching() throws Exception
183    {
184        final Echo proxy1 = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);
185        final Echo proxy2 = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);
186        assertNotSame(proxy1, proxy2);
187        assertSame(proxy1.getClass(), proxy2.getClass());
188    }
189
190    @Test
191    public void testDelegatingProxyInterfaceOrder()
192    {
193        final Echo echo = factory.createDelegatorProxy(createSingletonEcho(), Echo.class, DuplicateEcho.class);
194        final List<Class<?>> expected = new LinkedList<Class<?>>(Arrays.<Class<?>> asList(Echo.class,
195                DuplicateEcho.class));
196        final List<Class<?>> actual = new LinkedList<Class<?>>(Arrays.asList(echo.getClass().getInterfaces()));
197        actual.retainAll(expected); // Doesn't alter order!
198        assertEquals(expected, actual);
199    }
200
201    @Test
202    public void testDelegatingProxySerializable() throws Exception
203    {
204        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);
205        assertSerializable(proxy);
206    }
207
208    @Test
209    public void testInterceptingProxyClassCaching() throws Exception
210    {
211        final Echo proxy1 = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
212        final Echo proxy2 = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
213        assertNotSame(proxy1, proxy2);
214        assertSame(proxy1.getClass(), proxy2.getClass());
215    }
216
217    @Test
218    public void testInterceptingProxySerializable() throws Exception
219    {
220        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
221        assertSerializable(proxy);
222    }
223
224    @Test(expected = IOException.class)
225    public void testInterceptorProxyWithCheckedException() throws Exception
226    {
227        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
228        proxy.ioException();
229    }
230
231    @Test(expected = IllegalArgumentException.class)
232    public void testInterceptorProxyWithUncheckedException() throws Exception
233    {
234        final Echo proxy = factory.createInterceptorProxy(new EchoImpl(), new NoOpMethodInterceptor(), ECHO_ONLY);
235        proxy.illegalArgument();
236    }
237
238    @Test
239    public void testInterfaceHierarchies()
240    {
241        final SortedSet<String> set = factory.createDelegatorProxy(new ConstantProvider<SortedSet<String>>(
242                new TreeSet<String>()), SortedSet.class);
243        set.add("Hello");
244    }
245
246    @Test
247    public void testInvokerProxy() throws Exception
248    {
249        final InvokerTester tester = new InvokerTester();
250        final Echo echo = factory.createInvokerProxy(tester, ECHO_ONLY);
251        echo.echoBack("hello");
252        assertEquals(Echo.class.getMethod("echoBack", String.class), tester.method);
253        assertSame(echo, tester.proxy);
254        assertNotNull(tester.args);
255        assertEquals(1, tester.args.length);
256        assertEquals("hello", tester.args[0]);
257    }
258
259    @Test
260    public void testInvokerProxyClassCaching() throws Exception
261    {
262        final Echo proxy1 = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
263        final Echo proxy2 = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
264        assertNotSame(proxy1, proxy2);
265        assertSame(proxy1.getClass(), proxy2.getClass());
266    }
267
268    @Test
269    public void testInvokerProxySerializable() throws Exception
270    {
271        final Echo proxy = factory.createInvokerProxy(new InvokerTester(), ECHO_ONLY);
272        assertSerializable(proxy);
273    }
274
275    @Test
276    public void testMethodInvocationClassCaching() throws Exception
277    {
278        final InterceptorTester tester = new InterceptorTester();
279        final EchoImpl target = new EchoImpl();
280        final Echo proxy1 = factory.createInterceptorProxy(target, tester, ECHO_ONLY);
281        final Echo proxy2 = factory.createInterceptorProxy(target, tester, Echo.class, DuplicateEcho.class);
282        proxy1.echoBack("hello1");
283        final Class<?> invocationClass1 = tester.invocationClass;
284        proxy2.echoBack("hello2");
285        assertSame(invocationClass1, tester.invocationClass);
286    }
287
288    @Test
289    public void testMethodInvocationDuplicateMethods() throws Exception
290    {
291        final InterceptorTester tester = new InterceptorTester();
292        final EchoImpl target = new EchoImpl();
293        final Echo proxy = factory.createInterceptorProxy(target, tester, Echo.class, DuplicateEcho.class);
294        proxy.echoBack("hello");
295        assertEquals(Echo.class.getMethod("echoBack", String.class), tester.method);
296    }
297
298    @Test
299    public void testMethodInvocationImplementation() throws Exception
300    {
301        final InterceptorTester tester = new InterceptorTester();
302        final EchoImpl target = new EchoImpl();
303        final Echo proxy = factory.createInterceptorProxy(target, tester, ECHO_ONLY);
304        proxy.echo();
305        assertNotNull(tester.arguments);
306        assertEquals(0, tester.arguments.length);
307        assertEquals(Echo.class.getMethod("echo"), tester.method);
308        assertSame(proxy, tester.proxy);
309        proxy.echoBack("Hello");
310        assertNotNull(tester.arguments);
311        assertEquals(1, tester.arguments.length);
312        assertEquals("Hello", tester.arguments[0]);
313        assertEquals(Echo.class.getMethod("echoBack", String.class), tester.method);
314        proxy.echoBack("Hello", "World");
315        assertNotNull(tester.arguments);
316        assertEquals(2, tester.arguments.length);
317        assertEquals("Hello", tester.arguments[0]);
318        assertEquals("World", tester.arguments[1]);
319        assertEquals(Echo.class.getMethod("echoBack", String.class, String.class), tester.method);
320    }
321
322    @Test
323    public void testPrimitiveParameter()
324    {
325        final Echo echo = factory.createDelegatorProxy(createSingletonEcho(), ECHO_ONLY);
326        assertEquals(1, echo.echoBack(1));
327    }
328
329    @Test(expected = IOException.class)
330    public void testProxyWithCheckedException() throws Exception
331    {
332        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);
333        proxy.ioException();
334    }
335
336    @Test(expected = IllegalArgumentException.class)
337    public void testProxyWithUncheckedException() throws Exception
338    {
339        final Echo proxy = factory.createDelegatorProxy(new ConstantProvider<Echo>(new EchoImpl()), Echo.class);
340        proxy.illegalArgument();
341    }
342
343    @Test
344    public void testWithNonAccessibleTargetType()
345    {
346        final Echo proxy = factory.createInterceptorProxy(new PrivateEcho(), new NoOpMethodInterceptor(), ECHO_ONLY);
347        proxy.echo();
348    }
349
350    //**********************************************************************************************************************
351    // Inner Classes
352    //**********************************************************************************************************************
353
354    private static class ChangeArgumentInterceptor implements Interceptor
355    {
356        @Override
357        public Object intercept(Invocation methodInvocation) throws Throwable
358        {
359            methodInvocation.getArguments()[0] = "something different";
360            return methodInvocation.proceed();
361        }
362    }
363
364    protected static class InterceptorTester implements Interceptor
365    {
366        private Object[] arguments;
367        private Method method;
368        private Object proxy;
369        private Class<?> invocationClass;
370
371        @Override
372        public Object intercept(Invocation methodInvocation) throws Throwable
373        {
374            arguments = methodInvocation.getArguments();
375            method = methodInvocation.getMethod();
376            proxy = methodInvocation.getProxy();
377            invocationClass = methodInvocation.getClass();
378            return methodInvocation.proceed();
379        }
380    }
381
382    protected static class InvokerTester implements Invoker
383    {
384        private Object method;
385        private Object[] args;
386        private Object proxy;
387
388        @Override
389        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
390        {
391            this.proxy = proxy;
392            this.method = method;
393            this.args = args;
394            return null;
395        }
396    }
397
398    protected static class NoOpMethodInterceptor implements Interceptor, Serializable
399    {
400        @Override
401        public Object intercept(Invocation methodInvocation) throws Throwable
402        {
403            return methodInvocation.proceed();
404        }
405    }
406
407    private static class PrivateEcho extends EchoImpl
408    {
409    }
410}