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étodotoURI
- utilizamos o método
get
da classe auxiliarPaths
para obter umPath
a partir da URI - criamos uma String a partir dos bytes retornados pelo método
readAllBytes
da classe auxiliarFiles
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); }