[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: java library installation issues



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                                      preavis@partnersoft.com
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);
		}
	}


Reply to: