View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.service.impl;
17  
18  import com.thoughtworks.xstream.XStream;
19  import com.thoughtworks.xstream.converters.MarshallingContext;
20  import com.thoughtworks.xstream.converters.UnmarshallingContext;
21  import com.thoughtworks.xstream.converters.collections.CollectionConverter;
22  import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
23  import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
24  import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
25  import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
26  import com.thoughtworks.xstream.io.HierarchicalStreamReader;
27  import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
28  import com.thoughtworks.xstream.mapper.Mapper;
29  import org.kuali.rice.krad.document.Document;
30  import org.kuali.rice.krad.service.DocumentSerializerService;
31  import org.kuali.rice.krad.service.LegacyDataAdapter;
32  import org.kuali.rice.krad.service.SerializerService;
33  import org.kuali.rice.krad.service.XmlObjectSerializerService;
34  import org.kuali.rice.krad.service.util.DateTimeConverter;
35  import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator;
36  import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator;
37  import org.kuali.rice.krad.util.documentserializer.PropertyType;
38  import org.kuali.rice.krad.util.documentserializer.SerializationState;
39  import org.springframework.beans.factory.annotation.Required;
40  import org.springframework.util.AutoPopulatingList;
41  
42  import java.lang.reflect.Field;
43  import java.util.ArrayList;
44  import java.util.Iterator;
45  import java.util.List;
46  
47  /**
48   * Default implementation of the {@link DocumentSerializerService}.  If no <workflowProperties> have been defined in the
49   * data dictionary for a document type (i.e. {@link Document#getDocumentPropertySerizabilityEvaluator()} returns an instance of
50   * {@link AlwaysTruePropertySerializibilityEvaluator}), then this service will revert to using the {@link XmlObjectSerializerService}
51   * bean, which was the old way of serializing a document for routing.  If workflowProperties are defined, then this implementation
52   * will selectively serialize items.
53   *
54   * @author Kuali Rice Team (rice.collab@kuali.org)
55   */
56  public abstract class SerializerServiceBase implements SerializerService  {
57  
58  	protected LegacyDataAdapter legacyDataAdapter;
59      protected XmlObjectSerializerService xmlObjectSerializerService;
60  
61      protected XStream xstream;
62      protected ThreadLocal<SerializationState> serializationStates;
63      protected ThreadLocal<PropertySerializabilityEvaluator> evaluators;
64  
65      public SerializerServiceBase() {
66          serializationStates = new ThreadLocal<SerializationState>();
67          evaluators = new ThreadLocal<PropertySerializabilityEvaluator>();
68  
69          xstream = new XStream(new ProxyAndStateAwareJavaReflectionProvider());
70          xstream.registerConverter(new ProxyConverter(xstream.getMapper(), xstream.getReflectionProvider() ));
71          try {
72          	Class<?> objListProxyClass = Class.forName("org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl");
73              xstream.addDefaultImplementation(ArrayList.class, objListProxyClass);
74              xstream.addDefaultImplementation(AutoPopulatingList.class, objListProxyClass);
75          } catch ( Exception ex ) {
76          	// Do nothing - this will blow if the OJB class does not exist, which it won't in some installs
77          }
78          xstream.registerConverter(new AutoPopulatingListConverter(xstream.getMapper()));
79          xstream.registerConverter(new DateTimeConverter());
80      }
81  
82      /**
83       * @see org.kuali.rice.krad.service.DocumentSerializerService#serializeDocumentToXmlForRouting(org.kuali.rice.krad.document.Document)
84       */
85      public String serializeBusinessObjectToXml(Object businessObject) {
86          PropertySerializabilityEvaluator propertySerizabilityEvaluator =
87                  getPropertySerizabilityEvaluator(businessObject);
88          evaluators.set(propertySerizabilityEvaluator);
89          SerializationState state = new SerializationState(); //createNewDocumentSerializationState(document);
90          serializationStates.set(state);
91  
92          //Object xmlWrapper = null;//wrapDocumentWithMetadata(document);
93          String xml;
94          if (propertySerizabilityEvaluator instanceof AlwaysTruePropertySerializibilityEvaluator) {
95              xml = getXmlObjectSerializerService().toXml(businessObject);
96          } else {
97              xml = xstream.toXML(businessObject);
98          }
99  
100         evaluators.set(null);
101         serializationStates.set(null);
102         return xml;
103     }
104 
105     /**
106      * Method called by the ProxyAndStateAwareJavaReflectionProvider during serialization to determine if a field
107      * should be omitted from the serialized form.
108      *
109      * <p>This is a short circuit check that will avoid more expensive calls in to the PropertySerializabilityEvaluator
110      * if it returns true.</p>
111      *
112      * @param field the field
113      * @return true if the field should be omitted
114      */
115     protected boolean ignoreField(Field field) {
116         return false;
117     }
118 
119     /**
120      * Get the appropriate {@link PropertySerializabilityEvaluator} for the given dataObject.
121      *
122      * @param dataObject the data object
123      * @return the evaluator
124      */
125     protected abstract PropertySerializabilityEvaluator getPropertySerizabilityEvaluator(Object dataObject);
126 
127     public class ProxyConverter extends ReflectionConverter {
128         public ProxyConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
129             super(mapper, reflectionProvider);
130         }
131         @Override
132 		public boolean canConvert(Class clazz) {
133             return clazz.getName().contains("CGLIB") || clazz.getName().equals("org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl");
134         }
135 
136         @Override
137 		public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) {
138             if (obj.getClass().getName().equals("org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl")) {
139                 List copiedList = new ArrayList();
140                 List proxiedList = (List) obj;
141                 for (Iterator iter = proxiedList.iterator(); iter.hasNext();) {
142                     copiedList.add(iter.next());
143                 }
144                 context.convertAnother( copiedList );
145             }
146             else {
147                 super.marshal(legacyDataAdapter.resolveProxy(obj), writer, context);
148             }
149         }
150 
151         @Override
152 		public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
153             return null;
154         }
155     }
156 
157     public class ProxyAndStateAwareJavaReflectionProvider extends PureJavaReflectionProvider {
158         @Override
159         public void visitSerializableFields(Object object, Visitor visitor) {
160             SerializationState state = serializationStates.get();
161             PropertySerializabilityEvaluator evaluator = evaluators.get();
162 
163             for (Iterator iterator = fieldDictionary.serializableFieldsFor(object.getClass()); iterator.hasNext();) {
164                 Field field = (Field) iterator.next();
165                 if (!fieldModifiersSupported(field)) {
166                     continue;
167                 }
168 
169                 if (ignoreField(field)) {
170                     continue;
171                 }
172 
173                 validateFieldAccess(field);
174 
175                 initializeField(object, field);
176 
177                 Object value = null;
178                 try {
179                     value = field.get(object);
180                 } catch (IllegalArgumentException e) {
181                     throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
182                 } catch (IllegalAccessException e) {
183                     throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
184                 }
185 
186                 if (evaluator.isPropertySerializable(state, object, field.getName(), value)) {
187                     if (value != null && legacyDataAdapter.isProxied(value)) {
188                         // resolve proxies after we determine that it's serializable
189                         value = legacyDataAdapter.resolveProxy(value);
190                     }
191                     PropertyType propertyType = evaluator.determinePropertyType(value);
192                     state.addSerializedProperty(field.getName(), propertyType);
193                     visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value);
194                     state.removeSerializedProperty();
195                 }
196             }
197         }
198 
199         protected void initializeField(Object object, Field field) {
200         }
201     }
202 
203     public class AutoPopulatingListConverter extends CollectionConverter {
204 
205     	public AutoPopulatingListConverter(Mapper mapper){
206     		super(mapper);
207     	}
208 
209         @Override
210     	public boolean canConvert(Class clazz) {
211     		return clazz.equals(AutoPopulatingList.class);
212         }
213 
214     }
215 
216     protected XmlObjectSerializerService getXmlObjectSerializerService() {
217         return this.xmlObjectSerializerService;
218     }
219 
220     @Required
221     public void setXmlObjectSerializerService(XmlObjectSerializerService xmlObjectSerializerService) {
222         this.xmlObjectSerializerService = xmlObjectSerializerService;
223     }
224 
225     protected SerializationState createNewDocumentSerializationState(Document document) {
226         return new SerializationState();
227     }
228 
229     @Required
230 	public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) {
231 		this.legacyDataAdapter = legacyDataAdapter;
232 	}
233 }
234