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