JNI-Dlls aus dynamischen Wrapper-Klassen laden

  • Themenstarter Themenstarter red99
  • Beginndatum Beginndatum
Status
Nicht offen für weitere Antworten.
R

red99

Gast
Hallo alle zusammen,

ich hab im Zuge der PC-AG unserer Schule ein Spiel geschrieben, bei dem man Spieler in Form von JNI-Wrapperklassen dynamisch über den URL-Klassloader laden kann. Die zugehörigen Delphi-DLLs(nach Anleitung JNI unter Delphi http://www.pacifier.com/~mmead/jni/delphi/) schreiben die anderen PC-AGler. Dann können sie noch die Wrapperklassen, die von einer Oberklasse erben anpassen und kompilieren.
Oberklasse:
Code:
public abstract class Native {
	
	public abstract byte[] start(int Index, byte[][] Positionen);
	public abstract byte[] zug(byte[][] Positionen);
	
	protected String Library;
	protected String Path;
	
	public Native(String Path)
	{
		this.Path=Path;
	}
	
	protected void init()
	{
		System.out.println(Path+Library);
		System.load(Path+Library);
	}
	
}
eine Wrapperklasse
Code:
public class SpielerTest extends Native {

	public SpielerTest(String Path)
	{
		super(Path);
		Library = "SpielerTest.dll";
		init();
	}
	public native byte[] start(int Index, byte[][] Positionen);
	public native byte[] zug(byte[][] Positionen);

}
so wird die Klasse eingebunden:
Code:
	public Main(String... libs)
	{
		try {
			nv = new Native[libs.length];

			for (int i=0;i<libs.length;i++)
			{
				File file = new File(libs[i]);
				File Pfad = new File(file.getParent());
				
				if (!(file.exists()&&file.isFile()))
					throw new FileNotFoundException
						("Library "+libs[i]+" konnte nicht gefunden werden!");
				
				URL url = Pfad.toURI().toURL();
				System.out.println(url);
				URLClassLoader uc = new URLClassLoader(new URL[]{url});
				
				libs[i]=file.getName().substring(0, file.getName().length()-6);
				
				Class<?> n = uc.loadClass(libs[i]);
				
				if(n.getSuperclass()==Native.class)
				{
					c = (Class<? extends Native>)n;
					Class clsargs = String.class;
					Constructor constr = c.getConstructor(clsargs);
					String strarg = Pfad.getCanonicalPath();
					if (!(strarg.endsWith("/")||strarg.endsWith("\\")))
							strarg+=File.separatorChar;
					
					nv[i] = (Native) constr.newInstance(strarg);
				}
				
			}
			Positionen = new byte[nv.length][0];
		} catch (InstantiationException e) {
				e.printStackTrace();
		} catch (IllegalAccessException e) {
				e.printStackTrace();
		} catch (ClassNotFoundException e) {
				e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Das Probelm ist jetzt, dass dies zwar geht wenn sich die Wrapperklasse mit ihrer DLL im Classpath befindet jedoch nicht, wenn sie irgendwo anders ist, was jedoch mit rein java ja geht. Dann kommt folgende Fehlermeldung:
Code:
Exception in thread "Timer-0" java.lang.UnsatisfiedLinkError: SpielerTest.start(I[[B)[B
	at SpielerTest.start(Native Method)
	at Main.start(Main.java:83)
	at GUI.start(GUI.java:327)
	at GUI.playTask(GUI.java:314)
	at GUI.access$9(GUI.java:306)
	at GUI$7.run(GUI.java:301)
	at java.util.TimerThread.mainLoop(Timer.java:512)
	at java.util.TimerThread.run(Timer.java:462)

Ich habe den Verdacht, dass es an der Bennennung mit JNI wie Java_Paket_Klassenname_Funktion liegt

Könnte mir bitte jemand helfen.
Vielen Dank schon mal
 
Hallo???
Hat keiner eine Idee oder wurde das hier übersehen oder hab ich was falsch gemacht???? 🙁
Bitte doch mal was schreiben. 🙂 Danke
 
Das Probelm ist jetzt, dass dies zwar geht wenn sich die Wrapperklasse mit ihrer DLL im Classpath befindet jedoch nicht, wenn sie irgendwo anders ist, was jedoch mit rein java ja geht

Ist doch logisch. Befindet die DLL nicht im Classpath weiß Java nicht was es machen soll ( da die DLL nicht gefunden wird) und es gibt ein Fehler.

Meines Wissens kannst du auch beim starten des Java Programm ein Parameter mitgeben, der Java sagt wo die DLL ist.
 
danke für die Antwort
ja nein daran liegt es nicht sonst wär die Fehlermeldung anders. Den Pfad zur Dll übergebe ich ja im Konstruktor von Native bzw SpielerTest. Die Wrapperklasse muss auch nicht im Classpath sein, da die ja über den URL-Classloader aufgerufen wird

Es hat denk ich was mit der JNI Funktions-Bennenung in der Native-Dll zu tun. Vielleicht hat jemand eine Idee wie man das lösen kann.
Fehler:
Code:
java.lang.UnsatisfiedLinkError: SpielerTest.start(I[[B)[B
 
Man lädt die Libraries bei JNI nur mit dem Namen. Also ohen Pfad und endung. Die Endung wird dann je nach OS von der VM angehängt. Also z.B. .dll in Windows.

Und die Lib muss dann im Pfad liegen oder per Parameter übergeben werden.
 
Weshalb man dann mit -Djava.library.path den Pfad zu einer DLL außerhalb des Classpath angeben muss.
 
mit system.load() kann aber auch n-bibliotheken mit pfad und endung laden das ging bei mir
die bibliothek wird auch gefunden sonst wäre eine andere fehlermeldung
 
Ohne "-Djava.library.path ./meinPfad/meineDll.dll" , wie LR es geschrieben hat, funktioniert das nicht. Okay, es gäbe noch den Weg die DLL in die JRE rein zu kopieren, aber das "gehört sich nicht".

- Alex
 
ja aber das ist ein Laufzeitargument beim start. Kann man das dann auch über System.setProperties() mit Wirkung ändern? Ich denke nein und dann wär dieser weg nicht machbar.
 
Was hälst du davon das einfach auszuprobieren (ist ja nur eine zeile code) und dann zu berichten obs geklappt hat?

Nebenbei gesagt:

Wo liegt das Problem eine Convention einzuführen ala "dynamische Wrapper-Klassen werden im Projektverzeichnis im Pfad XYZ abgelegt" ??

- Alex
 
Ok werds ausprobieren bloß grad kei zeit, morgen. Das mit dem Pfad hatte ich mir auch überlegt, anders hätt ichs halt besser gefunden. Aber wenns nicht geht kann man nichts machen.
 
Das ist in vielen Programmen so... gerade wenns um DLLs geht.

[update]
Ich hatte ja geschrieben: Probier's aus die System-Property zur Laufzeit zu ändern. Vielleicht geht's ja tatsächlich....

- Alex
 
Also wenn ich mit System.setProperty() den Pfad zu der Bibliothek zu java.library.path hinzufüge wird es über System.loadLibrary() nicht gefunden und mit System.load() ist es egal, damit wird es immer gefunden.
 
Also ich hab das Projekt umorganisiert und feste Ordner angelegt.
Sieht so aus als gäbe es unter obigen Umständen keinen Lösung.
-----
closed
 
Also, wenn das dynamische Anpassen des java.library.path das einzige Problem ist, versuche mal:

Code:
public final static void setJavaLibraryPath(String path) throws NoSuchFieldException, IllegalAccessException{

	        final String newPath = path + File.pathSeparator + System.getProperty("java.library.path");
	        System.setProperty("java.library.path", newPath);

	        final Field field = (java.lang.ClassLoader.class).getDeclaredField("sys_paths");
	        field.setAccessible(true);
	        if(field != null) field.set((java.lang.System.class).getClassLoader(), null);
	}

Gruß,
Michael
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben