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    }