Esse artigo faz parte da revista Java Magazine edição 09. Clique aqui para ler todos os artigos desta edição

Atenção: por essa edição ser muito antiga não há arquivo PDF para download.Os artigos dessa edição estão disponíveis somente através do formato HTML. 

 

Tira-dúvidas

Como construir pacotes JAR e WAR

Uma das maiores dificuldades dos iniciantes na plataforma Java é construir corretamente os pacotes de suas aplicações para que sua execução ou deployment seja realizado com sucesso. Neste tira-dúvidas, são apresentados detalhadamente três dos principais pacotes Java.

Java é bem diferente de outras tecnologias populares no mercado, pela forma como aplicações são empacotadas para distribuição. Ao contrário dos ambientes focados no desenvolvimento de aplicações nativas para algum sistema operacional, não existe em Java o conceito de programa. Existem apenas classes independentes que interagem entre si – estas classes podem ser substituídas a qualquer momento, sem a necessidade de recompilação.

Empacotar uma aplicação Java é, portanto, um processo bastante diferente de instalar uma aplicação nativa. Especialmente no desenvolvimento J2EE, é importante entender como esse processo funciona, para evitar surpresas.

Programas, executáveis e pacotes

Linguagens de programação tradicionais estão intimamente ligadas ao conceito de programa. Por exemplo:

·         Em Pascal temos a declaração program, que referencia uma ou mais units;

·         Em C/C++ realizamos a linkedição de vários arquivos-objeto, derivados da compilação de arquivos .c ou .cpp, mas apenas um deles pode conter a função main que inicia o programa; Além disso, todas funções definidas em outros arquivos-objeto devem ser indicadas por declarações extern, geralmente reunidas em arquivos de cabeçalho (.h);

·         Em Visual Basic temos um projeto que determina o formulário principal ou a sub-rotina principal para o executável resultante da compilação do projeto.

Em qualquer dessas linguagens, o resultado final da compilação e/ou linkedição é um único arquivo executável, um .exe no Windows. Este executável é basicamente uma imagem de memória do programa, carregada pelo sistema operacional e entregue para execução direta pelo processador. Mesmo quando são utilizadas bibliotecas dinâmicas (.dll no Windows) ou bibliotecas compartilhadas (.so no Linux e Unix), o conceito é mantido, pois o efeito desses arquivos é compartilhar uma região de código na memória entre vários processos/programas.

Sabemos que Java não realiza a etapa de linkedição para gerar um executável – apenas compila cada arquivo-fonte em arquivos de classe (.class). Sabemos também que, ao executar aplicações Java stand-alone, devemos indicar para a máquina virtual uma classe que contenha o método main; essa classe pode estar em qualquer diretório ou pacote indicado no classpath (na verdade, podemos ter várias classes com métodos main no classpath, sem que haja conflitos).

Uma vez que a classe desejada seja carregada pela JVM, outras são carregadas à medida que forem necessárias, não havendo nenhum vínculo rígido entre elas como o que existe entre as funções e bibliotecas inclusas em um executável. Podemos, a qualquer momento (inclusive depois de iniciado o programa!), acrescentar classes ao classpath, substituir classes já presentes (desde que não tenham sido ainda carregadas), ou mesmo obter novas classes pela rede, como é feito em Applets e em aplicações RMI.

Mas, e quanto aos pacotes da plataforma Java, não seriam análogos aos arquivos .exe e .dll? Não, os vários tipos de pacotes são apenas agrupamentos de arquivos .class e outros arquivos auxiliares (como .properties e .gif), para comodidade na distribuição de um aplicativo, ou para organizar logicamente arquivos relacionados. Na maioria dos casos é indiferente ter esses arquivos abertos em um diretório ou empacotados em um .jar. Os pacotes podem ser remontados pelo acréscimo, remoção ou substituição de arquivos pelo desenvolvedor ou usuário.

Essa é uma das grandes vantagens de Java: as aplicações deixam de ser "monolitos" rígidos, transformando-se em conjuntos modulares que podem ser montados e remontados em combinações diferentes para melhor atender às necessidades particulares de cada usuário.

