java.lang.UnsatisfiedLinkError non *****. dll dans java.library.path

92

Comment puis-je charger un fichier dll personnalisé dans mon application Web? J'ai essayé ce qui suit:

  • Copié toutes les dll requises dans le system32dossier et essayé de charger l'une d'entre elles dans le ServletconstructeurSystem.loadLibrary
  • DLL requises copiées dans tomcat_home/shared/libettomcat_home/common/lib

Toutes ces dll sont dans WEB-INF/libl'application Web

Ketan Khairnar
la source

Réponses:

156

Pour System.loadLibrary()fonctionner, la bibliothèque (sous Windows, une DLL) doit être dans un répertoire quelque part sur votre PATH ou sur un chemin répertorié dans la java.library.pathpropriété système (vous pouvez donc lancer Java comme java -Djava.library.path=/path/to/dir).

En outre, pour loadLibrary(), vous spécifiez le nom de base de la bibliothèque, sans le .dllà la fin. Donc, pour /path/to/something.dll, vous utiliseriez simplement System.loadLibrary("something").

Vous devez également regarder exactement ce UnsatisfiedLinkErrorque vous obtenez. Si cela dit quelque chose comme:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no foo in java.library.path

alors il ne peut pas trouver la bibliothèque foo (foo.dll) dans votre fichier PATHou java.library.path. Si cela dit quelque chose comme:

Exception in thread "main" java.lang.UnsatisfiedLinkError: com.example.program.ClassName.foo()V

alors quelque chose ne va pas avec la bibliothèque elle-même en ce sens que Java n'est pas capable de mapper une fonction Java native de votre application à son homologue natif réel.

Pour commencer, je mettrais un peu de journalisation autour de votre System.loadLibrary()appel pour voir si cela s'exécute correctement. S'il lève une exception ou n'est pas dans un chemin de code réellement exécuté, vous obtiendrez toujours le dernier type d' UnsatisfiedLinkErrorexplication ci-dessus.

Pour rappel, la plupart des gens placent leurs loadLibrary()appels dans un bloc d'initialisation statique de la classe avec les méthodes natives, pour s'assurer qu'il est toujours exécuté exactement une fois:

class Foo {

    static {
        System.loadLibrary('foo');
    }

    public Foo() {
    }

}
Adam Batkin
la source
1
En mettant toutes les DLL dans System32 et en utilisant System.loadLibrary ("quelque chose") a fonctionné. Je faisais System.loadLibrary ("something.dll") plus tôt. Pourquoi ne peut-il pas charger toutes les DLL de WEB-INF? Je suppose qu'il charge tous les pots par défaut. Que puis-je faire pour charger ces dll à partir de WEB-INF directement au lieu de System32 / les spécifier dans java.library.path
Ketan Khairnar
2
+1 pour le 'utiliser "bla" au lieu de "bla.dll" pour loadLibrary()remarque - très utile lorsque vous n'avez aucune idée de ce que vous faites mal.
MarnixKlooster ReinstateMonica
21
Sur mon système (Linux et java7), j'ai besoin d'un libpréfixe. Donc System.loadLibrary("foo")besoins libfoo.so.
kristianlm
2
Merci. Cela a fonctionné pour moi lorsque j'ai mis à jour "PATH" pour Windows afin qu'il contienne un dossier contenant un fichier * .so.
user613114
J'aurais pu jurer que OSX requis blah.jnilibet Linux libblah.so. Eh bien, deux heures plus tard, après de nombreux essais, je suis arrivé à la conclusion qu'OSX nécessite également un libpréfixe
Jovan Perovic
15

Changer la variable 'java.library.path' lors de l'exécution ne suffit pas car elle n'est lue qu'une seule fois par JVM. Vous devez le réinitialiser comme:

System.setProperty("java.library.path", path);
//set sys_paths to null
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);

S'il vous plaît, prenez un butin sur: Modification du chemin de la bibliothèque Java au moment de l'exécution .

Alexandre
la source
10

La réponse originale d'Adam Batkin vous mènera à une solution, mais si vous redéployez votre application Web (sans redémarrer votre conteneur Web), vous devriez rencontrer l'erreur suivante:

