/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.sys.context; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.kuali.kfs.sys.ConfigureContext; import org.kuali.rice.krad.datadictionary.DataDictionary; import org.kuali.rice.krad.datadictionary.DocumentEntry; import org.kuali.rice.krad.service.DataDictionaryService; @ConfigureContext /** * Tests that every property on all KFS documents are either serializable or transient */ public class DocumentSerializabilityTest extends KualiTestBase { private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentSerializabilityTest.class); /** * Tests that every property on all KFS documents are either serializable or transient */ public void testAllDocumentsAreSerializable() { DataDictionary dataDictionary = SpringContext.getBean(DataDictionaryService.class).getDataDictionary(); Map<String, DocumentEntry> documentEntries = dataDictionary.getDocumentEntries(); Set<Class<?>> checkedClasses = new HashSet<Class<?>>(); Set<Class<?>> unserializableClasses = new HashSet<Class<?>>(); String errorMessage = ""; for (String documentEntryName : documentEntries.keySet()) { if (LOG.isDebugEnabled()) { LOG.debug("Checking: "+documentEntryName); } DocumentEntry entry = documentEntries.get(documentEntryName); Set<String> unserializableFields = null; Class<?> testedClass; if (entry instanceof org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry) { testedClass = ((org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry)entry).getMaintainableClass(); if ( testedClass == null ) { errorMessage += "Maintenance Document entry: " + documentEntryName + " has a null maintainable class"; } else { unserializableFields = getUnserializableFieldsFromClass(testedClass); } } else if (entry instanceof org.kuali.rice.kns.datadictionary.TransactionalDocumentEntry) { testedClass = ((org.kuali.rice.kns.datadictionary.TransactionalDocumentEntry)entry).getDocumentClass(); if ( testedClass == null ) { errorMessage += "Transactional Document entry: " + documentEntryName + " has a null document class"; } else { unserializableFields = getUnserializableFieldsFromClass(testedClass); } } else { unserializableFields = null; testedClass = null; } if (testedClass != null && !checkedClasses.contains(testedClass)) { checkedClasses.add(testedClass); if (unserializableFields != null && !unserializableFields.isEmpty()) { errorMessage += "Class "+testedClass.getName()+" has unserializable fields: "+StringUtils.join(unserializableFields, ',')+"\n"; unserializableClasses.add(testedClass); } } } if (!StringUtils.isBlank(errorMessage)) { LOG.info(errorMessage); } assertTrue(errorMessage, StringUtils.isEmpty(errorMessage) ); } /** * Determines if any of the fields of the given class are unserializable * @param clazz the class to check * @return a Set of field names, the unserializable field names */ protected Set<String> getUnserializableFieldsFromClass(Class<?> clazz) { Set<String> unserializableFields = new HashSet<String>(); if (clazz.getSuperclass() != null && !clazz.equals(java.lang.Object.class)) { unserializableFields.addAll(getUnserializableFieldsFromClass(clazz.getSuperclass())); } Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (!isSerializable(field)) { unserializableFields.add(field.getName()); } } return unserializableFields; } /** * Determines if a given field is serializable * @param field the field to check * @return true if the field is serializable, false otherwise */ protected boolean isSerializable(Field field) { return java.io.Serializable.class.isAssignableFrom(field.getType()) || java.util.Collection.class.isAssignableFrom(field.getType()) || java.util.Map.class.isAssignableFrom(field.getType()) || field.getName().equals("dataObject") || Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || isPrimitive(field.getType()); } /** * Determines if the given class represents one of the eight Java primitives * @param clazz the class to check * @return true if the class represents a byte, short, int, long, char, double, float, or boolean; false otherwise */ protected boolean isPrimitive(Class<?> clazz) { 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); } }