| import java.util.*; |
| import java.util.jar.*; |
| import java.util.zip.*; |
| import java.io.*; |
| import org.apache.bcel.classfile.*; |
| import org.apache.bcel.*; |
| import org.apache.bcel.util.ClassPath; |
| |
| /** |
| * Package the client. Creates a jar file in the current directory |
| * that contains a minimal set of classes needed to run the client. |
| * |
| * Use BCEL to extract class names and read/write classes |
| * @author First Hop Ltd / Torsten Rueger |
| */ |
| public class Package { |
| |
| /** |
| * The name of the resulting jar is Client.jar |
| */ |
| static String defaultJar = "Client.jar"; |
| |
| /* |
| * See usage() for arguments. Create an instance and run that |
| *(just so not all members have to be static) |
| */ |
| static void main(String args[]) { |
| Package instance = new Package(); |
| try{ |
| instance.go(args); |
| }catch(Exception e){ |
| e.printStackTrace(); |
| instance.usage(); |
| } |
| } |
| |
| /** |
| * We use a "default ClassPath object which uses the environments |
| * CLASSPATH |
| */ |
| ClassPath classPath = new ClassPath(); |
| |
| /** |
| * A map for all Classes, the ones we're going to package. |
| * Store class name against the JavaClass. From the JavaClass |
| * we get the bytes to create the jar. |
| */ |
| TreeMap allClasses = new TreeMap(); |
| /** |
| * We start at the root classes, put them in here, then go through |
| * this list, putting dependent classes in here and from there |
| * into allClasses. Store class names against class names of their dependents |
| */ |
| TreeMap dependents = new TreeMap(); |
| /** |
| * Collect all classes that could not be found in the classpath. |
| * Store class names against class names of their dependents |
| */ |
| TreeMap notFound = new TreeMap(); |
| /** |
| * See wheather we print the classes that were not found (default = false) |
| */ |
| boolean showNotFound = false ; |
| /** |
| * Remember wheather to print allClasses at the end (default = false) |
| */ |
| boolean printClasses = false ; |
| /** |
| * Wheather we log classes during processing (default = false) |
| */ |
| boolean log = false ; |
| |
| public void usage(){ |
| System.out.println(" This program packages classes and all their dependents"); |
| System.out.println(" into one jar. Give all starting classes (your main)"); |
| System.out.println(" on the command line. Use / as separator, the .class is"); |
| System.out.println(" optional. We use the environments CLASSPATH to resolve"); |
| System.out.println(" classes. Anything but java.* packages are packaged."); |
| System.out.println(" If you use Class.forName (or similar), be sure to"); |
| System.out.println(" include the classes that you load dynamically on the"); |
| System.out.println(" command line.\n"); |
| System.out.println(" These options are recognized:"); |
| System.out.println(" -e -error Show errors, meaning classes that could not "); |
| System.out.println(" resolved + the classes that referenced them."); |
| System.out.println(" -l -log Show classes as they are processed. This will"); |
| System.out.println(" include doubles, java classes and is difficult to"); |
| System.out.println(" read. I use it as a sort of progress monitor"); |
| System.out.println(" -s -show Prints all the classes that were packaged"); |
| System.out.println(" in alphabetical order, which is ordered by package"); |
| System.out.println(" for the most part."); |
| } |
| /** |
| * the main of this class |
| */ |
| void go(String[] args) throws IOException { |
| JavaClass clazz ; |
| // sort the options |
| for(int i = 0 ; i < args.length ; i++ ){ |
| if( args[i].startsWith("-e") ) { |
| showNotFound = true; |
| continue; |
| } |
| if( args[i].startsWith("-s") ) { |
| printClasses = true ; |
| continue; |
| } |
| if( args[i].startsWith("-l") ) { |
| log = true ; |
| continue; |
| } |
| String clName = args[i]; |
| if(clName.endsWith(".class")) { |
| clName = clName.substring(0,clName.length()-6); |
| } |
| clName = clName.replace('.','/'); |
| clazz = new ClassParser(classPath.getInputStream(clName),clName).parse(); |
| // here we create the root set of classes to process |
| addDependents(clazz); |
| System.out.println("Packaging for class: " + clName ); |
| } |
| if( dependents.isEmpty() ){ |
| usage(); |
| return ; |
| } |
| System.out.println("Creating jar file: " + defaultJar ); |
| // starting processing: Grab from the dependents list an add back to it |
| // and the allClasses list. see addDependents |
| while(!dependents.isEmpty() ){ |
| String name = (String)dependents.firstKey(); |
| String from = (String) dependents.remove(name); |
| if(allClasses.get(name) == null){ |
| try{ |
| InputStream is = classPath.getInputStream(name); |
| clazz = new ClassParser(is, name).parse(); |
| addDependents(clazz); |
| }catch( IOException e){ |
| //System.err.println("Error, class not found " + name ); |
| notFound.put(name,from); |
| } |
| } |
| } |
| if(printClasses) { // if wanted show all classes |
| printAllClasses(); |
| } |
| // create the jar |
| JarOutputStream jarFile = new JarOutputStream(new FileOutputStream(defaultJar)); |
| jarFile.setLevel(5); // use compression |
| Iterator keys = allClasses.keySet().iterator(); |
| int written = 0 ; |
| while(keys.hasNext()){ // add entries for every class |
| String name = (String)keys.next(); |
| JavaClass claz = (JavaClass) allClasses.get(name); |
| ZipEntry zipEntry = new ZipEntry(name+".class"); |
| byte[] bytes = claz.getBytes() ; |
| int length = bytes.length ; |
| jarFile.putNextEntry(zipEntry); |
| jarFile.write( bytes , 0 , length ); |
| written += length; // for logging |
| } |
| jarFile.close(); |
| System.err.println("The jar file contains " + allClasses.size() |
| +" classes and contains " +written+ " bytes"); |
| if( !notFound.isEmpty() ){ |
| System.err.println( notFound.size() +" classes could not be found"); |
| if(showNotFound){ // if wanted show the actual classes that we not found |
| while(!notFound.isEmpty()){ |
| String name = (String)notFound.firstKey(); |
| System.err.println( name+ " (" + notFound.remove(name)+")"); |
| } |
| }else{ |
| System.err.println("Use '-e' option to view classes that were not found"); |
| } |
| } |
| } |
| |
| /** |
| * Print all classes that were packaged. Sort alphabetically for better |
| * overview. Enabled by -s option |
| */ |
| void printAllClasses(){ |
| ArrayList names = new ArrayList(allClasses.keySet()); |
| Collections.sort(names); |
| for( int i = 0 ; i < names.size() ; i ++ ){ |
| String cl = (String)names.get(i); |
| System.err.println(cl); |
| } |
| } |
| |
| /** |
| *Add this class to allClasses. Then go through all its dependents |
| * and add them to the dependents list if they are not in allClasses |
| */ |
| void addDependents( JavaClass clazz ) throws IOException { |
| String name = clazz.getClassName().replace('.', '/'); |
| allClasses.put( name , clazz ); |
| ConstantPool pool = clazz.getConstantPool(); |
| for( int i = 1 ; i < pool.getLength() ; i++){ |
| Constant cons = pool.getConstant(i); |
| //System.out.println("("+i+") " + cons ); |
| if( cons!=null && cons.getTag() == Constants.CONSTANT_Class ){ |
| int idx = ((ConstantClass)pool.getConstant(i)).getNameIndex(); |
| String clas = ((ConstantUtf8)pool.getConstant(idx)).getBytes(); |
| addClassString(clas,name); |
| } |
| } |
| } |
| |
| /** |
| * add given class to dependents (from is where its dependent from) |
| * some fiddeling to be done because of array class notation |
| */ |
| void addClassString(String clas,String from) throws IOException{ |
| if(log){ |
| System.out.println("processing: " + clas +" referenced by " + from); |
| } |
| // must check if it's an arrary (start with "[") |
| if(clas.startsWith("[")) { |
| if(clas.length() == 2 ) { |
| // it's an array of built in type, ignore |
| return; |
| } |
| if( 'L' == clas.charAt(1) ){ |
| // it's an array of objects, the class name is between [L and ; |
| // like [Ljava/lang/Object; |
| addClassString(clas.substring(2,clas.length()-1),from); |
| return; |
| } |
| if( '[' == clas.charAt(1) ){ |
| // it's an array of arrays, call recursive |
| addClassString(clas.substring(1),from); |
| return ; |
| } |
| throw new IOException("Can't recognize class name =" + clas); |
| } |
| |
| if( !clas.startsWith("java/") && allClasses.get(clas) == null) { |
| dependents.put(clas,from); |
| // System.out.println(" yes" ); |
| } else { |
| // System.out.println(" no" ); |
| } |
| } |
| } |