Obtendo a pasta que está o arquivo .jar

16 respostas
Augusto_

Caros,

Fiz uma aplicação em Java que le um arquivo de configuração, e se ele não existe, a aplicação cria.
Depois disso, fiz um instalador com o Inno Setup, que cria um atalho para a minha aplicação no desktop.

O problema é:

O arquivo de configuração está sendo criado no desktop, e isso não é o que eu quero. Isso acontece por que eu crio o arquivo no diretório “.”, que passa a ser o Desktop quando o programa é executado a partir do atalho.

Alguém sabe me dizer como fazer o .jar gerar o arquivo de configurações dentro da pasta que está o .jar? Essa pasta é dinâmica e depende da instalação. Estou procurando algum comando que me retorne a pasta real que o .jar está mas não estou conseguindo.

Agradeço qualquer ajuda.

16 Respostas

E

Sua configuração é global (ou seja, depois da instalação não pode ser mais mexida) ou é por usuário?

Se for global, continue a verificar a história do jar.

Se for por usuário, veja o pacote java.util.prefs ( http://java.sun.com/javase/6/docs/api/java/util/prefs/package-summary.html )

EDIT - Mesmo se for global eu recomendo usar o pacote prefs. Ele salva as configurações no registry no caso do Windows, e em arquivos XML no caso do Unix/Linux.

D

por padrão, vc encontrara o .jar na pasta do projeto, dentro da pasta “dist”…
pelo menos se for pelo netbeans é lá…
vlw

Augusto_

Achei uma solução, mas não fui muito com a cara dela…

Aqui está:

public class PastaCorrente {
	public PastaCorrente() {
		try {
			String caminho = PastaCorrente.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
			caminho = caminho.substring(1, caminho.lastIndexOf('/') + 1);
			System.out.println(caminho);
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
}
E

Uma forma estúpida de obter isso é pegar, via getResource, a URL de algum recurso dentro desse JAR, e então pegar o nome do JAR a partir do objeto URL. que esse método retornou.

romarcio

Não sei que tipo de arquivo é o que está sendo criado, mas vc pode fazer algo assim:

...
         FileOutputStream out;
         PrintStream printStream;

         out = new FileOutputStream(new File(nomeDoArquivo).getAbsoluteFile(), true);
         printStream = new PrintStream(out);

         printStream.println(conteudo);

         printStream.close();

     }

O .getAbsoluteFile() que é um método da classe File vai criar seu arquivo no nivel da aplicação, ou seja, no mesmo diretório que estaria o pacote .jar de sua aplicação.

E

romarcio:

O .getAbsoluteFile() que é um método da classe File vai criar seu arquivo…

  1. getAbsoluteFile não cria arquivos
  2. Ele apenas verifica se o File corresponde a um diretório ou arquivo existente no disco, e se corresponder, expande o diretório para o path completo.

Usar new File (".").getAbsoluteFile() só vai lhe retornar o diretório corrente, que pode ser o diretório onde você iniciou o JAR se você deu um duplo clique, ou então algum outro. Portanto, não é muito confiável.

Veja: http://java.sun.com/javase/6/docs/api/java/io/File.html#getAbsoluteFile()

romarcio

entanglement:
romarcio:

O .getAbsoluteFile() que é um método da classe File vai criar seu arquivo…

  1. getAbsoluteFile não cria arquivos
  2. Ele apenas verifica se o File corresponde a um diretório ou arquivo existente no disco, e se corresponder, expande o diretório para o path completo.

Usar new File (".").getAbsoluteFile() só vai lhe retornar o diretório corrente, que pode ser o diretório onde você iniciou o JAR se você deu um duplo clique, ou então algum outro. Portanto, não é muito confiável.

Veja: http://java.sun.com/javase/6/docs/api/java/io/File.html#getAbsoluteFile()

Sim, se ele usar assim por exemplo :

File file = new File("arquivo.jar");
         String s = file.getAbsolutePath();

A string s, vai passar pra ele o local exato de onde está o arquivo .jar, então ele poderá criar seu arquivo a partir desse diretório.

Augusto_

Esse

File arquivo = new File("arquivo.jar");

if (arquivo.exists()) {
     System.out.println(arquivo.getAbsolutePath());
} else {
     System.out.println("O arquivo não existe");
}

só irá funcionar se eu estiver rodando o .jar da pasta corrente dele. Caso contrário, ele irá retornar a pasta corrente com o nome “arquivo.jar” no final.

Exemplo:
estou no desktop: java -jar arquivo.jar -> retorna c:\users\augusto\desktop\arquivo.jar
estou na minha users: java -jar desktop\arquivo.jar -> retorna o arquivo não existe

Por enquanto a única solução que realmente funciona foi aquela solução bizzara que falei no começo:

public class  PastaCorrente {  
  public PastaCorrente() {  
    try {  
       String caminho = PastaCorrente.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();  
       caminho = caminho.substring(1, caminho.lastIndexOf('/') + 1);  
       System.out.println(caminho);  
    } catch (URISyntaxException e) {  
        e.printStackTrace();  
    }  
  }  
}

Por que funciona? por que quando eu chamo PastaCorrente.class a máquina virtual do java pergunta diretamente à máquina virtual do java o nome do caminho do lugar aonde essa classe está no disco. Pra máquina virtual carregar, ela precisa saber esse endereço absoluto.

ViniGodoy

É virtualmente impossível descobrir o caminho onde está o .jar que disparou a aplicação, isso inclusive está na minha lista de coisas que odeio em Java. O java é feito justamente para você não ter esse caminho. Eu fiz um método que até funciona razoavelmente bem, e se baseia no que o Entanglement falou:

private static String findJarParentPath(File jarFile)
    {
        while (jarFile.getPath().contains(".jar"))
            jarFile = jarFile.getParentFile();

        return jarFile.getPath().substring(6);
    }

   /**
     * Return the instalation path of any class.
     * 
     * @param theClass The class to find the installation path.
     * @return The installation path of any class.
     */
    public static String getInstallPath(Class< ? > theClass)
    {
        String url = theClass.getResource(theClass.getSimpleName() + ".class").getPath();
        File dir = new File(url).getParentFile();

        if (dir.getPath().contains(".jar"))
            return findJarParentPath(dir).replace("%20", " ");

        return dir.getPath().replace("%20", " ");
    }

O uso é simples. Passe como parâmetro para o método qualquer classe que esteja dentro do pacote. Geralmente eu passo da minha classe onde está o main.

ViniGodoy

Ah sim, a solução ficou desajeitada justamente pq nosso security manager não permitia acesso ao protection domain.

Augusto_

O.o… acabei de dar uma solução que pergunta diretamente à JVM!

vou mostrar denovo:

public class PastaCorrente {
	public PastaCorrente() {
		try {
			String caminho = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
			caminho = caminho.substring(1, caminho.lastIndexOf('/') + 1);
			System.out.println(caminho);
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new PastaCorrente();
	}
}

Rodem isso e vejam que funciona.

Eu só estou perguntando se alguém sabe uma solução que não precise dessa parafernalha toda…

E

Se você puder usar o pacote java.util.prefs você nem precisa ficar com um arquivo de configuração, pode salvar as coisas no registry (no caso do Windows) ou em um arquivo XML (no caso do Linux/Unix). Isso pode ser interessante se você não quer que o pessoal fique mexendo com sua configuração.

Augusto_

vc tem algum exemplo de uso?

Augusto_

Peguei um exemplo de uso neste site: http://www.particle.kth.se/~lindsey/JavaCourse/Book/Part1/Java/Chapter10/Preferences.html

está acontecendo este erro:

13/07/2010 17:18:11 java.util.prefs.WindowsPreferences
WARNING: Could not create windows registry node Software\JavaSoft\Prefs\pasta/Corrente at root 0x80000002. Windows RegCreateKeyEx(…) returned error code 5.
13/07/2010 17:18:11 java.util.prefs.WindowsPreferences WindowsRegOpenKey1
WARNING: Trying to recreate Windows registry node Software\JavaSoft\Prefs\pasta/Corrente at root 0x80000002.
13/07/2010 17:18:11 java.util.prefs.WindowsPreferences openKey
WARNING: Could not open windows registry node Software\JavaSoft\Prefs\pasta/Corrente at root 0x80000002. Windows RegOpenKey(…) returned error code 2.

E

Isso ocorre se sua aplicação não tiver as permissões corretas para criar algumas chaves no Registry. Portanto, você só pode criar configurações de sistema se você fizer isso durante a instalação.

ViniGodoy

Augusto_:
Rodem isso e vejam que funciona.

Eu só estou perguntando se alguém sabe uma solução que não precise dessa parafernalha toda…

vou mostrar denovo:

Por acaso você leu o que escrevi no post seguinte? Sua “solução” requisita o ProtectionDomain. Aproveite que você postou novamente o código, e leia-o.
Esse método só funciona se o SecurityManager autorizar e, como eu havia ressaltado no post seguinte, o nosso não autorizava.

E, como eu falei, no Java é virtualmente impossível achar esse caminho.
Por isso é necessário essa “parafernalha” toda, que geralmente não funcionam em todos os casos.

Criado 13 de julho de 2010
Ultima resposta 13 de jul. de 2010
Respostas 16
Participantes 5