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