001/** 002 * Copyright 2005-2016 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 */ 016package org.kuali.rice.krad.service.impl; 017 018import com.thoughtworks.xstream.XStream; 019import com.thoughtworks.xstream.converters.MarshallingContext; 020import com.thoughtworks.xstream.converters.UnmarshallingContext; 021import com.thoughtworks.xstream.converters.collections.CollectionConverter; 022import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; 023import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; 024import com.thoughtworks.xstream.converters.reflection.ReflectionConverter; 025import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; 026import com.thoughtworks.xstream.io.HierarchicalStreamReader; 027import com.thoughtworks.xstream.io.HierarchicalStreamWriter; 028import com.thoughtworks.xstream.mapper.Mapper; 029import org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl; 030import org.apache.ojb.broker.core.proxy.ProxyHelper; 031import org.kuali.rice.krad.document.Document; 032import org.kuali.rice.krad.service.DocumentSerializerService; 033import org.kuali.rice.krad.service.PersistenceService; 034import org.kuali.rice.krad.service.SerializerService; 035import org.kuali.rice.krad.service.XmlObjectSerializerService; 036import org.kuali.rice.krad.service.util.DateTimeConverter; 037import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator; 038import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator; 039import org.kuali.rice.krad.util.documentserializer.PropertyType; 040import org.kuali.rice.krad.util.documentserializer.SerializationState; 041import org.springframework.util.AutoPopulatingList; 042 043import java.lang.reflect.Field; 044import java.util.ArrayList; 045import java.util.Iterator; 046import 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 * @author Kuali Rice Team (rice.collab@kuali.org) 056 */ 057public abstract class SerializerServiceBase implements SerializerService { 058// private static final Log LOG = LogFactory.getLog(SerializerServiceBase.class); 059 060 protected PersistenceService persistenceService; 061 protected XmlObjectSerializerService xmlObjectSerializerService; 062 063 protected XStream xstream; 064 protected ThreadLocal<SerializationState> serializationStates; 065 protected ThreadLocal<PropertySerializabilityEvaluator> evaluators; 066 067 public SerializerServiceBase() { 068 serializationStates = new ThreadLocal<SerializationState>(); 069 evaluators = new ThreadLocal<PropertySerializabilityEvaluator>(); 070 071 xstream = new XStream(new ProxyAndStateAwareJavaReflectionProvider()); 072 xstream.registerConverter(new ProxyConverter(xstream.getMapper(), xstream.getReflectionProvider() )); 073 xstream.addDefaultImplementation(ArrayList.class, ListProxyDefaultImpl.class); 074 xstream.addDefaultImplementation(AutoPopulatingList.class, ListProxyDefaultImpl.class); 075 xstream.registerConverter(new AutoPopulatingListConverter(xstream.getMapper())); 076 xstream.registerConverter(new DateTimeConverter()); 077 } 078 079 public class ProxyConverter extends ReflectionConverter { 080 public ProxyConverter(Mapper mapper, ReflectionProvider reflectionProvider) { 081 super(mapper, reflectionProvider); 082 } 083 public boolean canConvert(Class clazz) { 084 return clazz.getName().contains("CGLIB") || clazz.getName().equals("org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl"); 085 } 086 087 public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) { 088 if (obj instanceof ListProxyDefaultImpl) { 089 List copiedList = new ArrayList(); 090 List proxiedList = (List) obj; 091 for (Iterator iter = proxiedList.iterator(); iter.hasNext();) { 092 copiedList.add(iter.next()); 093 } 094 context.convertAnother( copiedList ); 095 } 096 else { 097 super.marshal(getPersistenceService().resolveProxy(obj), writer, context); 098 } 099 } 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