Christian Seiler: Classpath beim Aufruf über java -jar

Hallo allerseits,

Ich habe folgendes Java-Problem: Ich verwende in einem Projekt Xerces als XML-Parser. Der kommt ja in einer schönen JAR-Datei xercesImpl.jar. Wenn ich nun meinen Code, der Xerces verwende, auch in eine JAR-Datei packe, ein Manifest erzeuge und dann mittels

java -cp xercesImpl.java -jar meinprojekt.jar

versuche, mein Projekt aufzurufen, beschwert er sich, dass er Xerces nicht findet (die erste Xerces-Klasse, die er laden will, findet er nicht, dabei bricht er auch ab).

Ich habe mir daher mal folgenden Testcase gebastelt:

http://www.christian-seiler.de/temp/java-classpath-test.zip

Da drin ist ein Ant-Buildfile, das zwei .jar-Dateien erzeugt. Im folgenden der Code nochmal hier im Forum:

Es gibt zwei Pakete: example.a und example.b. Alle Klassen in example.a werden ins Paket example-a.jar gepackt, alle Klassen in example.b ins Paket example-b.jar.

In src/example/a befindet sich eine Datei Main.java mit folgendem Code:

package example.a;  
  
public class Main {  
 public static void main (String [] args) throws Exception {  
  System.out.println ("Class path: " + System.getProperty ("java.class.path"));  
  if (args.length >= 1) {  
   System.out.println ("Trying to load: " + args[0]);  
   Class test1 = Class.forName (args[0]);  
  }  
 }  
}

Zusätzlich habe ich folgendes Manifest für die example-a.jar-Datei:

Main-Class: example.a.Main

In src/example/b befindet sich eine Datei Test.java mit folgedem Code:

package example.b;  
  
public class Test {  
}

Der Testcase ist im Prinzip sehr billig: Die Main-Klasse des example.a-Pakets versucht die Klasse zu laden, deren Namen als erster Parameter übergeben wird.

Sobald man die *.jar mal erzeugt hat, kann man sich davon überzeugen, dass es funktioniert:

java -cp example-a.jar:example-b.jar example.a.Main example.a.Main

Ergibt:

Class path: example-a.jar:example-b.jar
 Trying to load: example.a.Main

Ich kann auch problemlos aus example-b.jar eine Klasse laden:

java -cp example-a.jar:example-b.jar example.a.Main example.b.Test

Ergibt:

Class path: example-a.jar:example-b.jar
 Trying to load: example.b.Test

Wenn ich nun aber -jar verwende und eine Klasse aus dem example-a.jar laden will (z.B. wieder Main selbst), dann funktioniert das auch noch:

java -cp example-b.jar -jar example-a.jar example.a.Main

Ergibt:

Class path: example-a.jar
 Trying to load: example.a.Main

Aber hier sieht man schon: Der Classpath ist hier nur example-a.jar, example-b.jar fehlt! Und deswegen fliegt auch folgender Aufruf auf die Schnauze:

java -cp example-b.jar -jar example-a.jar example.b.Test

Ergibt:

Class path: example-a.jar
 Trying to load: example.b.Test
 Exception in thread "main" java.lang.ClassNotFoundException: example.b.Test
  ...

[Die Umgebungsvariable CLASSPATH zu setzen ändert auch nichts.]

Meine Frage nun: Funktioniert das -jar grundsätzlich nicht mit einem zusätzlichen Classpath? Oder funktioniert das doch und man muss nur ein paar Dinge beachten?

Die Doku sagt lediglich lapidar:

| You can only specify one JAR file, which must contain all the application-specific code.

Das könnte man so lesen, als dass das wirklich nicht geht. Interpretiere ich das richtig?

Ja, ich weiß, ich kann selbst java.class.path ändern / mir mit einem java.net.URLClassLoader einen zusätzlichen ClassLoader erzeugen, den ich zum Laden der Klassen aus example-b.jar verwenden kann, aber das fände ich dann doch irgendwie etwas blöd...

Viele Grüße,
Christian

  1. Hallo allerseits,

    Im Chat habe ich ein paar Tipps bekommen und zusammenfassend kann man folgendes sagen:

    1. -jar setzt -cp wirklich außer Kraft.
    2. Man kann im Manifest einen Eintrag Class-Path: datei1.jar datei2.jar anlegen, der dann automatisch andere .jars nachlädt (natürlich nur wenn der relative oder absolute Pfad dieser jars bezogen auf das aktuelle jar bekannt ist).
    3. Ich werde jetzt wohl bei java -cp jar1:jar2:... mainklasse parameter bleiben.

    Viele Grüße,
    Christian