Dynamic java.library.path HowTo
A volte capita di dover usare delle librerie native per fare delle operazioni ( ad esempio calcolo statistico ) che, se scritte in Java, richiedono molto piu' tempo del necessario. Il modo "classico" di caricare una libreria e' quello di usare
Runtime.getRuntime().load(String s);
che sono equivalenti a usare
System.load(String s);
La differenza tra le due chiamate e' che load(String s) vuole un path assoluto, mentre loadLibrary vuole il nome della libreria che viene risolto dal sistema operativo. Ad esempio, se volessimo usare la libreria "TestJni" la chiamata
verrebbe risolta cosi'
Windows: TestJni.dll Unix: libTestJni.so
e verrebbe cercata nella variabile di ambiente java.library.path
Personalmente, preferisco caricare le librerie utilizzando il core name per un motivo : posso creare le librerie native per tutti i sistemi operativi che mi interessano e caricare la libreria sempre nello stesso modo senza dover pensare alla piattaforma su cui sta girando il programma. Questo metodo, pero', si porta dietro il problema di caricare la libreria. Ci sono due strategie adottabili :
- copiare la libreria nativa nel/nei percorsi di default del sistema operativo
- settare la variabile java.library.path all'avvio della Java Virtual machine
- copiare la libreria in un percorso prestabilito all'interno della directory di installazione del programma
Il problema principale della soluzione 1 e' che non sempre si dispone dei diritti necessari per scrivere in queste directory ; la soluzione 2 consiste nel lanciare la virtual machine con il parametro
-Djava.library.path=path_alla_directory_che_contiene_la_libreria
sicuramente fattibile, ma personalmente lo considero poco elegante. La soluzione 3 implica che java.library.path venga modificato prima di caricare la libreria, ma apparte questo, permette di installare tutte le dipendenze della nostra applicazione in ( ad esempio ) ./lib senza preoccuparsi di dove sia effettivamente la directory .
Per poter modificare java.library.path serve il percorso assoluto della directory :
Logger.getLogger(getClass()).debug("determining running path");
URL url = Utils.class.getProtectionDomain().getCodeSource().getLocation();
File file;
try {
file = new File(url.toURI().getPath());
} catch (URISyntaxException e) {
file = new File(url.getPath());
}
Logger.getLogger(Utils.class).debug("resolved running path : " + file.getAbsolutePath());
if (file.isDirectory()) {
return file.getAbsolutePath();
} else {
return getPath(file);
}
}
public String getPath(File file) {
Logger.getLogger(Utils.class).debug("resolving path for " + file.getName());
if (file.isDirectory()) {
Logger.getLogger(Utils.class).debug(file.getName() + " is a directory so the running path is "+ file.getAbsolutePath() + File.separator);
return file.getAbsolutePath() + File.separator;
}
String path = file.getAbsolutePath();
path = path.substring(0, path.lastIndexOf(file.getName()));
Logger.getLogger(Utils.class).debug(
file.getName() + " is a file so the running path is " + path);
return path;
}
La funzione getRunningPath ritorna il path ( directory ) della classe principale del nostro programma. Se abbiamo mantenuto una alberatura delle directory standard, la directory che contiene le librerie del nostro programma sara'
La funzione che interviene sulla variabile java.library.path e' la seguente
try {
// This enables the java.library.path to be modified at runtime
// From a Sun engineer at http://forums.sun.com/thread.jspa?threadID=707176
//
Field field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] paths = (String[])field.get(null);
for (int i = 0; i < paths.length; i++) {
if (s.equals(paths[i])) {
return;
}
}
String[] tmp = new String[paths.length+1];
System.arraycopy(paths,0,tmp,0,paths.length);
tmp[paths.length] = s;
field.set(null,tmp);
System.setProperty("java.library.path", System.getProperty("java.library.path") + File.pathSeparator + s);
} catch (IllegalAccessException e) {
throw new IOException("Failed to get permissions to set library path");
} catch (NoSuchFieldException e) {
throw new IOException("Failed to get field handle to set library path");
}
}
E quindi la chiamata dalla nostra classe principale :
in questo modo la chiamata
cerchera' la vostra libreria nativa anche in ./lib
Recent Comments