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