Dynamic java.library.path HowTo




written by Marco Ferretti on August 09, 2011, at 11:00 AM

en it

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().loadLibrary(String s);
Runtime.getRuntime().load(String s);

che sono equivalenti a usare

System.loadLibrary(String s);
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

 Runtime.getRuntime().load("TestJni");

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 :

  1. copiare la libreria nativa nel/nei percorsi di default del sistema operativo
  2. settare la variabile java.library.path all'avvio della Java Virtual machine
  3. 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 :

 public String getRunningPath() {
     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'

 String libraryPath = getRunningPath() + File.separator + "lib";
 

La funzione che interviene sulla variabile java.library.path e' la seguente

        public void addDir(String s) throws IOException {
                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 :

 addDir(getRunningPath() + File.separator + "lib") ;

in questo modo la chiamata

 System.loadLibrary(LIBRARY_CORE_NAME);

cerchera' la vostra libreria nativa anche in ./lib

Leave a comment

Name (required)
E-mail (required, will not be published)
Website
Comment

Enter value: Captcha