View Javadoc

1   /**
2    * Copyright 2005-2013 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.apache.ojb.broker.core.proxy.ListProxyDefaultImpl;
30  import org.apache.ojb.broker.core.proxy.ProxyHelper;
31  import org.kuali.rice.krad.document.Document;
32  import org.kuali.rice.krad.service.DocumentSerializerService;
33  import org.kuali.rice.krad.service.PersistenceService;
34  import org.kuali.rice.krad.service.SerializerService;
35  import org.kuali.rice.krad.service.XmlObjectSerializerService;
36  import org.kuali.rice.krad.service.util.DateTimeConverter;
37  import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator;
38  import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator;
39  import org.kuali.rice.krad.util.documentserializer.PropertyType;
40  import org.kuali.rice.krad.util.documentserializer.SerializationState;
41  import org.springframework.util.AutoPopulatingList;
42  
43  import java.lang.reflect.Field;
44  import java.util.ArrayList;
45  import java.util.Iterator;
46  import java.util.List;
47  
48  /**
49   * Default implementation of the {@link DocumentSerializerService}.  If no <workflowProperties> have been defined in the
50   * data dictionary for a document type (i.e. {@link Document#getDocumentPropertySerizabilityEvaluator()} returns an instance of 
51   * {@link AlwaysTruePropertySerializibilityEvaluator}), then this service will revert to using the {@link XmlObjectSerializerService}
52   * bean, which was the old way of serializing a document for routing.  If workflowProperties are defined, then this implementation
53   * will selectively serialize items.
54   *
55   * @author Kuali Rice Team (rice.collab@kuali.org)
56   */
57  public abstract class SerializerServiceBase implements SerializerService  {
58  //	private static final Log LOG = LogFactory.getLog(SerializerServiceBase.class);
59      
60      protected PersistenceService persistenceService;
61      protected XmlObjectSerializerService xmlObjectSerializerService;
62      
63      protected XStream xstream;
64      protected ThreadLocal<SerializationState> serializationStates;
65      protected ThreadLocal<PropertySerializabilityEvaluator> evaluators;
66      
67      public SerializerServiceBase() {
68          serializationStates = new ThreadLocal<SerializationState>();
69          evaluators = new ThreadLocal<PropertySerializabilityEvaluator>();
70          
71          xstream = new XStream(new ProxyAndStateAwareJavaReflectionProvider());
72          xstream.registerConverter(new ProxyConverter(xstream.getMapper(), xstream.getReflectionProvider() ));
73          xstream.addDefaultImplementation(ArrayList.class, ListProxyDefaultImpl.class);
74          xstream.addDefaultImplementation(AutoPopulatingList.class, ListProxyDefaultImpl.class);
75          xstream.registerConverter(new AutoPopulatingListConverter(xstream.getMapper()));
76          xstream.registerConverter(new DateTimeConverter());
77      }
78          
79      public class ProxyConverter extends ReflectionConverter {
80          public ProxyConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
81              super(mapper, reflectionProvider);
82          }
83          public boolean canConvert(Class clazz) {
84              return clazz.getName().contains("CGLIB") || clazz.getName().equals("org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl");
85          }
86  
87          public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) {
88              if (obj instanceof ListProxyDefaultImpl) { 
89                  List copiedList = new ArrayList(); 
90                  List proxiedList = (List) obj; 
91                  for (Iterator iter = proxiedList.iterator(); iter.hasNext();) { 
92                      copiedList.add(iter.next()); 
93                  } 
94                  context.convertAnother( copiedList );
95              } 
96              else { 
97                  super.marshal(getPersistenceService().resolveProxy(obj), writer, context);
98              }           
99          }
100 
101         public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
102             return null;
103         }
104     }
105     
106     public class ProxyAndStateAwareJavaReflectionProvider extends PureJavaReflectionProvider {
107         @Override
108         public void visitSerializableFields(Object object, Visitor visitor) {
109             SerializationState state = serializationStates.get();
110             PropertySerializabilityEvaluator evaluator = evaluators.get();
111             
112             for (Iterator iterator = fieldDictionary.serializableFieldsFor(object.getClass()); iterator.hasNext();) {
113                 Field field = (Field) iterator.next();
114                 if (!fieldModifiersSupported(field)) {
115                     continue;
116                 }
117                 
118                 if (ignoreField(field)) {
119                     continue;
120                 }
121                 
122                 validateFieldAccess(field);
123                 
124                 initializeField(object, field);
125                 
126                 Object value = null;
127                 try {
128                     value = field.get(object);
129                 } catch (IllegalArgumentException e) {
130                     throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
131                 } catch (IllegalAccessException e) {
132                     throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
133                 }
134                 
135                 if (evaluator.isPropertySerializable(state, object, field.getName(), value)) {
136                     if (value != null && ProxyHelper.isProxy(value)) {
137                         // resolve proxies after we determine that it's serializable
138                         value = getPersistenceService().resolveProxy(value);
139                     }
140                     PropertyType propertyType = evaluator.determinePropertyType(value);
141                     state.addSerializedProperty(field.getName(), propertyType);
142                     visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value);
143                     state.removeSerializedProperty();
144                 }
145             }
146         }
147         
148         protected boolean ignoreField(Field field) {
149             return false;
150         }
151         
152         protected void initializeField(Object object, Field field) {
153         }
154     }
155 
156     public PersistenceService getPersistenceService() {
157         return this.persistenceService;
158     }
159 
160     public void setPersistenceService(PersistenceService persistenceService) {
161         this.persistenceService = persistenceService;
162     }
163     
164     public XmlObjectSerializerService getXmlObjectSerializerService() {
165         return this.xmlObjectSerializerService;
166     }
167 
168     public void setXmlObjectSerializerService(XmlObjectSerializerService xmlObjectSerializerService) {
169         this.xmlObjectSerializerService = xmlObjectSerializerService;
170     }
171     
172     protected SerializationState createNewDocumentSerializationState(Document document) {
173         return new SerializationState();
174     }
175     
176     public class AutoPopulatingListConverter extends CollectionConverter {
177 
178     	public AutoPopulatingListConverter(Mapper mapper){
179     		super(mapper);
180     	}
181 
182         @Override
183     	public boolean canConvert(Class clazz) {
184     		return clazz.equals(AutoPopulatingList.class);
185         }
186 
187     }
188 
189     
190 }
191