/* * [JarCheck.java] * * Summary: Ensures javac -target versions of the class files in a jar are as expected. * * Copyright: (c) 2006-2011 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.5+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2006-01-16 initial version * 1.1 2006-01-16 * 1.2 2006-03-05 reformat with IntelliJ, add Javadoc * 1.3 2008-04-21 display version number of each class file checked. */ package org.docx4j.utils; import static java.lang.System.err; import static java.lang.System.out; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * Ensures javac -target versions of the class files in a jar are as expected. * * @author Roedy Green, Canadian Mind Products * @version 1.3 2008-04-21 display version number of each class file checked. * @since 2006-01-16 * * Modified by Jason Harrop 2011 06 25 * Note re Java 1.5, 1.6 behaviour. * In 1.5, you can't put @Override on something which merely * implements an interface * Eclipse will give an error if you do (and have source set to 1.5). * Maven and ant won't give an error, and if you have target=1.5 * (as we do for docx4j), will correctly produce 1.5 code. * This class allows us to check that all our dependencies are * also 1.5. * An alternative way of doing it would be to use clirr.sourceforge.net * which can run as an ant task, or from a command line, but I haven't * tried that. * */ public final class JarCheck { // ------------------------------ CONSTANTS ------------------------------ /** * how many bytes at beginning of class file we read<br /> 4=ca-fe-ba-be + 2=minor + 2=major */ private static final int chunkLength = 8; /** * undisplayed copyright notice */ public static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2006-2011 Roedy Green, Canadian Mind Products, http://mindprod.com"; private static final String RELEASE_DATE = "2008-04-21"; /** * embedded version string. */ public static final String VERSION_STRING = "1.3"; /** * translate class file major version number to human JVM version */ private static final HashMap<Integer, String> convertMachineToHuman = new HashMap<Integer, String>( 23 ); /** * translate from human JDK version to class file major version number */ private static final HashMap<String, Integer> convertHumanToMachine = new HashMap<String, Integer>( 23 ); /** * expected first 4 bytes of a class file */ private static final byte[] expectedMagicNumber = { ( byte ) 0xca, ( byte ) 0xfe, ( byte ) 0xba, ( byte ) 0xbe }; // -------------------------- STATIC METHODS -------------------------- static { convertHumanToMachine.put( "1.0", 44 ); convertHumanToMachine.put( "1.1", 45 ); convertHumanToMachine.put( "1.2", 46 ); convertHumanToMachine.put( "1.3", 47 ); convertHumanToMachine.put( "1.4", 48 ); convertHumanToMachine.put( "1.5", 49 ); convertHumanToMachine.put( "1.6", 50 ); convertHumanToMachine.put( "1.7", 51 ); convertHumanToMachine.put( "1.8", 52 ); } static { convertMachineToHuman.put( 44, "1.0" ); convertMachineToHuman.put( 45, "1.1" ); convertMachineToHuman.put( 46, "1.2" ); convertMachineToHuman.put( 47, "1.3" ); convertMachineToHuman.put( 48, "1.4" ); convertMachineToHuman.put( 49, "1.5" ); convertMachineToHuman.put( 50, "1.6" ); convertMachineToHuman.put( 51, "1.7" ); convertMachineToHuman.put( 52, "1.8" ); } /** * check one jar to make sure all class files have compatible versions. * * @param jarFilename name of jar file whose classes are to be tested. * @param low low bound for major version e.g. 44 * @param high high bound for major version. e.g. 50 * * @return true if all is ok. False if not, with long on System.err of problems. */ private static boolean checkJar( String jarFilename, int low, int high ) { out.println( "Checking jar " + jarFilename ); boolean success = true; FileInputStream fis; ZipInputStream zip = null; int lowest = 1000; int highest = 0; try { try { fis = new FileInputStream( jarFilename ); zip = new ZipInputStream( fis ); // loop for each jar entry entryLoop: while ( true ) { ZipEntry entry = zip.getNextEntry(); if ( entry == null ) { break; } // relative name with slashes to separate dirnames. String elementName = entry.getName(); //System.out.println(elementName); if ( !elementName.endsWith( ".class" ) ) { // ignore anything but a .final class file continue; } byte[] chunk = new byte[ chunkLength ]; int bytesRead = zip.read( chunk, 0, chunkLength ); zip.closeEntry(); if ( bytesRead != chunkLength ) { err.println( ">> Corrupt class file: " + elementName ); success = false; continue; } // make sure magic number signature is as expected. for ( int i = 0; i < expectedMagicNumber.length; i++ ) { if ( chunk[ i ] != expectedMagicNumber[ i ] ) { err.println( ">> Bad magic number in " + elementName ); success = false; continue entryLoop; } } /* * pick out big-endian ushort major version in last two * bytes of chunk */ int major = ( ( chunk[ chunkLength - 2 ] & 0xff ) << 8 ) + ( chunk[ chunkLength - 1 ] & 0xff ); /* F I N A L L Y. All this has been leading up to this TEST */ if (major>highest) highest=major; if (major<lowest) { lowest=major; } if ( low <= major && major <= high ) { // out.print( " OK " ); // out.println( convertMachineToHuman.get( major ) // + " (" // + major // + ") " // + elementName ); // leave success set as previously } else if ( major > high ) { err.println( ">> Wrong Version " ); err.println( convertMachineToHuman.get( major ) + " (" + major + ") " + elementName ); success = false; } else { // Suppress low version error // err.println( ">> Wrong Version " ); // err.println( convertMachineToHuman.get( major ) // + " (" // + major // + ") " // + elementName ); // success = false; } } // end while } catch ( EOFException e ) { // normal exit } zip.close(); if (lowest==highest) { System.out.println( convertMachineToHuman.get( highest )); } else { System.out.println( convertMachineToHuman.get( lowest ) + "-" + convertMachineToHuman.get( highest )); } return success; } catch ( IOException e ) { err.println( ">> Problem reading jar file." ); return false; } } // --------------------------- main() method --------------------------- /** * Main command line jarfileName lowVersion highVersion e.g. myjar.jar 1.0 1.6 * * @param args rot used */ public static void main( String[] args ) { // if ( args.length != 3 ) // { // err.println( "usage: java -ea -jar jarcheck.jar jarFileName.jar 1.1 1.6" ); // System.exit( 2 ); // } // String jarFilename = args[ 0 ]; // int low = convertHumanToMachine.get( args[ 1 ] ); // int high = convertHumanToMachine.get( args[ 2 ] ); int low = convertHumanToMachine.get( "1.3" ); int high = convertHumanToMachine.get( "1.6" ); String dirPath = System.getProperty("user.dir") + "/dist/"; File dir = new File(dirPath); if (dir.isDirectory() ) { File[] files = dir.listFiles(); for (int i=0 ; i<files.length; i++) { if (files[i].isFile() && files[i].getName().endsWith("jar")) { System.out.println(files[i].getName() ); boolean success = checkJar( files[i].getAbsolutePath(), low, high ); System.out.println(success); } } } } }