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
getResourcede nosso classe seguido ao métodotoURI - utilizamos o método
getda classe auxiliarPathspara obter umPatha partir da URI - criamos uma String a partir dos bytes retornados pelo método
readAllBytesda 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
FileSystema partir da URI do JAR - obter o
Pathdo 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);
}
Obrigado por compartilhar esse conhecimento. Foi muito útil para mim. Obrigado!