Erro ao executar o arquivo Jar fora da IDE

13 respostas
U

Bom dia a todos

Este é a minha primeira participação neste forum e desejo, antes de mais nada, poder ser utíl também a todos desta grande comunidade.

Sou iniciante em Java e estou com um problema um pouco dificil de resolver. Estou utilizando o NetBeans 5.5, MySql, e Linux. Para realizar a conexão com o MySql estou utilizando o mysql-connector-java-3.2.0-alpha-bin.jar e por meio do NetBeans eu associei este connector à aplicação, fazendo com que o NetBeans adicionasse este connector ao classPath. Quando executo a aplicação pela IDE funciona tudo correto mas quando eu mando gerar o arquivo jar a aplicação trava na parte onde tenho de ler o driver do mysql. Quando digo trava quer dizer isto mesmo...não dá nenhuma mensagem de erro (apesar de em minhas classes, quando ocorrer algum erro eu estou gerando uma exceção para transmitir esta exceção para um bloco superior que mostraria a mensagem. Vou postar parte do código para ficar mais claro.

package brc.database;


/**
 * @author Ubiraci Lage Brandão Júnior ([email removido])
 * @version 1.0.0 - 03/08/2007
 */
public final class LoadDriver {
    
    public static boolean MySql() throws Exception {
        try {
            //Class.forName("com.mysql.jdbc.Driver");
            Class.forName("org.gjt.mm.mysql.Driver");
            return true;
        }
        catch(Exception e) {
            throw new Exception("Não foi possível carregar o driver do MySql");
        }
    }
    
}

Esta classe acima faz parte de um framework que estou escrevendo para automatizar a criação de aplicações. Esta classe acima é quem faz a leitura do driver do mysql.

package brc.database;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @author Ubiraci Lage Brandão Júnior ([email removido])
 * @version 1.0.0 - 03/08/2007
 */
public final class LoadConnection {
    
    public static Connection MySql(String hostIP, String port, String dataBase) throws Exception {
        User user = new User(enmDatabase.enmTypeDatabase.MySql);
        
        String strConn = "jdbc:mysql://" + hostIP;
        
        if (port != "") {
            strConn += ":" + port + "/";
        }
        
        strConn +=  dataBase;
        
        try {
            return DriverManager.getConnection(strConn, user.get_User(), user.get_Password());
        }
        catch(SQLException sqle) {
            throw new Exception("Nao foi possivel conectar à base de dados");
        }
    }
}

Esta outra classe retorna para mim o objeto connection. A classe user é apenas uma classe que retorna o nome e senha do usuário do banco. Como pode ser visto, as duas classes acima estão com o código protegido por try..catch e caso ocorra algum erro, este erro é propagado acima com uso da instrução throw.

Tenho uma classe que gerencia todos os bancos de dados que minha aplicação irá trabalhar. Abaixo está o constructor da classe:

public DatabaseConnection(confManager configurator) throws Exception {
        this.config = configurator;
        
        this.typeDatabase = this.getTypeDatabase(this.config.getprops().getProperty("typeDatabase"));
        
        switch(this.typeDatabase) {
            case MySql: {
                try {
                    this.db = new MySql(this.config.getprops().getProperty("hostIP"),
                        this.config.getprops().getProperty("port"),
                        this.config.getprops().getProperty("dataBase"));
                }
                catch(Exception e) {
                    throw new Exception(e.getMessage());
                }
                break;
            }
            case Interbase: {
                break;
            }
        }
    }

O objeto config é uma classe que lê um arquivo de configuração (.conf) onde estão as informações do hostIP, port, Database. O objeto db é de um tipo de classe Base que serve como interface para todos os bancos de dados. A classe MySql extende a classe Base. Como dá pra ver no código, a criação da classe MySql também está protegida. Se ocorrer uma exceção, ela é propagada para algum bloco superior. Abaixo eu mostro o constructor da classe MySql:

public MySql(String hostIP, String port, String dataBase) throws Exception {
        super(hostIP, port, dataBase);
        
        try {
            LoadDriver.MySql(); // Lê o driver do MySql
        }
        catch(Exception e) {
            this.saveLog(e);
            throw new Exception(e.getMessage());
        }
        
        try {
            this.setConn(LoadConnection.MySql(hostIP, port, dataBase));
        }
        catch(Exception e) {
            this.saveLog(e);
            throw new Exception(e.getMessage());
        }
    }

Como pode ser visto, se ocorrer uma exceção ao ler o driver do MySql eu gero uma execeção e caso tenha passado sem erros na leitura do driver eu tento criar o objeto de conexão. Se ocorrer um erro também neste ponto, eu gero novamente um erro. Estas classes estão no framework. Agora abaixo eu vou colocar o constructor de uma classe de um aplicativo que eu estou criando utilizando o framework.

public Global() throws Exception {
        applicationPath = this.getApplicationPath();
        CONFIGURATOR_FILENAME = System.getProperty("user.home") + "/jtm.conf";
        
        File f = new File(CONFIGURATOR_FILENAME);
        
        if (f.exists()) {
            config = new confManager(CONFIGURATOR_FILENAME);
            
            try {
                db = new DatabaseConnection(config);
            }
            catch (Exception ex) {
                throw new Exception("O arquivo de configuração não contém dados para realizar a conexão ao banco de dados. Favor verificar!");
            }
        }
        else {
            throw new Exception("Não foi possível encontrar o arquivo de configuração (jtm.conf). Favor verificar!");
        }
    }

Como pode ser visto, o objeto db é criado em um bloco protegido; caso ocorra alguns dos erros gerados no framework, este bloco é que irá receber e tratar a exceção, mostrando uma mensagem ao usuário. A criação desta classe é feita dentro da tela de Splash. Existe um método que é chamado para realizar as inicializações da aplicação. Esta classe (Global) tem um constructor pois eu utilizo um método para ler o diretorio atual do arquivo jar e este método não funciona se for estático. Mas eu chamo o construtor apenas 1 vez e armazeno o retorno do método numa variável estática. Todos os fields desta classe são estáticos. Abaixo eu coloco o método que cria a classe Global:

public void userInitialization() {
        try {
            Global glb = new Global();
        }
        catch(Exception ex) {
            screen.setVisible(false);
            screen.dispose();
            msg.Error(ex.getMessage());
            System.exit(0);
        }
        //screen.setProgress("Yo " + i, i); // ProgressBar com mensagem
        screen.setProgress(1); // ProgressBar sem mensagem
    }

Como pode ser visto, se ocorrer um erro ao criar a classe global a tela splash é escondida (screen.setVisible(false)), depois é retirada da memória (screen.dispose), é mostrada a mensagem de erro e depois sai do programa.

O problema é este: funciona tudo correto quando estou dentro da IDE mas quando executo de fora não funciona...acontece algum erro e a tela splash fica parada no meio do desktop e não consigo nem sequer retirar esta tela. Só fazendo logoff no sistema. Sei que o erro acontece ao ler o driver do MySql pois executei uma vez colocando mensagens (JOptionPane) em cada ponto (antes e depois de passar pelo ponto que estava sendo testado). E vi que o erro ocorreu quando chega na parte de ler o driver do MySql (Class.for....) só que está funcionando se executo pela IDE.

Analisando os arquivos gerados na pasta dist, eu verifiquei que existe o arquivo jar, um arquivo txt gerado automaticamente pelo NetBeans e uma pasta lib onde dentro tem o connector do MySql e o jar do meu framework.

Onde foi que eu errei? Será que alguém pode me dar uma luz sobre este prolema? Sinceramente eu não sei como resolve-lo!!!

Fico no aguardo e desde já agradeço as respostas e a paciencia por terem lido um post tão grande.

13 Respostas

Z

Verifique se no arquivo JAR da sua aplicação contém as classes do driver JDBC do MySQL (e não o arquivo JAR do driver!).

Uma sugestão é usar o Log4J para controlar as saídas de erros (inclusive mensagens de debug) da sua aplicação, seja em um arquivo, enviando um email ou guardando-as em um banco de dados.

Procure nos tutoriais aqui do GUJ.

U

Olá zé_kiefa…obrigado por me responder…

desculpe a pergunta (como disse ainda sou iniciante em java)…como eu vejo se os arquivos JDBC do MySql estão dentro do jar? Eu abri o arquivo jar com o aplicativo Ark (compactador de arquivos) e consegui verificar que está dentro do jar somente as classes da minha aplicação. Olhei dentro do jar do framework e não tem também. Quais são as classes do MySql no JDBC ou onde posso encontrar estas classes?

Z

Nas configurações do seu projeto, verifique se a biblioteca do MySQL (driver JDBC) está configurada para ser exportada junto com o seu projeto.
Dessa maneira, o JAR de sua aplicação será gerado corretamente com todas as classes (as suas e das bibliotecas que acompanham o seu projeto).

OK?

U

ze_kiefa:
Nas configurações do seu projeto, verifique se a biblioteca do MySQL (driver JDBC) está configurada para ser exportada junto com o seu projeto.
Dessa maneira, o JAR de sua aplicação será gerado corretamente com todas as classes (as suas e das bibliotecas que acompanham o seu projeto).

OK?

Eu tirei 2 screenshots da tela de propriedades do NetBeans com as configurações atuais. Como dá pra ver nas abas compilar e executar estão configuradas o jar do connector. As classes do JDBC, pelo que eu entendi estão dentro do jar do connector (classes JDBC para MySql). O NetBeans ao gerar o jar da minha aplicação não coloca estas classes dentro do meu jar. Ao inves disso ele cria uma pasta chamada lib e coloca lá dentro todos os jar de terceiros (como o caso do connector do MySql)…quando a aplicação tenta acessar o banco de dados, o java tenta ler estas classes de dentro do jar do connector e não do jar da minha aplicação. Estou correto?

Se for isto que acontece, já está configurado desta forma só que não funciona. Vou mostrar como está o arquivo manisfest gerado pelo NetBeans (consegui o arquivo, abrindo o jar pelo compactador de arquivos, abrindo num editor de texto e copiando o conteúdo para a area de transferencia):

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.5
Created-By: 1.6.0-b105 (Sun Microsystems Inc.)
Main-Class: jtm.Main
Class-Path: lib/brclibrary.jar lib/mysql-connector-java-3.2.0-alpha-bi
 n.jar
X-COMMENT: Main-Class will be added automatically by build

Como dá pra ver, no Manifest.MF, o connector do java assim como o jar do meu framework estão referenciados no class-path da aplicação e pelo que eu entendi as classes JDBC do MySql estão dentro do connector. Então porque ao executar o programa não consigo executar se o start da aplicação não se dá atraves da IDE?




Z

Teoricamente está correto.
Eu não uso Netbeans (uso Eclipse) e tos meus projetos são WEB (o que difere bastante neste ponto).

Um projeto que eu fiz no JDeveloper, tinham configurações específicas na criação do JAR.
Creio que no Netbeans não seja diferente!

Procure alguma opção de deploy nas propriedades do seu projeto. Como o seu projeto é Desktop, o Netbeans trará para você as opções para gerar um arquivo JAR.

U

ze_kiefa:
Teoricamente está correto.
Eu não uso Netbeans (uso Eclipse) e tos meus projetos são WEB (o que difere bastante neste ponto).

Um projeto que eu fiz no JDeveloper, tinham configurações específicas na criação do JAR.
Creio que no Netbeans não seja diferente!

Procure alguma opção de deploy nas propriedades do seu projeto. Como o seu projeto é Desktop, o Netbeans trará para você as opções para gerar um arquivo JAR.

Olá ze_kiefa…

primeiramente gostaria de agradecer a sua ajuda…está sendo muito importante para mim…entendo que você utilizando o eclipse deve haver diferenças entre uma IDE e outra. Eu procurei ver todas as opções do menu de propriedades do pacote mas não encontrei nada significativo que me indicasse se tratar da inclusão de alguma classe relacionada ao JDBC. Pode ser que outro membro do forum que esteja mais acostumado com o NetBeans, até já tenha passado pelo mesmo problema.

Enquanto não consigo fazer funcionar o programa fora da IDE, ainda consigo realizar sua implementação pois, dentro da IDE eu consigo acessar o MySql. Somente na hora de instalar em um cliente, é que eu não vou conseguir fazer funcionar ou seja, ainda tenho algum tempo para pesquisar ou pedir ajuda a outros mais experientes do que eu (o sistema está relativamente no começo).

Esqueci de dizer que, quando falo em clientes, não me refiro a clientes pagantes pois, este meu primeiro programa em Java eu gostaria de contribuir com a comunidade do software livre. Este programa que estou fazendo é um software que gerencia pendencias ou listas de tarefas. Ideal para utilização em equipes de desenvolvimento de sistemas onde o desenvolvedor pode gerenciar as pendencias que estão em aberto, as que ele já concluiu e as que ele cancelou.

Vou procurar pesquisar mais e verificar as configurações do NetBeans para ver se é somente um caso de configuração. Se você tiver mais dicas do que posso fazer, vai ser de grande ajuda. Também peço a ajuda dos outros membros do forum para que possam dar um pouco de luz neste problema.

Agradeço novamente e fico no aguardo de uma dica ou sugestão para resolver este problema. Enquanto isto, eu também vou continuar a fazer pesquisas e encontrando alguma resposta ou caminho para chegar nela, postarei aqui a resposta para servir de material de consulta a outros que por ventura tiverem o mesmo problema.

Um abraço.

R

Como você está fazendo a chamada para sua aplicação? Linha de comando? Você está executando isso na raiz do projeto? Você está criando um jar do seu projeto com o mainifest dentro? e a pasta lib/ existe a partir do local de onde você está executando a aplicação?

U

Olá rmain…

obrigado por tentar me ajudar tambem…desculpe por não ter respondido logo o post pois fiquei o dia todo fora de casa e só agora que consegui entrar na net…

É o seguinte: o arquivo manifest está dentro do jar…já publiquei o conteúdo do manifest a alguns posts acima…o arquivo jar está dentro da pasta dist. A estrutura está assim:

-dist
    jtm.jar
    README.txt
    -lib
      brclibrary.jar
      mysql-connector-java-3.2.0-alpha-bin.jar

Não estou chamando em linha de comando. Estou dando um duplo clique no arquivo jar. Os arquivos jar estão associados no meu sistema operacional com o Java (estou conseguindo rodar outros arquivos jar desta forma).

A única biblioteca de terceiros que estou usando é a do connector do mysql pois a brclibrary foi eu quem fiz. Na verdade, quando dou um duplo clique no jar a JVM começa a execução do programa…chega a mostrar a tela de splash e atraves de mensagens colocadas no fonte eu detectei que a JVM inicializa todas as variáveis e classes do programa corretamente. O problema ocorre quando ele tenta carregar na memória o driver do mysql…aí trava e fica parado na tela splash…não consigo matar o aplicativo pois neste ponto (na tela splash) ainda não aparece no gerenciador de tarefas…então remover esta tela (splash) e limpar a memória ocupada por esta tela estou tendo de finalizar a seção e iniciar novamente.

Quando eu executo o mesmo programa sem mudar nenhuma linha de programação, mas dentro da IDE do NetBeans, este erro não ocorre…o programa consegue ler o driver do mysql…cria a connexão…e depois o form principal da aplicação…ou seja, não trava.

A minha questão é justamente esta: Porque quando executo dentro da IDE não dá erro e fora dá? Enquanto estiver dando erro fora da IDE eu também estou impossibilitado de distribuir a aplicação. Imagino que como o NetBenas é uma IDE madura já com vários anos na estrada (já está indo para a versão 6)…muitas pessoas já devem ter passado por isso, ou será que este caso só ocorreu comigo?

Se puder me dar uma ajuda eu fico muito agradecido…

Um abraço

R

Fala Ubiraci Júnior!

Você está executando assim?

Tudo indica que o mysql-connector-java-3.2.0-alpha-bin.jar não está correto no MANIFEST.MF. Mas pelo que você postou aqui, ele está correto.

Tenta fazer o seguinte, passe o classpath na mão para a linha de comando que está tentando executar, se funcionar, era porque o Class-Path do manifest estava errado.

Faça assim:

// no linux o separador é ':' java -cp lib/brclibrary.jar;lib/mysql-connector-java-3.2.0-alpha-bin.jar -jar jtm.jar

Isso dentro do diretório dist. :stuck_out_tongue:

Pela hierarquia de diretórios pude perceber que você não usa um log. Seria interessante você configurar um para a sua aplicação. Use o log4j. Desta forma você consegue ver o stack trace dos erros e tudo mais… :thumbup:

U

rmarin:
Fala Ubiraci Júnior!

Você está executando assim?

Tudo indica que o mysql-connector-java-3.2.0-alpha-bin.jar não está correto no MANIFEST.MF. Mas pelo que você postou aqui, ele está correto.

Tenta fazer o seguinte, passe o classpath na mão para a linha de comando que está tentando executar, se funcionar, era porque o Class-Path do manifest estava errado.

Faça assim:

// no linux o separador é ':' java -cp lib/brclibrary.jar;lib/mysql-connector-java-3.2.0-alpha-bin.jar -jar jtm.jar

Isso dentro do diretório dist. :stuck_out_tongue:

Pela hierarquia de diretórios pude perceber que você não usa um log. Seria interessante você configurar um para a sua aplicação. Use o log4j. Desta forma você consegue ver o stack trace dos erros e tudo mais… :thumbup:


Olá rmarin…

olha…com o comando que você passou funcionou…quero dizer, chamando diretamente pelo terminal com o comando:

// no linux o separador é ':' java -cp lib/brclibrary.jar;lib/mysql-connector-java-3.2.0-alpha-bin.jar -jar jtm.jar

Eu estava executando com clique duplo…abria ele pelo gerenciador de arquivos (Konkeror no linux) e dava um duplo clique nele. Imaginei que fosse automatico…todo arquivo jar que eu colocasse na pasta lib e referenciasse na aplicação, ao gerar o jar, ele colocaria no classPath. Tanto que quando eu abri o Manifest.mf num arquivo de texto, o class path estava correto mas, pelo que eu consegui entender executando a aplicação com o comando que você me passou, é como se o jar principal da aplicação não tivesse o classPath configurado, mesmo estando ele no arquivo de manifest.

Claro que desta forma já clariou bastante as idéias…dá pra eu criar um atalho com este comando para a pasta de instalação e o usuário vai poder abrir com um clique duplo, mas, sem querer abusar de você, gostaria que você pudesse me esclarecer o porque da forma como eu estava fazendo antes não funcionava.

Sei que o manifest estava correto…só se ao clicar duplo no jar, por algum motivo, o sistema operacional estava tentando executar somente as classes que foram feitas com a mesma IDE (pois, antes de travar procurando o driver do mysql, a execução passava por várias classes definidas na brclibrary.jar - também criada com o NetBeans). Não sei o porque…se você ou outra pessoa poder me exclarecer sobre isto, seria melhor pois, eu não necessitaria criar um atalho para o programa e sim, poderia clicar diretamente sobre ele.

Quanto ao log4, se você puder me falar somente um pouco sobre o que é ele eu agradeceria…entrei no site da URL que você enviou mas meu inglês não é bom (ainda estou aprendendo)…se você puder me passar referencias em português sobre este log4 eu agradeceria.

Novamente muito obrigado por estar me ajudando…fico no aguardo

P

Parem de sofrer:

http://fjep.sourceforge.net/fjeptutorial.html

Esse plugin empacota toda sua aplicação em um jar, imagine se você estiver usando mais de 10 jars e ter que que definir 1 por 1.

R

É uma saída, principalmente para aplicações pequenas. :smiley:

Apesar que eu particularmente não gosto de “jars gordões”. :stuck_out_tongue:

U

rmarin:
É uma saída, principalmente para aplicações pequenas. :smiley:

Apesar que eu particularmente não gosto de “jars gordões”. :stuck_out_tongue:


É isso aí…

em parte eu concordo com o rmarin pois, para uma aplicação grande, fica mais fácil a pessoa atualizar somente 1 jar do que todos os jars (empacotado em apenas 1 jar)…mas por outro lado o Pedrosa deu uma boa idéia de empacotar tudo num só…é uma questão apenas de encontrar o tipo de aplicação que melhor serve a 1 ou a outro tipo.

Com relação ao problema que eu tinha desde o começo posso dar como resolvido…o problema era classPath, embora eu ainda não saiba porque se eu excutasse com duplo clique a aplicação não funcionava mesmo estando o arquivo manifest correto.

Com relação ao plugin que o Pedrosa falou…vou pesquisar um pouco sobre como ele funciona e ver se o tipo de aplicação que estou criando se encaixa em apenas um jar.

Quero agradecer a todos por terem tirado um pouco do seu tempo para ajudar a um programador Java novato. Gostaria de dizer que, quando eu tiver a oportunidade, gostaria de poder ajudar mais ativamente no forum e/ou com produção de material ou software livre.

Um abraço a todos e novamente muito obrigado!!!

Criado 18 de setembro de 2007
Ultima resposta 20 de set. de 2007
Respostas 13
Participantes 4