# Eine JAR unter Linux starten ...



## Jano06 (5. Mrz 2009)

Hallo zusammen,

gleich vorab: Ich programmiere nicht in JAVA, sondern habe nur die Aufgabe ein unter Windows entwickeltes Programm unter Linux (Suse) lauffähig zu machen.

Das Programm erzeugt aus einer XML-Datei PDF-Dokumente. Es ist in einer JAR-Datei, nutzt aber noch weitere JAR's, die unter einem Unterverzeichnis "/lib" liegen.

Nachdem die JAR-Datei einen Fehler auswarf, dass die Startklasse nicht gefunden wurde, habe ich ins MANIFEST die Start-Class und den Class-Path aller "anderen JAR's" unter "/lib" eingetragen / eintragen lassen. Das MANIFEST sieht jetzt (für die Hauptanwendung) wie folgt aus:


```
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.0
Created-By: Rodrigo de Vivar
Main-Class: de.cas.printclient.BatchPrinter
Class-Path: lib/casprint.jar lib/batik.jar lib/avalon-framework-cvs-20
 020315.jar lib/swt.jar lib/ujac.jar lib/fop.jar lib/itext.jar lib/ite
 xttask.jar lib/itext-xml.jar lib/log4j-1.2.8.jar lib/registry.jar
```

Problem: Wenn ich jetzt das Hauptprogramm starte mit:
>java -jar casprintclient.jar
wird ein Fehler bzgl. einer Klasse ausgeworfen, die sich im "/lib"-Verzeichnis befindet,
nämlich in der "lib/casprint.jar". Hier die Fehlermeldung:


```
Exception in thread "main" java.lang.NoClassDefFoundError: de/cas/print/SpartenPrinter
        at de.cas.printclient.BatchPrinter.main(BatchPrinter.java:11)
Caused by: java.lang.ClassNotFoundException: de.cas.print.SpartenPrinter
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)
        ... 1 more
```

Frage: Was ist falsch ?
Muss in jedes MANIFEST der JAR's unter "/lib" auch die Starterklasse und ein Class-Path gesetzt werden ?

Vielen Dank für Eure Hilfe.

J.


----------



## Ebenius (5. Mrz 2009)

Du hast "lib/Library" eingetragen, musst aber "/lib/Library" (absolut) oder "Library" (relativ) eintragen.

Wieso liegen die Dateien eigentlich alle in "/lib"? Die gehören doch normaler Weise nach "/usr/lib" oder "/usr/share/java" oder nach "/opt/meinProgramm/lib", wenn sie über das Paket-Management installiert werden. Wenn nicht, gehören sie nach "/usr/local/lib" oder "/usr/local/share/java" oder "/opt/meinProgramm/lib". Bei SuSE ist das üblich: "/usr/[local/]share/java".

Ebenius


----------



## homer65 (5. Mrz 2009)

Hier mal ein Beispiel, wie ich das mache:

```
#!/bin/sh
CLASSPATH=$CLASSPATH:/home/christian/MyMP3Player/lib/jl1.0.1.jar
CLASSPATH=$CLASSPATH:/home/christian/MyMP3Player/lib/mysql-connector-java-5.1.7-bin.jar
CLASSPATH=$CLASSPATH:/home/christian/MyMP3Player/lib/MyMP3Player.jar
export CLASSPATH
java pack.Main /home/christian/MyMP3Player/MyMP3Player.ini
exit
```


----------



## Ebenius (5. Mrz 2009)

Homer, der Eingangsschreiber möchte doch aber das Manifest benutzen, damit das JAR selfcontained wird.

Mein Standard-Java Start-Skript sieht so aus: 
	
	
	
	





```
#! /bin/bash

# $Id$

case "$0" in
  /*) self="$0" ;;
   *) self="$(pwd)/$0" ;;
esac

selfdir=$( (cd "${self%/?*}" && pwd) )
[ "${selfdir}" ] || exit 1

topdir="${selfdir}/.."

CLASSPATH="$topdir/lib/myJar.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/myOtherJar.jar:${CLASSPATH}"

exec java "${JVM_ARGS:-}" "my.main.Clazz" "$@"
```

Ebenius


----------



## Jano06 (5. Mrz 2009)

Hallo und danke für Eure Antworten.

