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