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