Nochmal zur Erkläreung:
Man hat mir eine CD gegeben mit der Verzeichnisstruktur:


```
cas.ico
casprintclient.jar
config
EABatchDruck.exe
EADruck.exe
jre
lib
```

(wohlgemerkt: Der Entwickler hat unter Windows entwickelt).
Er hat dann einen Wrapper mit "Launch4J" gebaut, was den  beiden "*.exe" Dateien (oben) entspricht. Mit denen kann ich natürlich unter Linux nichts anfangen.

Da ich sowieso von einem ShellSkript das JAVA Programm starten will, ist mir die Vorgehensweise, bzw. die Art des Ausfrufs egal. Hauptsache es läuft.

@Ebenius:
Ich habe zum testen zunächst einmal unter "/home/jano06/" die Verzeichnisse "/lib" und "/config", sowie das Programm "casprintclient.jar" abgelegt. JRE hat mir ein Admin in der Version 1.6.0_12 installiert mit dem Hinweis, ich solle die Variable "JAVA_HOME" exportieren, die auf "/usr/bin/java" gelinkt ist.
Dehalb habe ich mir um Verzeichnisse noch keine Gedanken gemacht. Später - wenn es läuft - suche ich mir einen anderen Speicherort (vermutlich: "/opt/meinProgramm/lib")

Und noch was:
Wenn es eine Alternative zu diesen MANIFEST-Einträgen gibt, nur zu .... hauptsache ist, das das Programm läuft. Euren Beispielen entnehme ich, dass ich auf die MANIFEST-Einträge verzichten und die CLASSPATH "ausserhalb" setzen kann !?

Bitte zeigt mir einen einfachen Weg auf ....

Danke, LG

J.


----------



## Ebenius (5. Mrz 2009)

Willst Du Software als AutoStart-CD benutzen? Dann findest Du hier ggf. Hinweise.

Wenn Du die Software installieren willst, installiere alles analog zur CD oben exkl. jre in ein Verzeichnis Deiner Wahl. In meinem Beispiel-Skript einfach die letzten vier Zeilen (Leerzeile inkl.) anpassen. In etwa so könnte es aussehen: 
	
	
	
	





```
#! /bin/bash

# $Id$

case "$0" in
  /*) self="$0" ;;
   *) self="$(pwd)/$0" ;;
esac

selfdir=$( (cd "${self%/?*}" && pwd) )
[ "${selfdir}" ] || exit 1

topdir="${selfdir}/.."

CLASSPATH="$topdir/lib/casprint.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/batik.jar:${CLASSPATH}"
# ... <== für jedes der JARs inkl dem JAR wo die Main-Klasse drin ist eine Zeile

exec java "${JVM_ARGS:-}" "de.cas.printclient.BatchPrinter" "$@"
```
Das speicherst Du ab. "chmod +x" nicht vergessen und ausführen.

PS: Du hast oben in Deiner Beschreibung "lib" und "/lib" vermischelt. Da solltest Du aufpassen. "/lib" ist absolut, wärend "lib" (alias "./lib") relativ ist, in der Regel zum CWD (current working directory).

Ebenius


----------



## Jano06 (6. Mrz 2009)

Hallo und danke für Deine Antwort.

Habe jetzt folgende Dateien/ Verzeichnisse unterhalb von "/home/jano06":


```
casprintclient.jar
config
lib
PDF
XML
casStarter.sh
```

Im Verzeichnis "lib" habe ich alle JAR's so belassen, wie ich sie bekommen habe. Die Verzeichnisse "PDF" und "XML" sind die Ein- und Ausgabeverzeichnisse, in die das Programm schreiben soll. Im Verzeichnis "config" steht nur eine Datei "settings.properties", in der 2 Zeilen stehen, die genau die Ein-/ und Ausgabeverzeichnisse spezifizieren.
Das Skript "casStarter.sh" ist "Dein" von mir angepasstes Skript:


