History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: XSTR-239
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Assignee: Unassigned
Reporter: Joerg Schaible
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
XStream

Xstream fails to deserialize elements in a CGLIB based HashMap proxy

Created: 19/Aug/05 05:39 AM   Updated: 07/Aug/06 09:20 PM
Component/s: None
Affects Version/s: None
Fix Version/s: 1.2

Issue Links:
Supercedes
 


 Description  « Hide
XStream seems to have trouble with CGLIB proxies based on real classes. See following effect for a proxy extending HashMap, where the ObjectName element gets lost, that is stored in the HashMap of the DelegatingHandler:


    public class DelegatingHandler implements InvocationHandler, Serializable {
        private final 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);
        }
    }

    /**
     * @throws Exception
     */
    public void testCGLib() throws Exception {
        final Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HashMap.class);
        enhancer.setInterfaces(new Class[]{Map.class});
        enhancer.setCallback(new DelegatingHandler(new HashMap()));
        final Map orig = (Map)enhancer.create();
        orig.put("ObjectName", new ObjectName("domain:type=xy"));
        final XStream xstream = new XStream(new DomDriver());
        final String xml = xstream.toXML(orig);
        System.out.println(xml);
        final Object clone = xstream.fromXML(xml);
        System.out.println(orig);
        System.out.println(clone);
        assertEquals(orig.toString(), clone.toString());
    }


 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
Joerg Schaible - 05/Apr/06 04:52 PM
A really bad coincidence happen for CGLIB proxies of real classes. A CGLIB class proxy will delegate all method calls, but represents still a complete instance of the original class. If this class is Serializable and has a readObject/writeObject methods two things may happen:
a) any direct access to a field will reference the element in the proxied instance
b) any indirect access will be delegated by the proxy
This may leed to the situation, that the values written into the stream might be from different objects.

In the above case the proxied HashMap writes a size of 0 into the stream and writes afterwards the elements of the delegate. Since those elements are written again later, XStream creates a reference. Unfortunately the readObject method of the HashMap will recognize, that it has no elements and just skip all the written values ... and all references will suddenly point to nothing.

Joerg Schaible - 05/Apr/06 04:57 PM
A workaround for the above case is a Converter for CGLIB proxies, that skips the SerializationConverter. In the sample a modified ReflectionConverter is registered with a higher priority, that handles all CGLIB-based proxies.

        xstream.registerConverter(new ReflectionConverter(xstream.getMapper(), new JVM().bestReflectionProvider()){
            public boolean canConvert(Class type) {
                return Factory.class.isAssignableFrom(type);
            }
        });

Note: It is not yet clear if this handles all the CGLIB proxy problems. Some proxies might not be initializable anymore, others have writeReplace and readResolve methods, that can/will have additionally effects.

Joerg Schaible - 07/Apr/06 08:58 AM
Next chapter: The workaround in the last comment is only valid for the current process. If another process tries to deserialize the stream, the CGLIB proxy classes are no longer known. A combination of Mapper and Converter is necessary, although it is not yet clear if such a proxy can be properly recreated at all.

Joerg Schaible - 08/Apr/06 02:36 PM
Support for the CGLIB Enhancer has been added. Supported is limited to Enhancer created proxies using CGLIB's DefaultNamingStrategy, no super class or a superclass with an accessible default constructor and a single Callback.