java.lang.UnsatisfiedLinkError: Native Library "foo" already loaded in another classloader
   at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1715)
   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1646)
   at java.lang.Runtime.load0(Runtime.java:787)
   at java.lang.System.load(System.java:1022)

Cela se produit car le ClassLoader qui a chargé à l'origine votre DLL fait toujours référence à cette DLL. Cependant, votre application Web est maintenant en cours d'exécution avec un nouveau ClassLoader, et comme la même JVM est en cours d'exécution et qu'une JVM n'autorisera pas 2 références à la même DLL, vous ne pouvez pas la recharger . Ainsi, votre application Web ne peut pas accéder à la DLL existante et ne peut pas en charger une nouvelle. Alors ... vous êtes coincé.

La documentation ClassLoader de Tomcat explique pourquoi votre application Web rechargée s'exécute dans un nouveau ClassLoader isolé et comment vous pouvez contourner cette limitation (à un niveau très élevé).

La solution est d'étendre un peu la solution d'Adam Batkin:

   package awesome;

   public class Foo {

        static {
            System.loadLibrary('foo');
        }

        // required to work with JDK 6 and JDK 7
        public static void main(String[] args) {
        }

    }

Ensuite, placez un fichier jar contenant JUSTE cette classe compilée dans le dossier TOMCAT_HOME / lib.

Maintenant, dans votre application web, il vous suffit de forcer Tomcat à référencer cette classe, ce qui peut être fait aussi simplement que ceci:

  Class.forName("awesome.Foo");

Maintenant, votre DLL doit être chargée dans le chargeur de classe commun et peut être référencée à partir de votre application Web même après avoir été redéployée.

Ça a du sens?

Une copie de référence de travail peut être trouvée sur le code google, static-dll-bootstrapper .

Matt M
la source
1
Cette réponse est excellente pour les serveurs d'applications et devrait avoir sa propre question car elle est gaspillée sur cette question.
JoshDM
8

Vous pouvez utiliser System.load()pour fournir un chemin absolu qui est ce que vous voulez, plutôt qu'un fichier dans le dossier de bibliothèque standard pour le système d'exploitation respectif.

Si vous voulez des applications natives qui existent déjà, utilisez System.loadLibrary(String filename). Si vous voulez fournir le vôtre, vous êtes probablement mieux avec load ().

Vous devriez également pouvoir l'utiliser correctement loadLibraryavec l' java.library.pathensemble. Voir la ClassLoader.javasource d'implémentation montrant les deux chemins en cours de vérification (OpenJDK)

Philip Whitehouse
la source
Merci. L'utilisation de la charge est en fait beaucoup plus facile et intuitive à mon humble avis. Impossible de faire fonctionner loadLibrary, peu importe ce que j'ai fait ...
Plankalkül
2
Cette solution ne fonctionne pas pour les bibliothèques qui chargent leurs propres bibliothèques natives.
Justin Skiles
7

