java.lang.UnsatisfiedLinkError no *****. dll em java.library.path

92

Como posso carregar um arquivo dll personalizado em meu aplicativo da web? Tentei o seguinte:

  • Copiei todas as dlls necessárias na system32pasta e tentei carregar uma delas no ServletconstrutorSystem.loadLibrary
  • Dlls necessários copiados em tomcat_home/shared/libetomcat_home/common/lib

Todas essas dlls estão no WEB-INF/libaplicativo da web

Ketan Khairnar
fonte

Respostas:

154

Para System.loadLibrary()funcionar, a biblioteca (no Windows, uma DLL) deve estar em um diretório em algum lugar do seu PATH ou em um caminho listado na java.library.pathpropriedade do sistema (para que você possa iniciar o Java como java -Djava.library.path=/path/to/dir).

Além disso, para loadLibrary(), você especifica o nome base da biblioteca, sem o .dllno final. Então, para /path/to/something.dll, você apenas usaria System.loadLibrary("something").

Você também precisa olhar exatamente o UnsatisfiedLinkErrorque está obtendo. Se disser algo como:

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

então ele não consegue encontrar a biblioteca foo (foo.dll) em seu PATHou java.library.path. Se disser algo como:

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

então, algo está errado com a própria biblioteca no sentido de que o Java não é capaz de mapear uma função Java nativa em seu aplicativo para sua contraparte nativa real.

Para começar, eu colocaria algum registro em torno de sua System.loadLibrary()chamada para ver se ela é executada corretamente. Se ele lançar uma exceção ou não estiver em um caminho de código que é realmente executado, você sempre obterá o último tipo UnsatisfiedLinkErrorexplicado acima.

Como nota lateral, a maioria das pessoas coloca suas loadLibrary()chamadas em um bloco inicializador estático na classe com os métodos nativos, para garantir que ele seja sempre executado exatamente uma vez:

class Foo {

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

    public Foo() {
    }

}
Adam Batkin
fonte
1
Colocando todas as dlls em System32 e usando System.loadLibrary ("something") funcionou. Eu estava fazendo System.loadLibrary ("something.dll") anteriormente. Por que não consigo carregar todas as dlls do WEB-INF? Acho que carrega todos os frascos por padrão. O que posso fazer para carregar essas dlls de WEB-INF diretamente em vez de System32 / especificá-las em java.library.path
Ketan Khairnar
2
+1 para 'use "bla" em vez de "bla.dll" para loadLibrary()comentários - muito útil quando você não tem ideia do que está fazendo de errado.
MarnixKlooster ReinstateMonica
20
No meu sistema (Linux e java7), preciso de um libprefixo. Então System.loadLibrary("foo")precisa libfoo.so.
kristianlm
2
Obrigado. Funcionou para mim quando atualizei "PATH" para windows de modo que contenha uma pasta com o arquivo * .so.
user613114
15

Alterar a variável 'java.library.path' no tempo de execução não é suficiente porque ela é lida apenas uma vez pela JVM. Você deve redefini-lo como:

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);

Por favor, faça uma recompensa em: Changing Java Library Path at Runtime .

Alexandre
fonte
10

A resposta original de Adam Batkin levará você a uma solução, mas se você reimplantar seu webapp (sem reiniciar seu contêiner da web), deverá ocorrer o seguinte erro:

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)

Isso acontece porque o ClassLoader que carregou originalmente sua DLL ainda faz referência a essa DLL. No entanto, seu webapp agora está sendo executado com um novo ClassLoader e, como a mesma JVM está em execução e uma JVM não permite 2 referências à mesma DLL, você não pode recarregá- la. Portanto, seu webapp não pode acessar a DLL existente e não pode carregar uma nova. Então .... você está preso.

Documentação do ClassLoader do Tomcat descreve por que seu webapp recarregado é executado em um novo ClassLoader isolado e como você pode contornar essa limitação (em um nível muito alto).

A solução é estender um pouco a solução de 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) {
        }

    }

Em seguida, coloque um jar contendo APENAS esta classe compilada na pasta TOMCAT_HOME / lib.

Agora, dentro do seu webapp, você só precisa forçar o Tomcat a fazer referência a essa classe, o que pode ser feito da seguinte maneira:

  Class.forName("awesome.Foo");

Agora sua DLL deve ser carregada no carregador de classe comum e pode ser referenciada a partir de seu webapp mesmo depois de ser reimplantada.

Faz sentido?

Uma cópia de referência de trabalho pode ser encontrada no código do google, static-dll-bootstrapper .

Matt M
fonte
1
Essa resposta é excelente para servidores de aplicativos e deve ter sua própria pergunta, pois é desperdiçada nessa pergunta.
JoshDM de
8

Você pode usar System.load() para fornecer um caminho absoluto que é o que você deseja, em vez de um arquivo na pasta da biblioteca padrão para o respectivo sistema operacional.