```
#!/bin/sh
unset LC_ALL
unset LANG

export JAVA_HOME=/usr/bin/java
# $Id$

case "$0" in
  /*) self="$0" ;;
   *) self="$(pwd)/$0" ;;
esac

selfdir=$( (cd "${self%/?*}" && pwd) )
[ "${selfdir}" ] || exit 1

topdir="${selfdir}/.."

CLASSPATH="$topdir/lib/avalon-framework-cvs-20020315.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/batik.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/casprint.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/fop.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/itext-xml.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/itext.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/itexttask.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/log4j-1.2.8.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/registry.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/swt.jar:${CLASSPATH}"
CLASSPATH="$topdir/lib/ujac.jar:${CLASSPATH}"

exec $JAVA_HOME "${JVM_ARGS:-}" "de.cas.printclient.BatchPrinter" "$@",
```

Beim Aufruf erhalte ich jetzt folgende Meldung:


```
> ./casStarter.sh
Exception in thread "main" java.lang.NoClassDefFoundError:
Caused by: java.lang.ClassNotFoundException:
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)
Could not find the main class: .  Program will exit.
```

Was ist falsch ?

Danke und Gruss

J.


----------



## Ebenius (6. Mrz 2009)

Woher kommt denn das Komma am Ende der letzten Zeile?

Und Wieso das "JAVA_HOME"-Zeugs da? Lass es weg, "java" liegt in /usr/bin und ist damit im Pfad. Wenn sich ein Nutzer ein anderes Java installieren möchte, kann er dies zum Beispiel in /home/ebenius/bin/java ablegen und die PATH-Variable verändern. Dein Skript sollte ihn dann nicht zwingen, das andere Java zu benutzen.

Was passiert denn, wenn Du die letzte Zeile ersetzt? 
	
	
	
	





```
set -x
exec java "${JVM_ARGS:-}" "de.cas.printclient.BatchPrinter" "$@"
```

BTW: Ersetz mal noch alle "$topdir" durch "${topdir}". Hab ich vergessen. Das macht zwar keinen Unterschied, ist aber besserer Stil.

Ebenius


----------



## mvitz (6. Mrz 2009)

Du müsstest die casprintclient.jar auch noch mit in den Classpath aufnehmen!


----------



## Ebenius (6. Mrz 2009)

habi55 hat gesagt.:


> Du müsstest die casprintclient.jar auch noch mit in den Classpath aufnehmen!


Stimmt. Das erklärt aber die Fehlermeldung nicht. Mal sehen was Jano schreibt...

Jano, Du kannst auch alle "CLASSPATH=trallalala"-Zeilen ersetzen durch ein Konstrukt mit dem Du alle JAR-Dateien des Verzeichnisses in den CLASSPATH setzt: 
	
	
	
	





```
for jarfile in "${topdir}/lib/"* ; do
  CLASSPATH="${topdir}/lib/${jarfile}:${CLASSPATH}"
done
```
Ebenius


----------



## mvitz (6. Mrz 2009)

Jano06 hat gesagt.:


> Beim Aufruf erhalte ich jetzt folgende Meldung:
> 
> 
> ```
> ...



Mhm: Could not find the main class: .  Program will exit.
Erklärt es evtl doch? Die Mainklasse ist doch vermutlich in casprintclient.jar, welche wiederum nicht im Classpath ist?

[Edit] Wobei der . in der Meldung komisch ist, hast schon recht...


----------



## Jano06 (6. Mrz 2009)

Hallo zusammen,

konnte einen Kollegen finden, der das Programm unter Linux gestartet hat.
Einerseits war der Tipp von "habi55" richtig, andererseits sagte mein Kollege, dass die Reihenfolge auch noch wichtig wäre.

Noch was:
Das "Hauptprogramm" (also: casprintclient.jar) habe ich mit in das "lib"-Verzeichnis verschoben.

Dann der Aufruf (aus einem ShellSkript):

```
#!/bin/sh
unset LC_ALL
unset LANG

export JAVA_HOME=/usr/bin/java

CLASSPATH="./lib/ujac.jar:${CLASSPATH}"
CLASSPATH="./lib/log4j-1.2.8.jar:${CLASSPATH}"
CLASSPATH="./lib/itext.jar:${CLASSPATH}"
CLASSPATH="./lib/swt.jar:${CLASSPATH}"
CLASSPATH="./lib/itext-xml.jar:${CLASSPATH}"
CLASSPATH="./lib/fop.jar:${CLASSPATH}"
CLASSPATH="./lib/batik.jar:${CLASSPATH}"
CLASSPATH="./lib/itexttask.jar:${CLASSPATH}"
CLASSPATH="./lib/avalon-framework-cvs-20020315.jar:${CLASSPATH}"
CLASSPATH="./lib/casprintclient.jar:${CLASSPATH}"
CLASSPATH="./lib/casprint.jar:${CLASSPATH}"
CLASSPATH=".:${CLASSPATH}"