Dans le cas où le problème est que System.loadLibrary ne peut pas trouver la DLL en question, une idée fausse courante (renforcée par le message d'erreur de Java) est que la propriété système java.library.path est la réponse. Si vous définissez la propriété système java.library.path sur le répertoire où se trouve votre DLL, alors System.loadLibrary trouvera effectivement votre DLL. Cependant, si votre DLL dépend à son tour d'autres DLL, comme c'est souvent le cas, alors java.library.path ne peut pas aider, car le chargement des DLL dépendantes est entièrement géré par le système d'exploitation, qui ne sait rien de java.library. chemin. Ainsi, il est presque toujours préférable de contourner java.library.path et d'ajouter simplement le répertoire de votre DLL à LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS) ou Path (Windows) avant de démarrer la JVM.

(Remarque: j'utilise le terme «DLL» dans le sens générique de DLL ou de bibliothèque partagée.)

Ian Emmons
la source
5

Si vous devez charger un fichier relatif à un répertoire dans lequel vous vous trouvez déjà (comme dans le répertoire actuel), voici une solution simple:

File f;

if (System.getProperty("sun.arch.data.model").equals("32")) {
    // 32-bit JVM
    f = new File("mylibfile32.so");
} else {
    // 64-bit JVM
    f = new File("mylibfile64.so");
}
System.load(f.getAbsolutePath());
Rick C. Hodgin
la source
4

Pour ceux qui recherchent java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path

J'étais confronté à la même exception; J'ai tout essayé et les choses importantes pour le faire fonctionner sont:

  1. Version correcte de pdf lib.jar (dans mon cas, c'était une mauvaise version jar conservée dans le runtime du serveur)
  2. Créez un dossier et conservez le jar pdflib dedans et ajoutez le dossier dans votre variable PATH

Cela a fonctionné avec tomcat 6.

Mangesh M. Kulkarni
la source
3
  1. Si vous pensez avoir ajouté un chemin d'accès à la bibliothèque native %PATH%, essayez de tester avec:

    System.out.println(System.getProperty("java.library.path"))

Il devrait vous montrer si votre dll est activé %PATH%

  1. Redémarrez l'idée IDE, qui a semblé fonctionner pour moi après avoir configuré la variable env en l'ajoutant au %PATH%
AlexPes
la source
2

Pauvre de moi ! a passé une journée entière derrière cela, écrivez-le ici si un organisme reproduit ce problème.

J'essayais de charger comme Adam l'a suggéré, mais j'ai ensuite été pris avec l'exception AMD64 vs IA 32.Si dans tous les cas, après avoir travaillé selon la procédure pas à pas d'Adam (sans aucun doute le meilleur choix), essayez d'avoir une version 64 bits du dernier jre. vos JRE ET JDK sont 64 bits et vous les avez correctement ajoutés à votre classpath.

Mon exemple de travail va ici: erreur de lien non satisfaite

Sayannayas
la source
0

Pour Windows, j'ai trouvé que lorsque j'ai chargé les filles (appels jd2xsx.dll & ftd2xx.dll) dans le dossier windowws / system32, cela a résolu les problèmes. J'ai ensuite eu un problème avec mon plus récent fd2xx.dll concernant les paramètres, c'est pourquoi j'ai dû charger l'ancienne version de cette dll. Je devrai le découvrir plus tard.

Remarque: le jd2xsx.dll appelle le ftd2xx.dll, il se peut donc que la définition du chemin d'accès pour le jd2xx.dll ne fonctionne pas.

Dan Carte
la source
0

J'utilise Mac OS X Yosemite et Netbeans 8.02, j'ai la même erreur et la solution simple que j'ai trouvée est comme ci-dessus, cela est utile lorsque vous devez inclure une bibliothèque native dans le projet. Alors faites la suite pour Netbeans:

1.- Right click on the Project
2.- Properties
3.- Click on RUN
4.- VM Options: java -Djava.library.path="your_path"
5.- for example in my case: java -Djava.library.path=</Users/Lexynux/NetBeansProjects/NAO/libs>
6.- Ok

J'espère que cela pourrait être utile pour quelqu'un. Le lien où j'ai trouvé la solution est ici: java.library.path - Qu'est-ce que c'est et comment l'utiliser

alexventuraio
la source
Salut Alex, j'ai une question ici est-il un problème si le chemin de la bibliothèque comprend un espacement dans le chemin de cette ligneVM Options: java -Djava.library.path="your_path"
Lokesh Pandey
Mmm, je ne travaille pas avec Java depuis un an, mais comme des conflits peuvent apparaître, je recommande d' éviter d'ajouter des espaces vides dans le chemin du fichier .
alexventuraio
0

J'ai eu le même problème et l'erreur était due à un changement de nom de la DLL. Il se peut que le nom de la bibliothèque soit également écrit quelque part dans la DLL. Quand j'ai remis son nom d'origine, j'ai pu charger en utilisantSystem.loadLibrary

Alessandro Marini
la source
0

C'est simple, écrivez simplement java -XshowSettings: properties sur votre ligne de commande dans Windows, puis collez tous les fichiers dans le chemin indiqué par java.library.path.

Ali Sasoli
la source