Classes de exemplo

No decorrer deste artigo, serão construídos vários exemplos baseados em duas classes utilitárias, contendo métodos para formatação de texto. A classe FormataTexto garante que tenhamos iniciais maiúsculas em frases e nomes, enquanto que a classe FormataNumero insere um hífen no local correto em números de telefone com sete ou oito dígitos.

Classes utilitárias desse tipo são bastante utilizadas para a formatação de dados em relatórios ou em telas de entradas de dados, mas o objetivo aqui, claro, não é fornecer um framework robusto de formatação, mas sim exemplificar várias situações de construção de pacotes para bibliotecas e aplicações Java. Da mesma forma, os exemplos apresentados serão sempre os mais simples possíveis, pois o objetivo é entender como montar pacotes e não como construir as aplicações em si.

JARs

Um pacote JAR nada mais é do que um arquivo no formato ZIP, agrupando arquivos de classes e de propriedades, imagens e outros recursos utilizados pelas classes – os arquivos no JAR podem ou não estar compactados. Como tal, podemos criar, examinar e alterar pacotes JAR utilizando qualquer utilitário compatível com o formato ZIP, por exemplo os softwares livres Info-ZIP, e 7-Zip ou os populares sharewares WinZip e FilZip. Já que nenhum deles é padrão em todos os sistemas operacionais suportados por Java, o JDK fornece o utilitário jar, que será utilizado neste artigo.

As primeiras versões de Java utilizavam pacotes ZIP sem mudar a extensão (os leitores mais “dinossauros” certamente se lembram do pacote classes.zip que fornecia as APIs do Java 1.1 e 1.0.2). A única diferença entre um pacote JAR e um ZIP é que o primeiro contém, obrigatoriamente, um arquivo chamado META-INF/MANIFEST.MF (ao qual chamaremos apenas de “manifest” daqui em diante). O comando jar fornecido pelo JRE gera este arquivo com um conteúdo padrão, caso uma versão customizada não seja fornecida.

O manifest é um arquivo-texto semelhante a um arquivo de propriedades Java, contendo entradas na forma “chave: valor”, uma por linha. Sua finalidade é fornecer informações complementares para a JVM ou para o container responsável pela carga e execução do pacote – por exemplo IDEs visuais ou servidores de aplicações. No decorrer deste texto, serão vistas algumas das informações que podem ser inseridas dentro de um manifest.

As classes inclusas em um pacote JAR não são carregadas em bloco, mas sim individualmente, conforme a demanda, da mesma forma que seriam caso estivessem em arquivos .class individuais no sistema de arquivos. Por essa e outras razões, a estrutura de diretórios dentro do pacote deve obrigatoriamente refletir a estrutura de packages Java.

Por exemplo, o pacote jmutil.jar contendo as classes javamagazine.util.FormataTexto e javamagazine.util.FormataNumero deve ter a estrutura apresentada na Figura 1.

Como gerar este pacote? Digamos que os fontes das duas classes estejam no diretório C:\projetos\jmutil\src e que os arquivos de classe estejam em C:\projetos\jmutil\bin, estrutura muito comum em projetos criados por IDEs como o JBuilder e o Eclipse, ou cuja compilação seja direcionada pelo Ant. Usuários de Linux podem seguir os mesmos passos, substituindo os nomes dos diretórios por /home/usuario/jmutil/src e /home/usuario/jmutil/bin. A Figura 2 exibe a estrutura proposta para o projeto.

Observe que a estrutura do pacote jmutil.jar é a mesma do subdiretório bin do projeto, o que não é coincidência: os arquivos-fonte e os arquivos de classes foram colocados em diretórios separados para simplificar as atividades que atuam apenas sobre os fontes (backup, controle de versões) e as atividades que atuam apenas sobre os .class (empacotamento, distribuição).

Para compilar as classes, é necessária uma linha de comando bastante longa:

 

C:\> cd \projetos\jmutil\src

C:\projetos\jmutil\src> javac -d ..\bin javamagazine\util\*.java

 

Então podemos criar o pacote entrando no subdiretório bin e executando o comando jar com a opção c:

 

C:\projetos\jmutil\src> cd ..\bin

C:\projetos\jmutil\bin> jar cvf ..\jmutil.jar *

 

Usuários Linux necessitam apenas mudar os nomes dos diretórios e trocar as barras invertidas por barras de divisão, por exemplo:

 

localhost:~$ cd projetos/jmutil/bin

localhost:~/jmutil/bin$ jar cvf ../jmutil.jar *

Em ambos os casos, o resultado final é a criação do pacote jmutil.jar no diretório projetos/jmutil. Podemos verificar se o conteúdo do pacote está correto com o comando:

 

C:\projetos\jmutil\bin> jar tf ..\jmutil.jar

 

Este comando lista toda a estrutura de arquivos e diretórios dentro do pacote. Para mais informações sobre a sintaxe utilizada, veja o quadro "O comando jar"

Outra maneira de compilar as classes e gerar o pacote é utilizar o Ant, que possui uma tarefa (task) especializada na geração de JARs. O arquivo build.xml para o exemplo é:

 

<?xml version="1.0" encoding="iso-8859-1" ?>

 

<project name="jmutils" default="all">

  <target name="all" depends="compile,jar">

  </target>

 

  <target name="init">

    <property name="src" value="./src" />

    <property name="bin" value="./bin" />

    <property name="jar" value="./jmutil.jar" />

  </target>

 

  <target name="clean" depends="init">

    <delete file="${jar}" />

    <delete dir="${bin}" />

    <mkdir dir="${bin}" />

  </target>

 

  <target name="compile" depends="clean">

    <javac srcdir="${src}" destdir="${bin}" />

  </target>

 

  <target name="jar" depends="init">

    <jar destfile="${jar}" basedir="${bin}" />

  </target>

</project>

 

Então podemos digitar o comando a seguir para gerar o pacote (observe o diretório corrente indicado pelo prompt de comandos):

 

C:\projetos\jmutil> ant jar

 

Ou então podemos digitar o comando seguinte para compilar todas as classes e gerar o pacote de uma só vez, visto que o alvo (target) padrão (definido no elemento project do buildfile) é all:

 

C:\projetos\jmutil> ant

 

É bem mais produtivo gastar algum tempo para gerar um buildfile correto do que digitar sempre os mesmos e longos comandos javac e jar para compilar e empacotar um projeto. Criar arquivos batch do DOS ou scripts shell do Linux seria uma alternativa, mas eles não são portáveis, ao contrário de buildfiles do Ant (para informações sobre a instalação do Ant e a sintaxe dos seus buildfiles veja a Edição 2).

Estando pronto o pacote da nossa biblioteca de utilitários, podemos executar as rotinas de teste inclusas em cada classe. Experimente:

 

C:\projetos\jmutil> java -cp jmutil.jar javamagazine.util.FormataNumero
123-4567

1234-4567

C:\projetos\jmutil> java -cp jmutil.jar javamagazine.util.FormataTexto
Fernando

Fernando lozano

Fernando
Fernando Lozano

 

A opção -cp do comando java permite especificar um novo classpath para a execução da máquina virtual, em substituição ao fornecido pelas variáveis de ambiente do sistema operacional. Entretanto, não criamos o pacote jmutil.jar para rodar as classes contidas nele, e sim para reutilizar essas classes em outras aplicações, ou seja, nosso objetivo foi montar um pacote de biblioteca. Vamos agora falar sobre como empacotar aplicações em um JAR.

 

O comando jar

Apesar de seguir o formato de arquivo criado pelo PkZIP, o utilitário jar fornecido com o JDK segue a sintaxe do utilitário tar do Unix, que manipula um formato de arquivo totalmente diferente. Há quatro modos de operação principais e uma série de modificadores que podem ser utilizados com qualquer dos modos. As opções correspondentes aos modos de operação são:

...

Quer ler esse conteúdo completo? Tenha acesso completo