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 }