/* * $Log: jar.java,v $ * Revision 1.3 2000/06/13 19:29:45 stuart * Add comments, LGPL * * Revision 1.2 2000/06/13 19:17:31 stuart * minimize objects held by invoker * * Revision 1.1 2000/06/12 22:08:23 stuart * Initial revision * */ import java.io.*; import java.util.zip.*; import java.util.Enumeration; import java.util.Properties; import java.lang.reflect.Method; /** Execute a JAR. Find the main class from the MANIFEST and invoke it. @author Stuart D. Gathman Copyright (C) 2000 Business Management Systems, Inc.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ public class jar { private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; private static final String MAIN_CLASS = "Main-Class"; private BufferedReader r; /** Instances of jar represent a stripped version of java.util.jar.Manifest (from Java 2). */ private jar(InputStream f) throws IOException { r = new BufferedReader(new InputStreamReader(f,"UTF8")); /* Check first line for Manifest version. */ String s = r.readLine(); if (s == null || !s.startsWith("Manifest-Version: ")) throw new IOException("Invalid MANIFEST format."); } /** Return an attribute value. Since this is a special purpose class, we don't bother with a HashTable and just search sequentially. */ private String getValue(String key) throws IOException { for (;;) { String line = r.readLine(); if (line == null) break; int len = line.length(); if (len == 0) break; // end of section if (line.charAt(0) == ' ') continue; int pos = line.indexOf(':'); if (pos < 0) continue; if (len < pos + 3) continue; if (line.substring(0,pos).equalsIgnoreCase(key)) { String val = line.substring(pos + 2); String n = r.readLine(); while (n != null && n.length() > 0 && n.charAt(0) == ' ') { val = val + n.substring(1); n = r.readLine(); } return val; } } return null; } /** Find the MANIFEST entry. The standard says this search must be case insensitive. @return an InputStream for the MANIFEST if found. */ private static InputStream findIgnoreCase(ZipFile zipf,String name) throws IOException { Enumeration e = zipf.entries(); while (e.hasMoreElements()) { ZipEntry z = (ZipEntry)e.nextElement();; String fname = z.getName(); if (fname.equalsIgnoreCase(MANIFEST_NAME) || fname.startsWith("/") && fname.substring(1).equalsIgnoreCase(MANIFEST_NAME)) { return zipf.getInputStream(z); } } return null; } /** Find the main class in a JAR. */ private static String getMainClass(String jarname) throws IOException { ZipFile zipf = null; try { zipf = new ZipFile(jarname); } catch (IOException x) { jarname += ".jar"; zipf = new ZipFile(jarname); } InputStream f = findIgnoreCase(zipf,MANIFEST_NAME); if (f == null) fatal("Jar has no Manifest!"); String val = new jar(f).getValue(MAIN_CLASS); f.close(); zipf.close(); if (val == null) fatal("Manifest has no Main-Class entry."); return val; } private static void fatal(String msg) { System.err.println(msg); System.exit(1); } /** Modify class.path to include first element of argv (the JAR). @return a new argv less the first element. */ private static String[] setPath(String[] argv) { Properties prop = System.getProperties(); String path = prop.getProperty("java.class.path"); path = argv[0] + prop.getProperty("path.separator") + path; prop.put("java.class.path",path); String[] nargv = new String[argv.length - 1]; System.arraycopy(argv,1,nargv,0,nargv.length); return nargv; } public static void main(String[] argv) throws Exception { if (argv.length != 1) fatal("Usage: java jar jarfile[.jar] [args ...]"); String val = getMainClass(argv[0]); argv = setPath(argv); // must setPath before Class.forName() Method m = Class.forName(val).getMethod( "main",new Class[] { argv.getClass() }); val = null; // let main classname get GCed m.invoke(null,new Object[] { argv }); } }