Obtendo recursos embutidos em JARs com NIO.2

Digamos que no nosso projeto temos a seguinte estrutura:

.
└── src
    ├── meu-pacote
    │   └── nio2
    │       └── MinhaClasse.java
    └── config.xml

Dentro de MinhaClasse, estou interessado em obter o conteúdo do arquivo config.xml.

Para isso:

  • obtemos a URI do recurso através do método getResource de nosso classe seguido ao método toURI
  • utilizamos o método get da classe auxiliar Paths para obter um Path a partir da URI
  • criamos uma String a partir dos bytes retornados pelo método readAllBytes da classe auxiliar Files
public class MinhaClasse {
  public static void main(String[] args) throws URISyntaxException, IOException {

    URI uriDoRecurso = MinhaClasse.class.getResource("/config.xml").toURI();
    Path pathDoRecurso = Paths.get(uriDoRecurso);
    String conteudoDoRecurso = new String(Files.readAllBytes(pathDoRecurso));
    System.out.println(conteudoDoRecurso);

  }
}

Ao executarmos o código anterior de dentro de nossa IDE, o código tem o efeito esperado. Tudo funciona!

Mas e se exportarmos para um JAR?

Digamos que nosso JAR seja chamado minha-lib.jar. Para que a nossa classe seja executada por padrão na execução do JAR, conteria a configuração Main-Class do arquivo META-INF/MANIFEST.MF.

O conteúdo de minha-lib.jar seria o seguinte:

.
├── meu-pacote
│   └── nio2
│       └── MinhaClasse.class
├── config.xml
├── META-INF
│   └── MANIFEST.MF

Poderíamos então executar o JAR com o comando:

java -jar minha-lib.jar

Teríamos como resultado a seguinte exceção:

Exception in thread "main" java.nio.file.FileSystemNotFoundException
	at com.sun.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:171)
	at com.sun.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:157)
	at java.nio.file.Paths.get(Paths.java:143)
	at meu-pacote.nio2.MinhaClasse.main(MinhaClasse.java:14)

Porque será que foi lançada a exceção FileSystemNotFoundException?

O que acontece é que um recurso que está embutido dentro de um JAR tem uma URI diferente. No nosso caso, seria parecida com: jar:file:/home/alexandre/minha-lib.jar!/config.xml

Observe que temos primeiramente o caminho até o JAR e, depois do !, o caminho do recurso que está embutido dentro do JAR.

Nossa primeira alteração no programa deve ser a detecção que temos um recurso de um JAR. Podemos fazer isso verificando se há o caracter !:

URI uriDoRecurso = MinhaClasse.class.getResource("/config.xml").toURI();
Path pathDoRecurso;
if(uriDoRecurso.toString().contains("!")) { //é de JAR
  //lógica para obter recurso do JAR...
} else {
  pathDoRecurso = Paths.get(uriDoRecurso);
}

Para obter a URI do recurso relativa à URI do JAR, devemos utilizar a ideia de sistema de arquivos do NIO.2, disponível a partir do Java 7. Um sistema de arquivos é abstraído através da classe abstrata java.nio.file.FileSystem. Por padrão, é utilizado o sistema de arquivos do sistema operacional. Porém, podemos utilizar um sistema de arquivos a partir de um JAR ou arquivo ZIP.

Os seguintes passos deverão ser tomados:

  • separar as partes da URI que são do JAR e do recurso dentro do JAR
  • criar uma URI que aponta para o JAR
  • criar um FileSystem a partir da URI do JAR
  • obter o Path do recurso relativo ao sistema de arquivos do JAR

Teremos, então, o código:

URI uriDoRecurso = MinhaClasse.class.getResource("/config.xml").toURI();
Path pathDoRecurso;
if(uriDoRecurso.toString().contains("!")) { //é de JAR
  String[] partesDaURI = uriDoRecurso.toString().split("!");
  URI uriDoJAR = URI.create(partesDaURI[0]);
  FileSystem fs = FileSystems.newFileSystem(uriDoJAR, new HashMap<String, String>());
  pathDoRecurso = fs.getPath(partesDaURI[1]);
} else {
  pathDoRecurso = Paths.get(uriDoRecurso);
}
Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s