/* * Copyright (C) 2006, 2007, 2008 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. * * Created on 08. April 2006 by Joerg Schaible */ package com.thoughtworks.acceptance; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.reflection.CGLIBEnhancedConverter; import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.mapper.CGLIBMapper; import com.thoughtworks.xstream.mapper.MapperWrapper; //import net.sf.cglib.proxy.Callback; //import net.sf.cglib.proxy.CallbackFilter; //import net.sf.cglib.proxy.Dispatcher; //import net.sf.cglib.proxy.Enhancer; //import net.sf.cglib.proxy.Factory; //import net.sf.cglib.proxy.InvocationHandler; //import net.sf.cglib.proxy.MethodInterceptor; //import net.sf.cglib.proxy.MethodProxy; //import net.sf.cglib.proxy.NoOp; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Map; /** * @author Jörg Schaible */ public class CglibCompatibilityTest extends AbstractAcceptanceTest { protected XStream createXStream() { XStream xstream = new XStream(createDriver()) { protected MapperWrapper wrapMapper(MapperWrapper next) { return new CGLIBMapper(next); } }; xstream.registerConverter(new CGLIBEnhancedConverter(xstream.getMapper(), xstream .getReflectionProvider())); return xstream; } // public static class DelegatingHandler implements InvocationHandler, Serializable { // private Object delegate; // // public DelegatingHandler(Object delegate) { // this.delegate = delegate; // } // // public Object invoke(Object obj, Method method, Object[] args) throws Throwable { // return method.invoke(delegate, args); // } // } // public static class DelegatingInterceptor implements MethodInterceptor, Serializable, // Runnable { // private Object delegate; // // public DelegatingInterceptor(Object delegate) { // this.delegate = delegate; // } // // public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) // throws Throwable { // return method.invoke(delegate, args); // } // // public void run() { // } // } // public static class DelegatingDispatcher implements Dispatcher, Serializable { // private Object delegate; // // public DelegatingDispatcher(Object delegate) { // this.delegate = delegate; // } // // public Object loadObject() throws Exception { // return delegate; // } // } public void testSupportsClassBasedProxiesWithFactory() throws NullPointerException, MalformedURLException { // final Enhancer enhancer = new Enhancer(); // enhancer.setSuperclass(HashMap.class); // enhancer.setCallback(new DelegatingHandler(new HashMap())); // enhancer.setUseFactory(true); // true by default // final Map orig = (Map)enhancer.create(); final URL url = new URL("http://xstream.codehaus.org"); // orig.put("URL", url); final String xml = "" + "<CGLIB-enhanced-proxy>\n" + " <type>java.util.HashMap</type>\n" + " <interfaces/>\n" + " <hasFactory>true</hasFactory>\n" + " <com.thoughtworks.acceptance.CglibCompatibilityTest_-DelegatingHandler>\n" + " <delegate class=\"map\">\n" + " <entry>\n" + " <string>URL</string>\n" + " <url>http://xstream.codehaus.org</url>\n" + " </entry>\n" + " </delegate>\n" + " </com.thoughtworks.acceptance.CglibCompatibilityTest_-DelegatingHandler>\n" + "</CGLIB-enhanced-proxy>"; // final Map serialized = (Map)assertBothWays(orig, xml); // assertEquals(url, serialized.get("URL")); } public void testSupportsClassBasedProxiesWithoutFactory() throws NullPointerException, MalformedURLException { // final Enhancer enhancer = new Enhancer(); // enhancer.setSuperclass(HashMap.class); // enhancer.setCallback(new DelegatingHandler(new HashMap())); // enhancer.setUseFactory(false); // final Map orig = (Map)enhancer.create(); final URL url = new URL("http://xstream.codehaus.org"); // orig.put("URL", url); final String xml = "" + "<CGLIB-enhanced-proxy>\n" + " <type>java.util.HashMap</type>\n" + " <interfaces/>\n" + " <hasFactory>false</hasFactory>\n" + " <com.thoughtworks.acceptance.CglibCompatibilityTest_-DelegatingHandler>\n" + " <delegate class=\"map\">\n" + " <entry>\n" + " <string>URL</string>\n" + " <url>http://xstream.codehaus.org</url>\n" + " </entry>\n" + " </delegate>\n" + " </com.thoughtworks.acceptance.CglibCompatibilityTest_-DelegatingHandler>\n" + "</CGLIB-enhanced-proxy>"; // final Map serialized = (Map)assertBothWays(orig, xml); // assertEquals(url, serialized.get("URL")); } public void testSupportForClassBasedProxyWithAdditionalInterface() throws NullPointerException { // final Enhancer enhancer = new Enhancer(); // enhancer.setSuperclass(HashMap.class); // enhancer.setCallback(NoOp.INSTANCE); // enhancer.setInterfaces(new Class[]{Runnable.class}); // final Map orig = (Map)enhancer.create(); final String xml = "" + "<CGLIB-enhanced-proxy>\n" + " <type>java.util.HashMap</type>\n" + " <interfaces>\n" + " <java-class>java.lang.Runnable</java-class>\n" + " </interfaces>\n" + " <hasFactory>true</hasFactory>\n" + " <net.sf.cglib.proxy.NoOp_-1/>\n" + "</CGLIB-enhanced-proxy>"; // final Object serialized = assertBothWays(orig, xml); // assertTrue(serialized instanceof HashMap); // assertTrue(serialized instanceof Map); // assertTrue(serialized instanceof Runnable); } public void testSupportsProxiesWithMultipleInterfaces() throws NullPointerException { // final Enhancer enhancer = new Enhancer(); // enhancer.setCallback(NoOp.INSTANCE); // enhancer.setInterfaces(new Class[]{Map.class, Runnable.class}); // final Map orig = (Map)enhancer.create(); final String xml = "" + "<CGLIB-enhanced-proxy>\n" + " <type>java.lang.Object</type>\n" + " <interfaces>\n" + " <java-class>java.util.Map</java-class>\n" + " <java-class>java.lang.Runnable</java-class>\n" + " </interfaces>\n" + " <hasFactory>true</hasFactory>\n" + " <net.sf.cglib.proxy.NoOp_-1/>\n" + "</CGLIB-enhanced-proxy>"; // final Object serialized = assertBothWays(orig, xml); // assertTrue(serialized instanceof Map); // assertTrue(serialized instanceof Runnable); } public void testSupportProxiesUsingFactoryWithMultipleCallbacks() throws NullPointerException { // final Enhancer enhancer = new Enhancer(); // enhancer.setCallbacks(new Callback[]{ // // new DelegatingInterceptor(null), new DelegatingHandler(null), // new DelegatingDispatcher(null), NoOp.INSTANCE}); // enhancer.setCallbackFilter(new CallbackFilter() { // int i = 1; // // public int accept(Method method) { // if (method.getDeclaringClass() == Runnable.class) { // return 0; // } // return i < 3 ? i++ : i; // } // }); // enhancer.setInterfaces(new Class[]{Runnable.class}); // enhancer.setUseFactory(true); // final Runnable orig = (Runnable)enhancer.create(); // final String xml = xstream.toXML(orig); // final Factory deserialized = (Factory)xstream.fromXML(xml); // assertTrue("Not a Runnable anymore", deserialized instanceof Runnable); // Callback[] callbacks = deserialized.getCallbacks(); // assertEquals(4, callbacks.length); // assertTrue(callbacks[0] instanceof DelegatingInterceptor); // assertTrue(callbacks[1] instanceof DelegatingHandler); // assertTrue(callbacks[2] instanceof DelegatingDispatcher); // assertTrue(callbacks[3] instanceof NoOp); } public void testThrowsExceptionForProxiesNotUsingFactoryWithMultipleCallbacks() throws NullPointerException { // final Enhancer enhancer = new Enhancer(); // enhancer.setCallbacks(new Callback[]{ // // new DelegatingInterceptor(null), new DelegatingHandler(null), // new DelegatingDispatcher(null), NoOp.INSTANCE}); // enhancer.setCallbackFilter(new CallbackFilter() { // int i = 1; // // public int accept(Method method) { // if (method.getDeclaringClass() == Runnable.class) { // return 0; // } // return i < 3 ? i++ : i; // } // }); // enhancer.setInterfaces(new Class[]{Runnable.class}); // enhancer.setUseFactory(false); // final Runnable orig = (Runnable)enhancer.create(); // try { // xstream.toXML(orig); // fail("Thrown " + ConversionException.class.getName() + " expected"); // } catch (final ConversionException e) { // // } } public void testSupportProxiesWithMultipleCallbackSetToNull() throws NullPointerException { // final Enhancer enhancer = new Enhancer(); // enhancer.setSuperclass(HashMap.class); // enhancer.setCallback(NoOp.INSTANCE); // final HashMap orig = (HashMap)enhancer.create(); // ((Factory)orig).setCallback(0, null); final String xml = "" + "<CGLIB-enhanced-proxy>\n" + " <type>java.util.HashMap</type>\n" + " <interfaces/>\n" + " <hasFactory>true</hasFactory>\n" + " <null/>\n" + "</CGLIB-enhanced-proxy>"; // assertBothWays(orig, xml); } public void testSupportsSerialVersionUID() throws NullPointerException, NoSuchFieldException, IllegalAccessException { // final Enhancer enhancer = new Enhancer(); // enhancer.setCallback(NoOp.INSTANCE); // enhancer.setInterfaces(new Class[]{Runnable.class}); // enhancer.setSerialVersionUID(new Long(20060804L)); // final Runnable orig = (Runnable)enhancer.create(); final String xml = "" + "<CGLIB-enhanced-proxy>\n" + " <type>java.lang.Object</type>\n" + " <interfaces>\n" + " <java-class>java.lang.Runnable</java-class>\n" + " </interfaces>\n" + " <hasFactory>true</hasFactory>\n" + " <net.sf.cglib.proxy.NoOp_-1/>\n" + " <serialVersionUID>20060804</serialVersionUID>\n" + "</CGLIB-enhanced-proxy>"; // final Object serialized = assertBothWays(orig, xml); // final Field field = serialized.getClass().getDeclaredField("serialVersionUID"); // field.setAccessible(true); // assertEquals(20060804L, field.getLong(null)); } // public static class InterceptingHandler implements MethodInterceptor { // // public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) // throws Throwable { // return proxy.invokeSuper(obj, args); // } // } private final static String THRESHOLD_PARAM = "$THRESHOLD$"; private final static String CAPACITY_PARAM = "$CAPACITY$"; public void testSupportsInterceptedClassBasedProxies() throws NullPointerException, MalformedURLException { // final Enhancer enhancer = new Enhancer(); // enhancer.setSuperclass(HashMap.class); // enhancer.setCallback(new InterceptingHandler()); // enhancer.setUseFactory(true); // final Map orig = (Map)enhancer.create(); // orig.put("URL", new URL("http://xstream.codehaus.org")); final StringBuffer xml = new StringBuffer("" + "<CGLIB-enhanced-proxy>\n" + " <type>java.util.HashMap</type>\n" + " <interfaces/>\n" + " <hasFactory>true</hasFactory>\n" + " <com.thoughtworks.acceptance.CglibCompatibilityTest_-InterceptingHandler/>\n" + " <instance serialization=\"custom\">\n" + " <unserializable-parents/>\n" + " <map>\n" + " <default>\n" + " <loadFactor>0.75</loadFactor>\n" + " <threshold>$THRESHOLD$</threshold>\n" + " </default>\n" + " <int>$CAPACITY$</int>\n" + " <int>1</int>\n" + " <string>URL</string>\n" + " <url>http://xstream.codehaus.org</url>\n" + " </map>\n" + " </instance>\n" + "</CGLIB-enhanced-proxy>"); // JDK 1.3 has different threshold and capacity algorithms int idx = xml.toString().indexOf(THRESHOLD_PARAM); xml.replace(idx, idx + THRESHOLD_PARAM.length(), JVM.is14() ? "12" : "8"); idx = xml.toString().indexOf(CAPACITY_PARAM); xml.replace(idx, idx + CAPACITY_PARAM.length(), JVM.is14() ? "16" : "11"); // Map serialized = (Map)assertBothWays(orig, xml.toString()); // assertEquals(orig.toString(), serialized.toString()); } public static class ClassWithProxyMember { Runnable runnable; Map map; }; public void testSupportsProxiesAsFieldMember() throws NullPointerException { ClassWithProxyMember expected = new ClassWithProxyMember(); xstream.alias("with-proxy", ClassWithProxyMember.class); // final Enhancer enhancer = new Enhancer(); // enhancer.setCallback(NoOp.INSTANCE); // enhancer.setInterfaces(new Class[]{Map.class, Runnable.class}); // final Map orig = (Map)enhancer.create(); // expected.runnable = (Runnable)orig; // expected.map = orig; final String xml = "" + "<with-proxy>\n" + " <runnable class=\"CGLIB-enhanced-proxy\">\n" + " <type>java.lang.Object</type>\n" + " <interfaces>\n" + " <java-class>java.util.Map</java-class>\n" + " <java-class>java.lang.Runnable</java-class>\n" + " </interfaces>\n" + " <hasFactory>true</hasFactory>\n" + " <net.sf.cglib.proxy.NoOp_-1/>\n" + " </runnable>\n" + " <map class=\"CGLIB-enhanced-proxy\" reference=\"../runnable\"/>\n" + "</with-proxy>"; final Object serialized = assertBothWays(expected, xml); assertTrue(serialized instanceof ClassWithProxyMember); } public void testProxyTypeCanBeAliased() throws MalformedURLException { // final Enhancer enhancer = new Enhancer(); // enhancer.setSuperclass(HashMap.class); // enhancer.setCallback(new DelegatingHandler(new HashMap())); // final Map orig = (Map)enhancer.create(); // orig.put("URL", new URL("http://xstream.codehaus.org")); xstream.aliasType("cglib", Map.class); final String expected = "" + "<cglib>\n" + " <type>java.util.HashMap</type>\n" + " <interfaces/>\n" + " <hasFactory>true</hasFactory>\n" + " <com.thoughtworks.acceptance.CglibCompatibilityTest_-DelegatingHandler>\n" + " <delegate class=\"map\">\n" + " <entry>\n" + " <string>URL</string>\n" + " <url>http://xstream.codehaus.org</url>\n" + " </entry>\n" + " </delegate>\n" + " </com.thoughtworks.acceptance.CglibCompatibilityTest_-DelegatingHandler>\n" + "</cglib>"; // assertEquals(expected, xstream.toXML(orig)); } }