001 /**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.service.impl;
017
018 import com.thoughtworks.xstream.XStream;
019 import com.thoughtworks.xstream.converters.MarshallingContext;
020 import com.thoughtworks.xstream.converters.UnmarshallingContext;
021 import com.thoughtworks.xstream.converters.collections.CollectionConverter;
022 import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
023 import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
024 import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
025 import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
026 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
027 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
028 import com.thoughtworks.xstream.mapper.Mapper;
029 import org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl;
030 import org.apache.ojb.broker.core.proxy.ProxyHelper;
031 import org.kuali.rice.krad.document.Document;
032 import org.kuali.rice.krad.service.DocumentSerializerService;
033 import org.kuali.rice.krad.service.PersistenceService;
034 import org.kuali.rice.krad.service.SerializerService;
035 import org.kuali.rice.krad.service.XmlObjectSerializerService;
036 import org.kuali.rice.krad.util.DateTimeConverter;
037 import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator;
038 import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator;
039 import org.kuali.rice.krad.util.documentserializer.PropertyType;
040 import org.kuali.rice.krad.util.documentserializer.SerializationState;
041 import org.springframework.util.AutoPopulatingList;
042
043 import java.lang.reflect.Field;
044 import java.util.ArrayList;
045 import java.util.Iterator;
046 import java.util.List;
047
048 /**
049 * Default implementation of the {@link DocumentSerializerService}. If no <workflowProperties> have been defined in the
050 * data dictionary for a document type (i.e. {@link Document#getDocumentPropertySerizabilityEvaluator()} returns an instance of
051 * {@link AlwaysTruePropertySerializibilityEvaluator}), then this service will revert to using the {@link XmlObjectSerializerService}
052 * bean, which was the old way of serializing a document for routing. If workflowProperties are defined, then this implementation
053 * will selectively serialize items.
054 */
055 public abstract class SerializerServiceBase implements SerializerService {
056 // private static final Log LOG = LogFactory.getLog(SerializerServiceBase.class);
057
058 protected PersistenceService persistenceService;
059 protected XmlObjectSerializerService xmlObjectSerializerService;
060
061 protected XStream xstream;
062 protected ThreadLocal<SerializationState> serializationStates;
063 protected ThreadLocal<PropertySerializabilityEvaluator> evaluators;
064
065 public SerializerServiceBase() {
066 serializationStates = new ThreadLocal<SerializationState>();
067 evaluators = new ThreadLocal<PropertySerializabilityEvaluator>();
068
069 xstream = new XStream(new ProxyAndStateAwareJavaReflectionProvider());
070 xstream.registerConverter(new ProxyConverter(xstream.getMapper(), xstream.getReflectionProvider() ));
071 xstream.addDefaultImplementation(ArrayList.class, ListProxyDefaultImpl.class);
072 xstream.addDefaultImplementation(AutoPopulatingList.class, ListProxyDefaultImpl.class);
073 xstream.registerConverter(new AutoPopulatingListConverter(xstream.getMapper()));
074 xstream.registerConverter(new DateTimeConverter());
075 }
076
077 public class ProxyConverter extends ReflectionConverter {
078 public ProxyConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
079 super(mapper, reflectionProvider);
080 }
081 public boolean canConvert(Class clazz) {
082 return clazz.getName().contains("CGLIB") || clazz.getName().equals("org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl");
083 }
084
085 public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) {
086 if (obj instanceof ListProxyDefaultImpl) {
087 List copiedList = new ArrayList();
088 List proxiedList = (List) obj;
089 for (Iterator iter = proxiedList.iterator(); iter.hasNext();) {
090 copiedList.add(iter.next());
091 }
092 context.convertAnother( copiedList );
093 }
094 else {
095 super.marshal(getPersistenceService().resolveProxy(obj), writer, context);
096 }
097 }
098
099 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
100 return null;
101 }
102 }
103
104 public class ProxyAndStateAwareJavaReflectionProvider extends PureJavaReflectionProvider {
105 @Override
106 public void visitSerializableFields(Object object, Visitor visitor) {
107 SerializationState state = serializationStates.get();
108 PropertySerializabilityEvaluator evaluator = evaluators.get();
109
110 for (Iterator iterator = fieldDictionary.serializableFieldsFor(object.getClass()); iterator.hasNext();) {
111 Field field = (Field) iterator.next();
112 if (!fieldModifiersSupported(field)) {
113 continue;
114 }
115
116 if (ignoreField(field)) {
117 continue;
118 }
119
120 validateFieldAccess(field);
121
122 initializeField(object, field);
123
124 Object value = null;
125 try {
126 value = field.get(object);
127 } catch (IllegalArgumentException e) {
128 throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
129 } catch (IllegalAccessException e) {
130 throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e);
131 }
132
133 if (evaluator.isPropertySerializable(state, object, field.getName(), value)) {
134 if (value != null && ProxyHelper.isProxy(value)) {
135 // resolve proxies after we determine that it's serializable
136 value = getPersistenceService().resolveProxy(value);
137 }
138 PropertyType propertyType = evaluator.determinePropertyType(value);
139 state.addSerializedProperty(field.getName(), propertyType);
140 visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value);
141 state.removeSerializedProperty();
142 }
143 }
144 }
145
146 protected boolean ignoreField(Field field) {
147 return false;
148 }
149
150 protected void initializeField(Object object, Field field) {
151 }
152 }
153
154 public PersistenceService getPersistenceService() {
155 return this.persistenceService;
156 }
157
158 public void setPersistenceService(PersistenceService persistenceService) {
159 this.persistenceService = persistenceService;
160 }
161
162 public XmlObjectSerializerService getXmlObjectSerializerService() {
163 return this.xmlObjectSerializerService;
164 }
165
166 public void setXmlObjectSerializerService(XmlObjectSerializerService xmlObjectSerializerService) {
167 this.xmlObjectSerializerService = xmlObjectSerializerService;
168 }
169
170 protected SerializationState createNewDocumentSerializationState(Document document) {
171 return new SerializationState();
172 }
173
174 public class AutoPopulatingListConverter extends CollectionConverter {
175
176 public AutoPopulatingListConverter(Mapper mapper){
177 super(mapper);
178 }
179
180 @Override
181 public boolean canConvert(Class clazz) {
182 return clazz.equals(AutoPopulatingList.class);
183 }
184
185 }
186
187
188 }
189