/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.compatibility; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.StringTokenizer; import java.util.Enumeration; import java.util.jar.JarFile; import java.util.zip.ZipEntry; /** * This is the class in which most of the action in compatibility is performed. * The methods in this class are meant to be called from the CompatibilityTool, * so see that class for more information about what this class does. * * @see org.jboss.compatibility.CompatibilityTool * * @version $Revision: 1.2.2.1 $ * @author Thomas Roka-Aardal. * */ public class CompatibilityToolParser { boolean foundExternalizable = false; boolean foundSerializable = false; private StringBuffer output = new StringBuffer(); int result = 0; StringTokenizer arguments = null; String command; // Set the local properties from the arguments on the command line public CompatibilityToolParser(String command, StringTokenizer arguments) { this.command = command; this.arguments = arguments; } public void parseArguments() { String argument = null; while(arguments.hasMoreTokens()) { argument = arguments.nextToken().trim(); if(argument.endsWith(".jar")) { try { JarFile jar = new JarFile(argument); Enumeration enum = jar.entries(); while(enum.hasMoreElements()) { String zipEntryName = ((ZipEntry) enum.nextElement()).getName(); if(zipEntryName.endsWith(".class")) { // Replace "/" with "." zipEntryName = zipEntryName.replace('/', '.'); // Strip away .class from name (not necessary) zipEntryName = zipEntryName.substring(0, zipEntryName.length() - ".class".length()); parseFile(zipEntryName); } } } catch(IOException ioEx) { output.append("Could not read jar file: " + argument + ", continuing"); result = -1; } } else { parseFile(argument); } } System.out.print(output.toString()); } public void parseFile(String _argument) { Class argClass = null; String serializedClassName = null; foundExternalizable = false; foundSerializable = false; serializedClassName = new String(_argument + ".ser"); // In any case we need to load the argument classes, do this // using reflection. try { // Load the class using this class' classloader, and initialize it using the // public no-args constructor. output.append("[" + _argument + "] "); argClass = Class.forName( _argument, true, CompatibilityToolParser.class.getClassLoader()); // Verify that the class implements the Externalizable interface. This // is a requirement so that we can persist (write to disk) an instance // of the class, and be able to read it back from a serialized state into // an instance. findInterfaces(argClass.getInterfaces()); if (!(foundExternalizable || foundSerializable)) { // A contract class didn't implement java.io.Externalizable or java.io.Serializable, // which is a requirement, so exit. output.append( "-> Contract class does not implement Externalizable or Serializable!\n"); result = -1; } else { // The contract class correctly implemented java.io.Externalizable or // java.io.Serializable. output.append( foundExternalizable ? "-> Externalizable, " : "-> Serializable, "); // See what it is we were called to do (make or check) if (command.equalsIgnoreCase("make")) { serializeAndWriteFile(serializedClassName, argClass); } // check existing serialized file else { checkSerializedFile(serializedClassName); } } } catch (ClassNotFoundException e) { // Class isn't in classpath, give a meaningful response. output.append("-> Contract class not in classpath!\n"); result = -1; } catch (Throwable e) { output.append( "-> Could not load class, dependent classes may not be in classpath.\n"); result = -1; } } /** * Parse the list of interfaces and check if either * java.io.Externalizable or java.io.Serializable are implemented * * @param argClassInterfaces list of interfaces */ private void findInterfaces(Class[] argClassInterfaces) { for (int cnt2 = 0; cnt2 < argClassInterfaces.length; cnt2++) { if (argClassInterfaces[cnt2] .getName() .equals("java.io.Externalizable")) { foundExternalizable = true; break; } else if ( argClassInterfaces[cnt2].getName().equals("java.io.Serializable")) { foundSerializable = true; break; } } } /** * Write a class to disk * @param filename * @param argClass */ private void serializeAndWriteFile(String filename, Class argClass) { FileOutputStream outStream; ObjectOutputStream objectOutStream; File partialFile; // Create serialized files (and overwrite existing ones) try { outStream = new FileOutputStream(filename); try { objectOutStream = new ObjectOutputStream(outStream); try { // We use the defined public no-args constructor that is // required by the serialization API to create the instance // that is written to disk. When implementing the Externalizable interface, // only the identity (the serialVersionUID) is written to disk. objectOutStream.writeObject(argClass.newInstance()); output.append(" serialized.\n"); } catch (InstantiationException in) { output.append( " could not instantiate class using a public, no-args constructor\n"); // Remove the partially written file partialFile = new File(filename); try { objectOutStream.close(); outStream.close(); partialFile.delete(); } catch (Exception ignore) { } result = -1; } catch (IllegalAccessException ia) { output.append(" could not access class.\n"); result = -1; } catch (Throwable e) { output.append( " could not create instance - dependent classes may not be in classpath.\n"); // Remove the partially written file partialFile = new File(filename); try { objectOutStream.close(); outStream.close(); partialFile.delete(); } catch (Exception ignore) { } result = -1; } } catch (IOException io) { output.append(" could not write to file!\n"); result = -1; } } catch (FileNotFoundException f) { output.append(" file exists, but cannot be overwritten!\n"); result = -1; } } /** * Try to create an object of the serialized file. * If success, no errors are found.. * @param filename name of serialized class */ private void checkSerializedFile(String filename) { FileInputStream inStream; ObjectInputStream objectInStream; Object serializedObject; // Verify compiled classes' version against serialized files try { inStream = new FileInputStream(filename); try { objectInStream = new ObjectInputStream(inStream); // Read back the object from a serialized state (note that // this also implicitly calls the public, no-args constructor // that must be available. serializedObject = objectInStream.readObject(); // No exception, so things must have gone okay. Continue. output.append(" COMPATIBLE\n"); } catch (IOException io) { output.append( " serial id NOT COMPATIBLE! You are breaking a contract class!\n"); } } catch (FileNotFoundException f1) { output.append( " Class added, does not break existing compatibility.\n"); } catch (ClassNotFoundException e) { // It shouldnt be possible to come here.. - i think.. output.append("-> Error when creating object from serialized file.\n"); result = -1; } } public int getResult() { return result; } }