1 package org.kuali.rice.krad.service.impl;
2
3 import com.thoughtworks.xstream.XStream;
4 import com.thoughtworks.xstream.converters.MarshallingContext;
5 import com.thoughtworks.xstream.converters.UnmarshallingContext;
6 import com.thoughtworks.xstream.converters.collections.CollectionConverter;
7 import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
8 import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
9 import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
10 import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
11 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
12 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
13 import com.thoughtworks.xstream.mapper.Mapper;
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl;
17 import org.apache.ojb.broker.core.proxy.SetProxyDefaultImpl;
18 import org.kuali.rice.krad.service.KRADServiceLocator;
19 import org.kuali.rice.krad.service.PersistenceService;
20 import org.kuali.rice.krad.service.XmlObjectSerializerService;
21
22 import java.lang.reflect.Field;
23 import java.util.*;
24
25
26
27
28 public class XmlObjectSerializerServiceImpl implements XmlObjectSerializerService {
29 private static final Log LOG = LogFactory.getLog(XmlObjectSerializerServiceImpl.class);
30
31
32 private PersistenceService persistenceService;
33
34 private XStream xstream;
35
36 public XmlObjectSerializerServiceImpl() {
37 xstream = new XStream(new ProxyAwareJavaReflectionProvider());
38 xstream.registerConverter(new ProxyConverter(xstream.getMapper(), xstream.getReflectionProvider()));
39 xstream.addDefaultImplementation(ArrayList.class, ListProxyDefaultImpl.class);
40
41
42
43 xstream.registerConverter(new ListProxyDefaultImplConverter(xstream.getMapper()));
44 xstream.registerConverter(new SetProxyDefaultImplConverter(xstream.getMapper()));
45 }
46
47
48
49
50
51 public String toXml(Object object) {
52 if (LOG.isDebugEnabled()) {
53 LOG.debug("toXml(" + object + ") : \n" + xstream.toXML(object));
54 }
55 return xstream.toXML(object);
56 }
57
58
59
60
61
62 public Object fromXml(String xml) {
63 if (LOG.isDebugEnabled()) {
64 LOG.debug("fromXml() : \n" + xml);
65 }
66 if (xml != null) {
67 xml = xml.replaceAll("--EnhancerByCGLIB--[0-9a-f]{0,8}", "");
68 }
69 return xstream.fromXML(xml);
70 }
71
72
73
74
75
76 public class ProxyConverter extends ReflectionConverter {
77 public ProxyConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
78 super(mapper, reflectionProvider);
79 }
80
81 @Override
82
83
84 @SuppressWarnings("unchecked")
85 public boolean canConvert(Class clazz) {
86 return clazz.getName().contains("CGLIB");
87 }
88
89 @Override
90 public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) {
91 super.marshal(getPersistenceService().resolveProxy(obj), writer, context);
92 }
93
94
95 }
96
97 public class ProxyAwareJavaReflectionProvider extends PureJavaReflectionProvider {
98
99 public ProxyAwareJavaReflectionProvider() {
100 super();
101 }
102
103
104
105
106 @Override
107 public void visitSerializableFields(Object object, Visitor visitor) {
108 for (Iterator iterator = fieldDictionary.fieldsFor(object.getClass()); iterator.hasNext(); ) {
109 Field field = (Field) iterator.next();
110 if (!fieldModifiersSupported(field)) {
111 continue;
112 }
113 validateFieldAccess(field);
114 Object value = null;
115 try {
116 value = field.get(object);
117 if (value != null && getPersistenceService().isProxied(value)) {
118 value = getPersistenceService().resolveProxy(value);
119 }
120 } catch (IllegalArgumentException e) {
121 throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
122 } catch (IllegalAccessException e) {
123 throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
124 }
125 visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value);
126 }
127 }
128
129
130 }
131
132 public PersistenceService getPersistenceService() {
133 if (persistenceService == null) {
134 persistenceService = KRADServiceLocator.getPersistenceService();
135 }
136 return persistenceService;
137 }
138
139
140
141
142
143
144 private static class ListProxyDefaultImplConverter extends CollectionConverter {
145
146 ListProxyDefaultImplConverter(Mapper mapper) {
147 super(mapper);
148 }
149
150 @Override
151 @SuppressWarnings("unchecked")
152 public boolean canConvert(Class type) {
153 return ListProxyDefaultImpl.class.equals(type);
154 }
155
156
157
158
159 @Override
160 @SuppressWarnings("unchecked")
161 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
162
163
164 ArrayList altered = new ArrayList((List) source);
165
166 super.marshal(altered, writer, context);
167 }
168
169
170
171
172
173
174
175 @Override
176 @SuppressWarnings("unchecked")
177 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
178
179 ArrayList result = null;
180
181 while (reader.hasMoreChildren()) {
182 try {
183 reader.moveDown();
184
185 if (reader.getNodeName().startsWith("_")) {
186
187 } else {
188 if (result == null) {
189 result = new ArrayList();
190 }
191 addCurrentElementToCollection(reader, context, result, result);
192 }
193 } finally {
194 reader.moveUp();
195 }
196 }
197
198 return result;
199 }
200 }
201
202
203
204
205
206
207 private static class SetProxyDefaultImplConverter extends CollectionConverter {
208
209 SetProxyDefaultImplConverter(Mapper mapper) {
210 super(mapper);
211 }
212
213 @Override
214 @SuppressWarnings("unchecked")
215 public boolean canConvert(Class type) {
216 return SetProxyDefaultImpl.class.equals(type);
217 }
218
219
220
221
222 @Override
223 @SuppressWarnings("unchecked")
224 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
225
226
227 HashSet altered = new HashSet((Set) source);
228
229 super.marshal(altered, writer, context);
230 }
231
232
233
234
235
236
237
238 @Override
239 @SuppressWarnings("unchecked")
240 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
241
242 HashSet result = null;
243
244 while (reader.hasMoreChildren()) {
245 try {
246 reader.moveDown();
247
248 if (reader.getNodeName().startsWith("_")) {
249
250 } else {
251 if (result == null) {
252 result = new HashSet();
253 }
254 addCurrentElementToCollection(reader, context, result, result);
255 }
256 } finally {
257 reader.moveUp();
258 }
259 }
260
261 return result;
262 }
263 }
264 }