001 /*
002 * (c)reated 21.07.2010 by oliver
003 */
004 package patterntesting.runtime.junit;
005
006 import java.io.*;
007 import java.util.*;
008
009 import org.slf4j.*;
010
011 import patterntesting.runtime.monitor.ClasspathMonitor;
012 import patterntesting.runtime.util.Converter;
013
014 /**
015 * This is a utility class to check the serializable nature of classes.
016 * <br/>
017 * NOTE: In the future this class will be perhaps part of the ObjectTester
018 * class.
019 * <br/>
020 * Before v1.1 the methods are named "checkSerialization". Since 1.1 these
021 * methods will have now an "assert" prefix ("assertSerialization").
022 *
023 * @author oliver (oliver.boehm@agentes.de)
024 * @since 1.0.3 (21.07.2010)
025 */
026 public final class SerializableTester {
027
028 private static final Logger log = LoggerFactory.getLogger(SerializableTester.class);
029 private static final ClasspathMonitor classpathMonitor = ClasspathMonitor.getInstance();
030
031 /** Utitlity class - no need to instantiate it. */
032 private SerializableTester() {}
033
034 /**
035 * Check serialization and deserialization of an object.
036 * Because not all classes has a (correct) overwritten equals method
037 * we do not compare the deserialized object with the equals() method.
038 * But if it implements Comparable we verify the equality of the two
039 * objects with the compareTo() method.
040 *
041 * @param object the object
042 * @throws NotSerializableException if object can't serialized
043 * @since 1.1
044 */
045 public static void assertSerialization(final Serializable object)
046 throws NotSerializableException {
047 byte[] bytes = Converter.serialize(object);
048 log.debug(object + " serialized in " + bytes.length + " bytes");
049 try {
050 Object deserialized = Converter.deserialize(bytes);
051 if (object instanceof Comparable<?>) {
052 ObjectTester.assertCompareTo(object, deserialized);
053 }
054 } catch (ClassNotFoundException canthappen) {
055 throw new RuntimeException(canthappen);
056 }
057 }
058
059 /**
060 * This method will create an object of the given class using the
061 * default constructor. So three preconditions must be true:
062 * <ol>
063 * <li>the class must not be abstract</li>
064 * <li>there must be a (public) default constructor</li>
065 * <li>it must be Serializable</li>
066 * </ol>
067 *
068 * @param clazz the clazz
069 * @throws NotSerializableException if the check fails
070 * @since 1.1
071 */
072 public static void assertSerialization(final Class<? extends Serializable> clazz) throws NotSerializableException {
073 if (log.isTraceEnabled()) {
074 log.trace("checking " + clazz.getName() + " if it can be serialized...");
075 }
076 try {
077 Serializable obj = clazz.newInstance();
078 assertSerialization(obj);
079 } catch (InstantiationException e) {
080 throw new IllegalArgumentException("can't instantiate " + clazz, e);
081 } catch (IllegalAccessException e) {
082 throw new IllegalArgumentException("can't access ctor of " + clazz, e);
083 }
084 }
085
086 /**
087 * Check for each class in the given collection if it can be serialized and
088 * deserialized.
089 *
090 * @param classes a collection of classes to be checked
091 * @throws NotSerializableException if one of the classes can't be serialized
092 * @since 1.1
093 */
094 public static void assertSerialization(final Collection<Class<Serializable>> classes) throws NotSerializableException {
095 for (Class<?> clazz : classes) {
096 assertSerialization(clazz);
097 }
098 }
099
100 /**
101 * Check for each class in the given package if it can be serialized and
102 * deserialized.
103 * <br/>
104 * To get a name of a package call {@link Package#getPackage(String)}.
105 * But be sure that you can't get <em>null</em> as result. In this case
106 * use {@link #assertSerializationOfPackage(String)}.
107 *
108 * @param pkg the package e.g. "patterntesting.runtime"
109 * @throws NotSerializableException if one of the class can't be serialized
110 * @see #assertSerializationOfPackage(String)
111 * @since 1.1
112 */
113 public static void assertSerialization(final Package pkg) throws NotSerializableException {
114 assert pkg!= null;
115 assertSerializationOfPackage(pkg.getName());
116 }
117
118 /**
119 * Check for each class in the given package if it can be serialized and
120 * deserialized.
121 *
122 * @param packageName the package name e.g. "patterntesting.runtime"
123 * @throws NotSerializableException if one of the class can't be serialized
124 * @since 1.1
125 */
126 public static void assertSerializationOfPackage(final String packageName) throws NotSerializableException {
127 assert packageName != null;
128 Collection<Class<Serializable>> classes = getSerializableClasses(packageName);
129 assertSerialization(classes);
130 }
131
132 @SuppressWarnings("unchecked")
133 private static Collection<Class<Serializable>> getSerializableClasses(final String packageName) {
134 String[] classnames = classpathMonitor.getClasspathClasses();
135 Collection<Class<Serializable>> classes = new ArrayList<Class<Serializable>>();
136 for (int i = 0; i < classnames.length; i++) {
137 if (classnames[i].startsWith(packageName)) {
138 try {
139 Class<?> clazz = Class.forName(classnames[i]);
140 if (Serializable.class.isAssignableFrom(clazz)) {
141 classes.add((Class<Serializable>) clazz);
142 }
143 } catch (ClassNotFoundException ignored) {
144 log.info(classnames[i] + " will be ignored", ignored);
145 }
146 }
147 }
148 return classes;
149 }
150
151 }