View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    * 
4    * Copyright 2005-2014 The Kuali Foundation
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.sys.context;
20  
21  import java.lang.reflect.Field;
22  import java.lang.reflect.Modifier;
23  import java.util.HashSet;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.kfs.sys.ConfigureContext;
29  import org.kuali.rice.krad.datadictionary.DataDictionary;
30  import org.kuali.rice.krad.datadictionary.DocumentEntry;
31  import org.kuali.rice.krad.service.DataDictionaryService;
32  
33  @ConfigureContext
34  /**
35   * Tests that every property on all KFS documents are either serializable or transient
36   */
37  public class DocumentSerializabilityTest extends KualiTestBase {
38      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentSerializabilityTest.class);
39      /**
40       * Tests that every property on all KFS documents are either serializable or transient
41       */
42      public void testAllDocumentsAreSerializable() {
43          DataDictionary dataDictionary = SpringContext.getBean(DataDictionaryService.class).getDataDictionary();
44          Map<String, DocumentEntry> documentEntries = dataDictionary.getDocumentEntries();
45          Set<Class<?>> checkedClasses = new HashSet<Class<?>>();
46          Set<Class<?>> unserializableClasses = new HashSet<Class<?>>();
47          String errorMessage = "";
48  
49          for (String documentEntryName : documentEntries.keySet()) {
50              if (LOG.isDebugEnabled()) {
51                  LOG.debug("Checking: "+documentEntryName);
52              }
53              DocumentEntry entry = documentEntries.get(documentEntryName);
54  
55              Set<String> unserializableFields = null;
56              Class<?> testedClass;
57              if (entry instanceof org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry) {
58                  testedClass = ((org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry)entry).getMaintainableClass();
59                  if ( testedClass == null ) {
60                      errorMessage += "Maintenance Document entry: " + documentEntryName + " has a null maintainable class";
61                  } else {
62                      unserializableFields = getUnserializableFieldsFromClass(testedClass);
63                  }
64              } else if (entry instanceof org.kuali.rice.kns.datadictionary.TransactionalDocumentEntry) {
65                  testedClass = ((org.kuali.rice.kns.datadictionary.TransactionalDocumentEntry)entry).getDocumentClass();
66                  if ( testedClass == null ) {
67                      errorMessage += "Transactional Document entry: " + documentEntryName + " has a null document class";
68                  } else {
69                      unserializableFields = getUnserializableFieldsFromClass(testedClass);
70                  }
71              } else {
72                  unserializableFields = null;
73                  testedClass = null;
74              }
75  
76              if (testedClass != null && !checkedClasses.contains(testedClass)) {
77                  checkedClasses.add(testedClass);
78                  if (unserializableFields != null && !unserializableFields.isEmpty()) {
79                      errorMessage += "Class "+testedClass.getName()+" has unserializable fields: "+StringUtils.join(unserializableFields, ',')+"\n";
80                      unserializableClasses.add(testedClass);
81                  }
82              }
83          }
84  
85          if (!StringUtils.isBlank(errorMessage)) {
86              LOG.info(errorMessage);
87          }
88          assertTrue(errorMessage, StringUtils.isEmpty(errorMessage) );
89      }
90  
91      /**
92       * Determines if any of the fields of the given class are unserializable
93       * @param clazz the class to check
94       * @return a Set of field names, the unserializable field names
95       */
96      protected Set<String> getUnserializableFieldsFromClass(Class<?> clazz) {
97          Set<String> unserializableFields = new HashSet<String>();
98  
99          if (clazz.getSuperclass() != null && !clazz.equals(java.lang.Object.class)) {
100             unserializableFields.addAll(getUnserializableFieldsFromClass(clazz.getSuperclass()));
101         }
102         Field[] fields = clazz.getDeclaredFields();
103         for (Field field : fields) {
104             if (!isSerializable(field)) {
105                 unserializableFields.add(field.getName());
106             }
107         }
108 
109         return unserializableFields;
110     }
111 
112     /**
113      * Determines if a given field is serializable
114      * @param field the field to check
115      * @return true if the field is serializable, false otherwise
116      */
117     protected boolean isSerializable(Field field) {
118         return java.io.Serializable.class.isAssignableFrom(field.getType())
119                 || java.util.Collection.class.isAssignableFrom(field.getType())
120                 || java.util.Map.class.isAssignableFrom(field.getType())
121                 || field.getName().equals("dataObject")
122                 || Modifier.isTransient(field.getModifiers())
123                 || Modifier.isStatic(field.getModifiers())
124                 || isPrimitive(field.getType());
125     }
126 
127     /**
128      * Determines if the given class represents one of the eight Java primitives
129      * @param clazz the class to check
130      * @return true if the class represents a byte, short, int, long, char, double, float, or boolean; false otherwise
131      */
132     protected boolean isPrimitive(Class<?> clazz) {
133         return Byte.TYPE.isAssignableFrom(clazz) || Short.TYPE.isAssignableFrom(clazz) || Integer.TYPE.isAssignableFrom(clazz) || Long.TYPE.isAssignableFrom(clazz) || Float.TYPE.isAssignableFrom(clazz) || Double.TYPE.isAssignableFrom(clazz) || Character.TYPE.isAssignableFrom(clazz) || Boolean.TYPE.isAssignableFrom(clazz);
134     }
135 }