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