Se você quiser aplicativos nativos que já existem, use System.loadLibrary(String filename). Se você deseja fornecer o seu, provavelmente é melhor usar load ().

Você também deve ser capaz de usar loadLibraryo java.library.pathconjunto corretamente. Veja ClassLoader.javapara fonte de implementação mostrando ambos os caminhos sendo verificados (OpenJDK)

Philip Whitehouse
fonte
Obrigado. Usar o load é IMHO muito mais fácil e intuitivo. Não foi possível fazer o loadLibrary funcionar independentemente do que eu fiz ...
Plankalkül
2
Esta solução não funciona para bibliotecas que carregam suas próprias bibliotecas nativas.
Justin Skiles,
7

No caso em que o problema é que System.loadLibrary não consegue encontrar a DLL em questão, um equívoco comum (reforçado pela mensagem de erro do Java) é que a propriedade de sistema java.library.path é a resposta. Se você definir a propriedade de sistema java.library.path para o diretório onde sua DLL está localizada, então System.loadLibrary realmente encontrará sua DLL. No entanto, se sua DLL, por sua vez, depender de outras DLLs, como costuma ser o caso, java.library.path não pode ajudar, porque o carregamento das DLLs dependentes é gerenciado inteiramente pelo sistema operacional, que nada sabe sobre java.library. caminho. Portanto, é quase sempre melhor ignorar java.library.path e simplesmente adicionar o diretório da DLL a LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS) ou Path (Windows) antes de iniciar a JVM.

(Observação: estou usando o termo "DLL" no sentido genérico de DLL ou biblioteca compartilhada.)

Ian Emmons
fonte
5

Se você precisa carregar um arquivo relativo a algum diretório onde você já está (como no diretório atual), aqui está uma solução fácil:

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
fonte
4

Para quem procura java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path

Eu estava enfrentando a mesma exceção; Tentei de tudo e coisas importantes para fazer funcionar são:

  1. Versão correta do pdf lib.jar (no meu caso, era a versão errada do jar mantido no tempo de execução do servidor)
  2. Faça uma pasta e mantenha o jar pdflib nela e adicione a pasta na sua variável PATH

Funcionou com o tomcat 6.

Mangesh M. Kulkarni
fonte
3
  1. Se você acredita que adicionou um caminho de lib nativo para %PATH%, tente testar com:

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

Deve mostrar se a dll está ativada %PATH%

  1. Reinicie o IDE Idea, que pareceu funcionar para mim depois que configurei a variável env, adicionando-a ao %PATH%
AlexPes
fonte
2

Pobre de mim! passou um dia inteiro atrás disso. Escrevendo aqui embaixo se algum corpo replica esse problema.

Eu estava tentando carregar como Adam sugeriu, mas fui pego com a exceção AMD64 vs IA 32. Se em qualquer caso, depois de trabalhar de acordo com o passo a passo de Adam (sem dúvida a melhor escolha), tente ter uma versão de 64 bits do jre mais recente. Certifique-se de seu JRE E JDK são de 64 bits e você os adicionou corretamente ao seu classpath.

Meu exemplo de trabalho vai aqui: erro de link não satisfeito

Sayannayas
fonte
0

Para o Windows, descobri que quando carreguei os filles (jd2xsx.dll chama & ftd2xx.dll) na pasta windowws / system32 isso resolveu os problemas. Então, tive um problema com meu fd2xx.dll mais recente relacionado a parâmetros, por isso tive que carregar a versão mais antiga dessa dll. Terei que descobrir isso mais tarde.

Observação: o jd2xsx.dll chama o ftd2xx.dll, portanto, apenas definir o caminho para o jd2xx.dll pode não funcionar.

Dan Carte
fonte
0

Estou usando Mac OS X Yosemite e Netbeans 8.02, obtive o mesmo erro e a solução simples que encontrei é como a acima, isso é útil quando você precisa incluir biblioteca nativa no projeto. Então, faça o próximo para o 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

Espero que possa ser útil para alguém. O link onde encontrei a solução está aqui: java.library.path - O que é e como usar

alexventuraio
fonte
Olá, Alex, tenho uma pergunta se há algum problema se o caminho da biblioteca incluir algum espaçamento no caminho nesta linhaVM Options: java -Djava.library.path="your_path"
Lokesh Pandey
Humm, não estou trabalhando com Java há um ano, mas como podem aparecer conflitos, recomendo evitar adicionar espaços vazios no caminho do arquivo .
alexventuraio
0

Eu tive o mesmo problema e o erro foi devido a uma renomeação da dll. Pode acontecer que o nome da biblioteca também esteja escrito em algum lugar dentro da dll. Quando coloquei de volta o nome original, consegui carregar usandoSystem.loadLibrary

Alessandro Marini
fonte
0

É simples, basta escrever java -XshowSettings: properties na linha de comando do Windows e colar todos os arquivos no caminho mostrado por java.library.path.

Ali Sasoli
fonte