1 /*
2  * Copyright (C) 2004, 2005 Joe Walnes.
3  * Copyright (C) 2006, 2007, 2008, 2010, 2013, 2018 XStream Committers.
4  * All rights reserved.
5  *
6  * The software in this package is published under the terms of the BSD
7  * style license a copy of which has been included with this distribution in
8  * the LICENSE.txt file.
9  * 
10  * Created on 25. March 2004 by Joe Walnes
11  */

12 package com.thoughtworks.xstream.converters.extended;
13
14 import com.thoughtworks.xstream.converters.ConversionException;
15 import com.thoughtworks.xstream.converters.Converter;
16 import com.thoughtworks.xstream.converters.MarshallingContext;
17 import com.thoughtworks.xstream.converters.UnmarshallingContext;
18 import com.thoughtworks.xstream.core.ClassLoaderReference;
19 import com.thoughtworks.xstream.core.util.Fields;
20 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
21 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
22 import com.thoughtworks.xstream.mapper.DynamicProxyMapper;
23 import com.thoughtworks.xstream.mapper.Mapper;
24
25 import java.lang.reflect.Field;
26 import java.lang.reflect.InvocationHandler;
27 import java.lang.reflect.Method;
28 import java.lang.reflect.Proxy;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 /**
33  * Converts a dynamic proxy to XML, storing the implemented
34  * interfaces and handler.
35  *
36  * @author Joe Walnes
37  */

38 public class DynamicProxyConverter implements Converter {
39
40     private ClassLoaderReference classLoaderReference;
41     private Mapper mapper;
42     private static final Field HANDLER = Fields.locate(Proxy.class, InvocationHandler.classfalse);
43     private static final InvocationHandler DUMMY = new InvocationHandler() {
44         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
45             return null;
46         }
47     };
48
49     /**
50      * @deprecated As of 1.4.5 use {@link #DynamicProxyConverter(Mapper, ClassLoaderReference)}
51      */

52     public DynamicProxyConverter(Mapper mapper) {
53         this(mapper, DynamicProxyConverter.class.getClassLoader());
54     }
55
56     /**
57      * Construct a DynamicProxyConverter.
58      * @param mapper the Mapper chain
59      * @param classLoaderReference the reference to the {@link ClassLoader} of the XStream instance
60      * @since 1.4.5
61      */

62     public DynamicProxyConverter(Mapper mapper, ClassLoaderReference classLoaderReference) {
63         this.classLoaderReference = classLoaderReference;
64         this.mapper = mapper;
65     }
66
67     /**
68      * @deprecated As of 1.4.5 use {@link #DynamicProxyConverter(Mapper, ClassLoaderReference)}
69      */

70     public DynamicProxyConverter(Mapper mapper, ClassLoader classLoader) {
71         this(mapper,new ClassLoaderReference(classLoader));
72     }
73
74     public boolean canConvert(Class type) {
75         return type != null && (type.equals(DynamicProxyMapper.DynamicProxy.class) || Proxy.isProxyClass(type));
76     }
77
78     public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
79         InvocationHandler invocationHandler = Proxy.getInvocationHandler(source);
80         addInterfacesToXml(source, writer);
81         writer.startNode("handler");
82         String attributeName = mapper.aliasForSystemAttribute("class");
83         if (attributeName != null) {
84             writer.addAttribute(attributeName, mapper.serializedClass(invocationHandler.getClass()));
85         }
86         context.convertAnother(invocationHandler);
87         writer.endNode();
88     }
89
90     private void addInterfacesToXml(Object source, HierarchicalStreamWriter writer) {
91         Class[] interfaces = source.getClass().getInterfaces();
92         for (int i = 0; i < interfaces.length; i++) {
93             Class currentInterface = interfaces[i];
94             writer.startNode("interface");
95             writer.setValue(mapper.serializedClass(currentInterface));
96             writer.endNode();
97         }
98     }
99
100     public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
101         List interfaces = new ArrayList();
102         InvocationHandler handler = null;
103         Class handlerType = null;
104         while (reader.hasMoreChildren()) {
105             reader.moveDown();
106             String elementName = reader.getNodeName();
107             if (elementName.equals("interface")) {
108                 interfaces.add(mapper.realClass(reader.getValue()));
109             } else if (elementName.equals("handler")) {
110                 String attributeName = mapper.aliasForSystemAttribute("class");
111                 if (attributeName != null) {
112                     handlerType = mapper.realClass(reader.getAttribute(attributeName));
113                     break;
114                 }
115             }
116             reader.moveUp();
117         }
118         if (handlerType == null) {
119             throw new ConversionException("No InvocationHandler specified for dynamic proxy");
120         }
121         Class[] interfacesAsArray = new Class[interfaces.size()];
122         interfaces.toArray(interfacesAsArray);
123         Object proxy = null;
124         if (HANDLER != null) { // we will not be able to resolve references to the proxy
125             proxy = Proxy.newProxyInstance(classLoaderReference.getReference(), interfacesAsArray, DUMMY);
126         }
127         handler = (InvocationHandler) context.convertAnother(proxy, handlerType);
128         reader.moveUp();
129         if (HANDLER != null) {
130             Fields.write(HANDLER, proxy, handler);
131         } else {
132             proxy = Proxy.newProxyInstance(classLoaderReference.getReference(), interfacesAsArray, handler);
133         }
134         return proxy;
135     }
136 }
137