1
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
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.class, false);
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
52 public DynamicProxyConverter(Mapper mapper) {
53 this(mapper, DynamicProxyConverter.class.getClassLoader());
54 }
55
56
62 public DynamicProxyConverter(Mapper mapper, ClassLoaderReference classLoaderReference) {
63 this.classLoaderReference = classLoaderReference;
64 this.mapper = mapper;
65 }
66
67
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) {
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