/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm;
import java.io.File;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.jbpm.util.ClassLoaderUtil;
public class SerializabilityTest extends AbstractJbpmTestCase {
static final String[] excusedClasses = {
"org.jbpm.JbpmConfiguration$",
"org.jbpm.ant.",
"org.jbpm.context.exe.JbpmType",
"org.jbpm.db.AbstractDbTestCase",
"org.jbpm.db.ContextSession",
"org.jbpm.db.FileSession",
"org.jbpm.db.GraphSession",
"org.jbpm.db.JbpmSession",
"org.jbpm.db.JbpmSchema",
"org.jbpm.db.JobSession",
"org.jbpm.db.LoggingSession",
"org.jbpm.db.SchedulerSession",
"org.jbpm.db.TaskMgmtSession",
"org.jbpm.db.compatibility.JbpmSchemaUpdate",
"org.jbpm.db.hibernate.JbpmNamingStrategy",
"org.jbpm.db.hibernate.MySQLDialect",
"org.jbpm.db.hibernate.SybaseDialect",
"org.jbpm.db.hibernate.SybaseRowLockDialect",
"org.jbpm.graph.action.ActionTypes",
"org.jbpm.graph.exe.ExecutionContext",
"org.jbpm.graph.node.InterleaveStart$DefaultInterleaver",
"org.jbpm.graph.node.NodeTypes",
"org.jbpm.graph.node.ProcessFactory",
"org.jbpm.instantiation.BeanInstantiator",
"org.jbpm.instantiation.ConfigurationPropertyInstantiator",
"org.jbpm.instantiation.ConstructorInstantiator",
"org.jbpm.instantiation.DefaultInstantiator",
"org.jbpm.instantiation.Delegation$CompactXmlWriter",
"org.jbpm.instantiation.FieldInstantiator",
"org.jbpm.instantiation.ProcessClassLoader",
"org.jbpm.instantiation.XmlInstantiator",
"org.jbpm.job.executor.DispatcherThread",
"org.jbpm.job.executor.JobExecutorThread",
"org.jbpm.job.executor.JobExecutor$JobRejectionHandler",
"org.jbpm.job.executor.JobParcel",
"org.jbpm.job.executor.LockMonitorThread",
"org.jbpm.jpdl.convert.Converter",
"org.jbpm.jpdl.el.",
"org.jbpm.jpdl.par.FileArchiveParser",
"org.jbpm.jpdl.par.JpdlArchiveParser",
"org.jbpm.jpdl.xml.JpdlXmlReader",
"org.jbpm.jpdl.xml.JpdlXmlWriter",
"org.jbpm.persistence.db.StaleObjectLogConfigurer$LogWrapper",
"org.jbpm.util.Clock$DefaultDateGenerator",
"org.jbpm.util.CustomLoaderObjectInputStream",
"org.jbpm.util.NodeIterator",
"org.jbpm.util.XmlUtil$ElementPredicate",
"org.jbpm.web.JobExecutorLauncher",
"org.jbpm.web.JbpmConfigurationCloser"
};
public void testForNonSerializableClasses() throws URISyntaxException {
URL location = ClassLoaderUtil.class.getProtectionDomain().getCodeSource().getLocation();
File classDir = new File(new URI(location.toString()));
File[] files = classDir.listFiles();
if (files == null) return;
// scan top level packages
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isDirectory()) {
scanForClasses(file, file.getName());
}
}
}
private static void scanForClasses(File classDir, String packageName) {
File[] files = classDir.listFiles();
if (files == null) return;
for (int i = 0; i < files.length; i++) {
File file = files[i];
String fileName = file.getName();
if (file.isDirectory()) {
scanForClasses(file, packageName + '.' + fileName);
}
else if (fileName.endsWith(".class")) {
String className = packageName + '.' + fileName.substring(0, fileName.length() - 6);
assertClassIsSerializable(className);
}
}
}
private static void assertClassIsSerializable(String className) {
try {
Class clazz = Class.forName(className);
if (!Serializable.class.isAssignableFrom(clazz)
&& !Modifier.isAbstract(clazz.getModifiers())
&& !isAnonymous(clazz)
&& !isUtility(clazz)
&& !isExcused(className)) {
fail(className + " is NOT Serializable");
}
}
catch (ClassNotFoundException e) {
fail("no such class: " + className);
}
}
private static boolean isAnonymous(Class clazz) {
return clazz.getName().matches(".*\\$\\d+");
}
/**
* Tells whether the given class consists exclusively of static methods and has no public
* constructors.
*/
private static boolean isUtility(Class clazz) {
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (!Modifier.isStatic(method.getModifiers())
&& method.getDeclaringClass() != Object.class) return false;
}
return clazz.getConstructors().length == 0;
}
private static boolean isExcused(String className) {
for (int i = 0; i < excusedClasses.length; i++) {
if (className.startsWith(excusedClasses[i])) return true;
}
return false;
}
}