Native Library aus JAR laden

fReUnD

Grünschnabel
Hallo,

bisher habe ich nur passiv gelesen, aber jetzt komme ich so nicht weiter...

Um was es geht:
Ich möchte einfach nur meine native Bibliothek, die sich in demselben Java Archiv wie meine Classfiles befindet, laden.

Die Struktur in meinem JAR:
-cdromlib.dll
-foobar
-Server.class​
-... weitere classes​
-META-INF
-MANIFEST.MF​

Codeausschnitt aus der Server.java:
Code:
URL path = ClassLoader.getSystemResource("cdromlib.dll");
			
System.out.println(path.getFile());//Debug-Ausgabe
System.load(path.getFile());


Fall 1:
Die Binaries und die Lib liegen in D:/foobar/

path.getFile() liefert mir den richtigen Pfad in folgender Syntax zurück:
/D:/foobar/cdromlib.dll

System.load() lädt anhand des Strings die Bibliothek => alles in Ordnung


Fall 2:
Die Binaries liegen in D:/foobar/foobar.jar

path.getFile() liefert mir den Pfad in folgender Syntax zurück:
file:/D:/foobar/foobar.jar!/cdromlib.dll

System.load() kann mit diesem String nichts anfangen, Fehlermeldung:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Can't load library:
D:/foobar/foobar.jar!/cdromlib.dll
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.load0(Unknown Source)
at java.lang.System.load(Unknown Source)
at foobar.Server.<clinit>(Server.java:30)


Zusatzbemerkungen:

Zunächst habe ich auch System.loadLibrary() probiert, die Methode findet aber meine Library im Fall 2 (mit Java Archiv) ebenfalls nicht.
Müsste ich dafür in der Manifest spezielle Classpaths setzen?
Wenn ja, welche und wie sieht die Syntax aus?
Über Google bin ich auf "load.library.path" aufmerksam geworden, ich weiss aber nicht, wie ich das Attribut setzen soll.

Wenn ich das Problem doch über System.load() lösen möchte, welche Syntax erwartet die Methode für den Fall 2? Ich habe darüber in der API DOC leider keine Angaben gefunden.

Falls es noch einen ganz anderen Weg gibt, wäre ich auch für einen solchen Hinweis sehr dankbar.

MFG

fReUnD
 
Zuletzt bearbeitet:
Hi fReUnD!

Es kann auch an dem Package liegen, da das Programm die DLL-Datei nicht laden konnte!

Das, was in der DLL-Datei die Klasse in foobar steht, muss in Java auch in demselben Package stehen!

Im Package muss "foobar" stehen und dort muss die DLL-Datei drin sein, um sie mit System.load zu laden!

Dann müsste es eigentlich klappen, sonst frag mich weiter! ;-)

Gruß
Sebastian
 
Hallo!

Du könntest die native Bibliothek beispielsweise so aus dem jar ziehen:
(In diesem Beispiel ziehe ich die swt-win32-3232.dll aus dem org.eclipse.swt.win32.win32.x86_3.2.0.v3232l.jar und lege das jar in dem Verzeichnis ab aus dem die Anwendung heraus gestartet wurde.)
Java:
/**
 * 
 */
package de.tutorials;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

/**
 * @author Tom
 *
 */
public class SWTTest {

	static final String libraryName = "swt-win32-3232.dll";
	static final File libraryFile;
	
	static{
		InputStream inputStream = Display.class.getClassLoader().getResourceAsStream(libraryName);
		libraryFile = new File(libraryName);
		libraryFile.deleteOnExit();
		try {
			FileOutputStream fileOutputStream = new FileOutputStream(libraryFile);
			byte[] buffer = new byte[8192];
			int bytesRead;
			while((bytesRead = inputStream.read(buffer)) > 0){
				fileOutputStream.write(buffer, 0, bytesRead);
			}
			fileOutputStream.close();
			inputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Display display = new Display();
		
		Shell shell = new Shell(display);
		
		shell.setText("SWTTest");
		shell.pack();
		shell.open();

		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	}
}
... und in ein Verzeichnis kopieren, dass in java.library.path liegt. Löschen kann man die diese DLL von Java aus dann jedoch nicht (zumindest nicht aus dem selben Prozess), da die dll in den Speicher des Prozesses geladen wurde und erst wieder freigegeben wird wenn der Prozess beendet wurde.

Gruß Tom
 
Hi,

erstmal danke für die schnellen Antworten.

Sebastian29 hat gesagt.:
Im Package muss "foobar" stehen und dort muss die DLL-Datei drin sein, um sie mit System.load zu laden!

Wenn ich die DLL mit in das Package-Dir stecke, dann findet getSystemResource() nichts und gibt "nullnull" zurück.

Folglich lädt System.load() dann auch nichts =(

Zudem erwartet System.load() einen absoluten Pfad, also kann ich der Methode auch nicht einfach den DLL Namen übergeben. Um den absoluten Pfad herauszubekommen, komme ich doch nicht um die Verwendung von getSystemResource() herum oder?!



@Thomas:
Die Lösung hab ich auch schon bedacht, fänd sie aber nicht befriedigend, wenn die DLL dann in irgendeinem System-Dir rumbaumelt,...

Kann ich nicht den java.library.path so erweitern, dass das JAR mit durchsucht wird?

MFG

fReUnD
 
Hallo,

ich denke, dass das nicht moeglich ist. System.loadLibrary ueber mehrere Stufen irgendwann an die Native Methode java.lang.ClassLoader.NativeLibrary.load die nur einen Bibliotheksnamen uebergeben bekommt und dann mit Mitteln des Betriebsystems versucht die gewünschte Bibliothek zu laden ( wobei dann in: ("java.library.path");
("sun.boot.library.path"); geschaut wird). Ich denke die einfachste Möglichkeit wäre die einen Launcher mitzuliefern, der die dll aus dem Jar zieht die Applikation in einem neuen Prozess startet und bei beenden der Applikation die dll löscht (ist ja dann nicht mehr vom Applikationsprozess gesperrt). Noch einfacher wäre es natürlich die native Lib ausgepackt direkt mizuliefern ;-)

Gruß Tom
 
Hi,

nagut dann akzeptiere ich das mal schweren Herzens :rolleyes:

Danke für die Hinweise, wieder was gelernt!

MFG

fReUnD
 
Hi!

Mit getSystemRessource() brauchst du gar nicht, sondern du brauchst nur das in die Klasse unter dem Package "foobar" schreiben:

static
{
System.loadLibrary("cdromlib"); // <-- ohne .dll
}

Die DLL-Datei packst du im Hauptverzeichnis der jar-Datei! Der findet immer im Hauptverzeichnis, sonst kannst du sie nicht laden!

Gruß
Sebastian
 
Hallo!

Ja, das weiss ich, das klappt aber leider nur, wenn die DLL nicht im JAR selbst liegt, sondern im gleichen Verzeichnis, so mach ich das ja zurzeit auch.
Aber genau das will ich ja eigentlich vermeiden, ich möchte bei Auslieferung nur das JAR haben, dadrin sollen alle benötigten Filez enthalten sein, auch die DLL.

Wenn die DLL im JAR ist, dann findet System.loadLibrary("cdromlib") allerdings nichts mehr, deshalb habe ich versucht den Umweg über getSystemResource() und System.load() zu gehen, da die zweite Methode auch mit absoluten Pfaden umgehen kann, jedoch leider nicht, wenn diese auf ein JAR referenzieren. ;(

MFG

fReUnD
 

Neue Beiträge

Zurück