On 03 Apr 2001 07:50:33 +0200, Egon Willighagen wrote: > > I've left out versioning issues. If one want to support multiple > > versions of the same library one could install LIBRARY-VERSION.jar, > > and install a symlink from LIBRARY.jar, but having compilers and > > VMs pick the right version is unclear to me. > > Versioning of jars is very important to me. It currently is not in the > Java policy, but i would love to see that happen. > > I like your proposol. I am not sure wath Debian policy says about > linking, but i can imagine that the link will also point to the most recent > release... Since we want to move forward... and that if applications need > an older version, they (the /usr/bin/exec) should classpath the old > library itself... I was playing around with a scheme; it went like this: -> versioned files and symlinks much like .so -> e.g. ---> antlr-1.2.3.jar ---> antlr-1.2.4.jar ---> antlr-2.0.1.jar ---> antlr-2.1.0.jar ---> antlr-2.1.3.jar ---> antlr.jar -> antlr-2.1.3.jar ---> antlr-2.jar -> antlr-2.1.3.jar ---> antlr-1.jar -> antlr-1.2.4.jar Then you have some wrapper script utlities to help you build a classpath. E.g. (pardon my scripting; I'm a Java programmer): #!/usr/bin/perl use "debclasspath"; @jars = ("antlr", "mystuff"); launchJava("com.mydotcom.mystuff.myapp", \@jars, \@ARGV); That would launch the app with the latest versions of whatever. And if you wanted to use an older version for a legacy app or whatever, or need a very specific version you just specify the version as far as you like: @jars = ("antlr-1", "mystuff-1.0.3.2.9"); Wrapper scripts are also good e.g. if there need to be special VM parameters passed (maximum heap size, no incremental image drawing, etc.) Alternately, it can all be done in Java, though that poses some other interesting problems (e.g. which java to do it in). Attached is source for our current Java launcher, which requires only one Jar in the classpath, and adds the others based on a config file. It then passes control to another class which is loads a registry of appname->classname+argument mappings. > > With this setup: > > (1) All Java compilers and VMs can compile find all "installed" .jars, > > without users having to fiddle with classpaths. > > An second option is to have a system wide CLASSPATH set, that would > include all jars... That's a really super bad idea, for two reasons: 1) jar explosion. Assuming we finally get a decent standard worked out, and everyone uses it, there could be dozens or hundreds of jar files. That creates needless overhead for the JVM's classloader, which would have to dig through the mess for your classes. The classloader certainly doesn't need to be any _slower_. 2) what good are versions then? How do you prevent two conflicting implementations from being in the classpath at the same time? What about order? I've never liked the "global classpath" approach; it always caused problems with our deployment. -- Paul Reavis [EMAIL PROTECTED] Design Lead Partner Software, Inc. http://www.partnersoft.com
package com.PartnerSoft.platform; import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.PrintWriter; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.net.URL; import java.net.URLClassLoader; import java.lang.reflect.Method; /** * Initiates the Java virtual machine; adds jars and then launches the named * class. * This class (and any others in this package) cannot depend on _anything_ else other than stock 1.3 JVM classes. * @author Brad Gray, Paul Reavis * Copyright 2000 Partner Software, Inc. */ public class LaunchJava { /** * Serious magic here. */ public static void main(String[] args) { // bogons? if (args.length < 1) { System.err.println("Not enough arguments. C'mon, you!"); System.exit(1); } boolean verbose = false; int startIndex = 0; boolean mightBeArgs = true; String jarPath = "Platform/resources/lib/"; while (mightBeArgs) { if (args[startIndex].equalsIgnoreCase("-v")) { verbose = true; startIndex = 1; } else if (args[startIndex].equalsIgnoreCase("-j")) { jarPath = args[startIndex + 1]; startIndex += 2; } else { mightBeArgs = false; } } // more bogons? if (args.length <= startIndex) { System.err.println("Not enough arguments. C'mon, you!"); System.exit(1); } String[] apps = new String[args.length - startIndex]; System.arraycopy(args, startIndex, apps, 0, apps.length); LaunchJava launcher = new LaunchJava(apps, jarPath, verbose); launcher.launch(); } //******** properties private PrintWriter outie; private boolean verbose; private String[] apps; private String jarPath; //******** deconstruction public LaunchJava(String[] apps, String jarPath, boolean verbose) { this.apps = apps; this.verbose = verbose; this.jarPath = jarPath; } //******** do something, will ya? public void launch() { try { // set up log if verbose if (verbose) { File logFile = new File("Shared" + File.separator + "data" + File.separator + "LaunchJava.log"); if (!logFile.getParentFile().exists()) logFile.getParentFile().mkdirs(); outie = new PrintWriter(new BufferedWriter(new FileWriter(logFile))); System.out.println("Logging to " + logFile); } // directory we expect to find jars and jars.txt in File jarDir = new File(jarPath); // load the jars.txt file ArrayList loaderList = new ArrayList(); File jarListFile = new File(jarDir, "jars.txt"); if (jarListFile.exists()) blab("Loading jar list from " + jarListFile); else die("Jar list file " + jarListFile + " does not exist!"); String newClassPath = ""; BufferedReader innie = new BufferedReader(new FileReader(jarListFile)); String line = null; while ((line = innie.readLine()) != null) { line = line.trim(); // just a little test if (line.length() > 4) { File jarFile = new File(jarDir, line); if (jarFile.exists()) blab(" -> " + jarFile); else die("Jar file " + jarFile + " does not exist!"); URL jarURL = new URL("file:" + jarFile.getCanonicalPath()); if (newClassPath == null) newClassPath = jarFile.getCanonicalPath(); else newClassPath += File.pathSeparator + jarFile.getCanonicalPath(); loaderList.add(jarURL); } } innie.close(); // now cast from a ArrayList to the url[] blab("Adding jars to classpath."); URL[] jarList = new URL[loaderList.size()]; for (int i = 0; i < jarList.length; i++) jarList[i] = (URL)loaderList.get(i); // now load the new jars URLClassLoader loader = new URLClassLoader(jarList); // and set the system classpath for JPython System.setProperty("java.class.path", newClassPath); // finally, pass the buck to AppLauncher blab("Preparing for AppLauncher."); // this is a bit of a pain... Class[] oohh = {apps.getClass()}; Class theClass = Class.forName("com.PartnerSoft.util.AppLauncher", false, loader); Method method = theClass.getDeclaredMethod("main", oohh); Object[] methodArgs = new Object[1]; methodArgs[0] = apps; // run it! blab("Passing to AppLauncher; LaunchJava says goodbye!"); if (verbose) { outie.close(); verbose = false; } method.invoke(theClass, methodArgs); } catch (Exception oopsie) { oopsie.printStackTrace(System.err); if (verbose) oopsie.printStackTrace(outie); die("Error occurred in LaunchJava"); } } private void blab(String message) throws IOException { if (verbose) outie.println(message); System.out.println(message); } private void die(String message) { if (verbose) { outie.println(message); outie.close(); } System.err.println(message); System.exit(1); } }