"$JAVA_HOME" -cp "${CLASSPATH}" "de.cas.printclient.BatchPrinter"
```

Damit läuft's !!!

Vielen Dank nochmal für Eure Hilfen !!!

J.

p.s. Schaue vielleicht nochmal hier vorbei, weil ich noch einen JAVA "Port-Listener" zum Laufen bringen muss, der die XML-Dateien von einem Webservice annimmt. Ausserdem kommt vielleicht noch ein Popup-Programm (von der Stange) auf "meinen" Server, der Systemmeldungen auf einen Windows-Rechner schicken soll.


----------



## Ebenius (6. Mrz 2009)

Mein Skript gefällt mir besser. Das Skript das Du eben geschrieben hast, funktioniert bspw. nur, wenn Du Dich im richtigen Verzeichnis befindest. Warum $LANG leer gemacht wird ist mir unklar.

BTW: Mein Skript braucht die Bash. "#! /bin/sh" ist falsch. Betrifft das vorhergehende Skript das Du schriebst.

Ebenius


----------



## Jano06 (6. Mrz 2009)

Hallo.

Dann frag' ich doch nochmal nach (zu Deinem Skript). Würde es gern nutzen.
Habe ein paar Echo-Ausgaben eingebaut, um nachzuvollziehen, was "self", "selfdir" und "topdir" machen. Leider verstehe ich das nicht. Vielleicht magst Du mir das mal erklären.


```
#!/bin/sh

echo "First: $0"

case "$0" in
  /*) self="$0" ;;
   *) self="$(pwd)/$0" ;;
esac

echo "Self: $self"

selfdir=$( (cd "${self%/?*}" && pwd) )
[ "${selfdir}" ] || exit 1

echo "Selfdir: $selfdir"

topdir="${selfdir}/.."

echo "Topdir: $topdir"
```

ergibt das Ausgabe:


```
> ./test.sh
First: ./test.sh
Self: /home/jano/./test.sh
Selfdir: /home/jano
Topdir: /home/jano/..
```

Wenn ich jetzt versuche den CLASSPATH aufzubauen - so wie Du es SKript machst -, also: 
	
	
	
	





```
CLASSPATH="$topdir/lib/casprint.jar:${CLASSPATH}"
```
,

dann steht doch da:

CLASSPATH=/home/jano/../lib/casprint.jar" ..... oder ?

Der Befehlsprozessor kann doch das "/../" nicht auflösen, oder ?

Gruss

J.


----------



## Ebenius (6. Mrz 2009)

Aarg bin ich doof... Normaler Weise hat man diese Verzeichnisstruktur:

```
[ProgrammVerzeichnis] (alias ${topdir})
 + [bin] (alias ${selfdir})
    + startscript (alias ${self})
 + [lib]
```
Wenn Du natürlich kein "bin"-Verzeichnis hast in dem das Start-Skript steht, dann muss das Skript angepasst werden: 
	
	
	
	





```
#!/bin/sh

echo "First: $0"

case "$0" in
  /*) self="$0" ;;
   *) self="$(pwd)/$0" ;;
esac

echo "Self: $self"

selfdir=$( (cd "${self%/?*}" && pwd) )
[ "${selfdir}" ] || exit 1

echo "Selfdir: $selfdir"

### Hier ist's anders ###
topdir="${selfdir}"
#########################

echo "Topdir: $topdir"
```

Ebenius


----------



## mvitz (6. Mrz 2009)

Was mir gerade noch auffällt. In Posting 1 ist ja der Inhalt der Manifest-Datei.
Hast du die so 1:1 kopiert? Weil wenn ja, dann fehlt die Leerzeile, die am Ende des Manifests sein muss.

Manifest ist nämlich wohl der eleganteste Weg imho. Funktioniert unter Windows/Linux/Mac und es ist egal, aus welchem Verzeichnis man das jar aufruft.

Ansonsten hatten wir das auch schon mal in der Firma, dass man den Class-Path vor der Main-Class angeben musste (wieso auch immer).


----------

