Introdução
Pré-requisitos
Motivando o uso do Maven
Uma introdução ao Maven
o Criando os usuários que irão trabalhar com o Maven
o Instalando o Maven
o Gerando um projeto de negócios
o Compilando, testando, instalando e implantando artefatos
o Gerando um projeto Web que utiliza o framework Wicket
o Compreendendo alguns detalhes no pom.xml do projeto Web
o Executando a aplicação Web
o Vendo a árvore de dependências da aplicação Web
Uma introdução ao Nexus
o Motivando o uso do Nexus
o Instalando o Nexus
o Alterando as configurações do Maven para que ele use o Nexus
Alterando o projeto web gerado inicialmente
Alterando o projeto negócios gerado inicialmente
Efetuando o deploy do projeto de negócios
Finalizando a construção do projeto Web
Extras
Introdução
No momento da escrita deste tutorial, o Lado Servidor esteve responsável por refazer a arquitetura de uma
aplicação em um de seus clientes. Tal aplicação é composta por módulos de negócio empacotados em JARs e
por uma camada de apresentação Web. A camada Web é produzida por uma equipe, que chamararemos de
equipe web, e consome serviços implementados na camada de negócio por outro time, que compõe uma equipe
de negócios.
O trabalho de alteração da arquitetura deveria ser simples e rápido. Contudo, alguns problemas recorrentes
(vivenciados pelo Lado Servidor em outros clientes) apareceram:
A ocorrência dos problemas acima motivou o Lado Servidor a escrever este tutorial e apresentar o Maven e
o Nexus como solução para vários deles. E, atuando em parceria com umSCM, como o Subversion por
exemplo, todos podem ser resolvidos.
O propósito deste tutorial é o de introduzir como estas ferramentas podem trabalhar em conjunto para solucionar
os problemas apresentados. Nele é abordado a instalação do Maven, do Nexus e do Subversion, e a simulação
do trabalho de desenvolvedores das equipes (web e negócios) para gerar e publicar snapshots e releases
(conceitos que serão explicados aqui). Sua estrutura foi organizada para que as tarefas possam ser realizadas
(passo a passo) apenas copiando e colando os comandos apresentados nas caixas que apresentam como
executá-los.
Para fins didáticos, este tutorial também foi dividido em três partes:
Pré-Requisitos
Neste tutorial não é esperado conhecimento anterior no uso das ferramentas que serão apresentadas.
Entretanto, ele foi concebido e testado num ambiente Linux Ubuntu 10.04, na plataforma x86-32. Sendo assim,
é necessário algum conhecimento básico de Linux para tarefas relativas a instalação/configuração.
Pelo fato dos comandos executados no Maven serem multi-plataforma, as tarefas relativas ao uso destas
ferramentas poderão ser executadas por quaisquer usuários que não tenham experiência anterior em Linux. Até
mesmo pelo uso de outro sistema operacional como o Windows ou o MAC OS. Caso seja utilizando o Windows,
recomendamos fortemente o uso doCygwin para a execução das tarefas deste tutorial.
Copie do início da palavra sudo até o apóstrofo (') da última linha. A seguir, cole em seu shell:
Instalando O Maven
Agora vamos baixar e instalar o Maven 3.0. Ele será instalado no diretório /opt pois será compartilhado pelos
usuarios negocios e web:
Para que o Maven possa funcionar na conta dos usuários criados, o arquivo ~/.bashrc de cada um deles
precisará indicar a localização de $JAVA_HOME. Também precisaremos colocar seus binários no PATH do
usuário. A execução em sequência dos dois comandos a seguir configura o arquivo ~/.bashrc, para cada um
destes usuários:
Agora, podemos testar a execução do Maven fazendo o login com a conta de qualquer um destes usuários. Os
comandos a seguir solicitam o login como o usuário web e, em seguida, a impressão da versão do Maven. O
exit (terceiro comando) solicita o logout do usuário:
$ sudo su - web
$ mvn -v
$ exit
O projeto conterá métodos de negócios utilizados por uma aplicação Web. Logo, vamos nos logar como o
usuário negocios que será responsável pela codificação deste projeto. Os comandos a seguir, efetuam o
logon deste usuário e solicitam a criação do projeto:
$ sudo su - negocios
$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
Observe atentamente (pela saída do comando) que o Maven realiza o download de vários JARs para realizar
esta tarefa. Os arquivos baixados são instalados num repositório local (~/.m2/repository) e o motivo para
o download de tantos arquivos é explicado um pouco mais a frente. Se outro desenvolvedor da corporação
também precisar utilizar o Maven, o mesmo processo será repetido.
Se por acaso você estiver executando este tutorial numa rede em que é necessária a
configuração de um proxy para o acesso a Internet, antes de executar o comando acima
será necessário configurar o Maven. Para fazer isto, configure o
arquivo settings.xml conforme descrito no artigo "Configuring a proxy".
Após alguns downloads, o plugin de archetype, irá solicitar a resposta para algumas perguntas. Responda-as
conforme apresentado na tela a seguir:
$ tree -a teste
teste
├── pom.xml
└── src
├── main
│ └── java
│ └── com
│ └── ladoservidor
│ └── App.java
└── test
└── java
└── com
└── ladoservidor
└── AppTest.java
9 directories, 3 files
Se desejarmos apenas compilar os fontes da aplicação, sem testá-la, o comando a seguir serve a este
propósito. (Note que novamente uma série de arquivos serão baixados e instalados no repositório local):
$ mvn compile
Para executar os TestCases da aplicação gerada (classes que por convenção estão no diretório src/test)
executamos o comando abaixo. (Mais uma vez, ocorre uma série de downloads... ):
$ mvn test
Para gerar o artefato (um JAR neste caso), o comando seria o seguinte (mais downloads ;-):
$ mvn package
O comando acima gerará um artefato cujo nome será composto pelos elementos definidos
no pom.xml utilizando o padrão artifactId-version.packaging. Ou seja, neste caso, o artefato
gerado será teste-1.0-SNAPSHOT.jar. Este arquivo fica disponível no diretório target, como
demonstrado abaixo:
$ tree -a
.
├── pom.xml
├── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── ladoservidor
│ │ └── App.java
│ └── test
│ └── java
│ └── com
│ └── ladoservidor
│ └── AppTest.java
└── target
├── classes
│ └── com
│ └── ladoservidor
│ └── App.class
├── maven-archiver
│ └── pom.properties
├── surefire-reports
│ ├── com.ladoservidor.AppTest.txt
│ └── TEST-com.ladoservidor.AppTest.xml
├── test-classes
│ └── com
│ └── ladoservidor
│ └── AppTest.class
└── teste-1.0-SNAPSHOT.jar
18 directories, 9 files
Para que este artefato possa ser utilizado por algum outro, que o tenha como dependência, ele deverá estar
instalado. A instalação pode ocorrer em dois locais diferentes: o primeiro deles, é o repositório local. Quando um
artefato é implantado neste repositório, o desenvolvedor que o implantou pode utilizá-lo mas, nenhum outro
desenvolvedor terá como também utilizar este artefato a não ser que o implante em seu próprio repositório. É aí
então que surge a necessidade de um repositório remoto (e compartilhado) que possa ser utilizado por todos os
desenvolvedores. Mais a frente, neste tutorial, iremos utilizar o Nexus para criar e gerenciar este tipo de
repositório (remoto e compartilhado).
A instalação de um artefato no repositório local pode ser realizada através do seguinte comando (mais
downloads serão realizados):
$ mvn install
O processo de instalação geralmente solicita que antes sejam executados os testes e, por fim, o pacote é
copiado para o repositório local, conforme pode ser notado pela saída do comando acima.
Como nosso artefato foi configurado com o valor ladoservidor para o elemento groupId no
arquivo pom.xml, em nosso repositório local haverá um diretório ladoservidor que irá conter o JAR
instalado. Observe a árvore criada pelo maven a partir deste diretório:
$ tree ~/.m2/repository/ladoservidor/
/home/negocios/.m2/repository/ladoservidor/
└── teste
├── 1.0-SNAPSHOT
│ ├── maven-metadata-local.xml
│ ├── teste-1.0-SNAPSHOT.jar
│ └── teste-1.0-SNAPSHOT.pom
└── maven-metadata-local.xml
Uma vez instalado, outro artefato que tivesse este como dependência, poderia então utilizá-lo. Além disto, após
a instalação, talvez você queira apagar os arquivos produzidos no ato da construção do artefato. É fácil fazer
isto simplesmente executando o comando abaixo (mais downloads serão realizados):
$ mvn clean
O comando acima removerá o diretório target que contém tudo o que é gerado pela solicitação do Maven.
Como o Maven executa os comandos acima? Porque ele faz tantos downloads? A resposta a estas questões: o
Maven é extremamente modular. Quando o instalamos, ele só possui aquilo que é necessário para fazer o
download de todo o resto. O Maven é fortemente baseado no conceito de plugins que são configurados no
arquivo pom.xml para o uso pelo projeto. Os plugins do maven são, de certa forma, equivalentes a targets
do Ant. São eles que executam várias tarefas, realizadas por fases no processo de construção. Além de muitas
vezes estes plugins não estarem presentes, e ser necessário o seu download para a execução de tarefas
solicitadas em uma linha de comando, o Maven também baixará todas as dependências que um artefato
precisar para instalá-las no repositório local.
Uma vez instalados os plugins, eles ficarão permanentemente disponíveis e versionados no repositório local do
usuário. Eles só sairiam de lá caso o usuário excluísse o repositório. Sendo assim, caso solicitássemos a re-
execução dos comandos deste tópico, nenhum download seria realizado novamente. Podemos testar isto
realizando a execução do comandos a seguir:
Se observarmos cuidadosamente o log de saída do comando acima, poderemos notar que a execução do
TestCase com.ladoservidor.AppTest foi realizada três vezes. Isto ocorre pelo fato de, ao executarmos
os comandos test, package e install, estarmos executando fases. A fase test solicita, obviamente, a
execução de testes. Mas, a fasepackage é uma fase que depende da execução dos testes e, por
consequência, é executada novamente. O mesmo ocorre para a fase install. É por isto que a execução
ocorre repetidamente. Logo, o correto, quando queríamos repetir as fases executadas pelos vários comandos
anteriores em uma só linha, seria executarmos apenas as seguintes instruções:
Como talvez tenha sido notado, o Maven trabalha com o conceito de "convenções ao invés de configurações".
Por exemplo, é uma convenção a estrutura de diretórios utilizada para o armazenamento dos arquivos. Caso
desejássemos outra, poderíamos tê-la, mas também necessecitaríamos de configurações para isto. O conceito
de "convenções ao invés de configurações" é chave no Maven e é amplamente adotado em diversos aspectos,
tornando a ferramenta ainda mais simples de ser utilizada.
$ sudo su - web
$ mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=org.apache.wicket -DarchetypeArtifactId=wicket-archetype-
quickstart -DarchetypeVersion=1.4.9 \
-DgroupId=com.ladoservidor -DartifactId=teste-wicket -Dversion=1.0-SNAPSHOT
Alguns detalhes que precisamos notar na geração do projeto, pela execução do comando acima:
Para executar esta aplicação web, o pom.xml gerado por este archetype já possui todas as informações
necessárias para o trabalho com o plugin jetty, que é sobe um web container e implanta automaticamente o
artefato gerado (no caso, um arquivo WAR).
Também é interessante notar as configurações para as dependências do Jetty, que executa a aplicação. Como
os JARs necessários para a execução do mesmo já estão implantados neste container, não faz sentido que eles
também sejam colocados no diretório WEB-INF/lib do WAR que será gerado. Logo, o
elemento scope configurado para as dependências relativas aos JARs, neste caso, é to tipo provided. Isto
pode ser exemplificado pelo trecho de código a seguir:
O resultado do sed executado acima também demonstra que a versão do artefato jetty é configurada através de
uma propriedade. O seu valor pode ser obtido através da configuração do elemento properties:
Para implantar e executar a aplicação no Jetty, o pom.xml precisa conter a configuração para o uso do plugin
jetty. Isto é informado nas linhas abaixo, que estão configuradas como parte do elemento plugins:
Além destes, vários outros detalhes de configuração também poderiam ser explorados aqui, mas vamos seguir
em frente.
A parte ruim desta história: todos os downloads que já haviam sido realizados anteriormente na conta do
usuário negocios foram refeitos pelo usuário web:-(.
Atuar como proxies altamente configuráveis entre uma organização e os repositórios públicos do
Maven;
Suprir uma organização com um destino para a publicação de seus próprios artefatos;
Atuando como proxy para repositórios públicos, o Nexus reduz significativamente a quantidade de downloads
reduntantes pelos elementos da organização, evitando chamadas externas a Internet e diminuindo o tempo
gasto internamente, para o download de artefatos. Isto reduz, consideravelmente, o tempo para a construção de
aplicações.
Instalando O Nexus
Talvez desejemos ver o vídeo produzido pela própria Sonatype falando da instalação do Nexus. Mas, como o
processo é realmente muito simples, os passos são apenas os que estão publicados abaixo:
Vamos instalar o nexus, inicialmente em seu próprio $HOME. Para isto, vamos iniciar um terceiro shell, criar e
nos logar com um novo usuário:
$ wget -c http://nexus.sonatype.org/downloads/nexus-oss-webapp-1.7.0-
bundle.tar.gz
$ tar xvfz nexus-oss-webapp-1.7.0-bundle.tar.gz
Agora estamos preparados para iniciar o Nexus em modo console (mostrando o log de sua execução):
$ cd nexus-oss-webapp-1.7.0
$ ./bin/jsw/linux-x86-32/nexus console
Um repositório do tipo proxy é um repositório externo em que o Nexus busca os artefatos e os publica em sua
base local. Um repositório do tipo hosted é interno, ou seja, os artefatos que são publicados neste repositório
não são obtidos de nenhuma fonte externa. Um repositório do tipo group é, na verdade, um agrupamento de
repositórios.
No Maven, uma aplicação pode depender de dois tipos de artefatos: SNAPSHOTS e RELEASES. Um
SNAPSHOT geralmete é um artefato que não está marcado (tageado) em um sistema de controle de versões.
Um RELEASE, por sua vez, é um artefato que já pode ser considerado maduro (estável) e que geralmente já
está tageado num SCM. O Nexus gerencia e armazena estes dois tipos de artefatos, produzidos por uma
organização qualquer.
A partir de agora, todo e qualquer download que for realizado será feito através do Nexus, tendo como base a
URL especificada no arquivo $M2_HOME/conf/settings.xml. Sendo assim, quando outro qualquer outro
desenvolvedor da corporação for utilizar o Maven (configurando $M2_HOME p/ /opt/maven), caso os artefatos já
estejam armazenados no cache deste gerenciador, não haverá mais a necessidade de novas requisições HTTP
para a Internet, a fim de realizar o download dos mesmos artefatos.
Uma boa experiência, que enche os olhos de qualquer novo utilizador de um gerenciador de repositórios, é
apagar os artefatos armazenados em repositório local para testar a velocidade de um novo download dos
mesmos após a instalação do gerenciador. Vamos experrimentar isto!
Indo ao shell aberto para o usuário web, iremos apagar o seu repositório local e a seguir, mandaremos
reexecutar a aplicação:
$ rm -rf ~/.m2/repository
$ mvn install jetty:run
Podemos notar que agora todo o download está sendo realizado pela URL local do gerenciador de repositórios
do Nexus, ao invés de através da Internet. Como o Nexus está atuando como proxy, todos artefatos estão
sendo colocados em seu cache. Em seguida, o Maven faz a cópia do artefato para repositório local do usuário.
Desta forma podemos saber que, se apagarmos outra vez o repositório local, todos os artefatos baixados
anteriormente estarão no cache do nexus e, consequentemente, não haverá download de artefatos a partir da
Internet tornando a montagem do repositório local extremamente mais rápida!
A configuração feita acima, no arquivo settings.xml, habilitará o Maven a trabalhar com um repositório do
tipo group, que é um agrupamento de alguns repositórios onde, além de podermos fazer o download de
artefatos de repositórios públicos proxiados pelo Nexus, também podemos publicar snapshots e releases.
Na configuração apresentada, foi definido o uso de um único profile: nexus. Este profile está configurado para o
download a partir de um repositório central através de uma URL falsa (http://central). Esta URL é
sobrescrita nas configurações do Nexus e aponta para um repositório do tipo group. Ao final do arquivo está
explícito, através do elementoactiveProfiles, que este profile está ativo.
Para entender um pouco melhor o que é este grupo configurado no Nexus e que é acessível através da
URL http://localhost:8081/nexus/content/groups/public, vamos nos logar no Nexus para ver como este grupo
está configurado. Para fazer o login, o usuário/senha que devem ser informadas são admin/admin123. Após
o logon, vamos clicar no repositório "Public Repositories" pois é ele que está configurado para a Repository
Path com a URL informada no arquivo ~/.m2/settings.xml. Note que aparecerá a aba "Configuration" na
parte inferior da página.
Através desta configuração podemos notar que o identificador do grupo ( Group ID) configurado no
arquivo ~/.m2/settings.xml com o nome public representa um grupo cuja a ordem de busca dos
artefatos é listada no campo "Ordered Group Repositories". Veja que neste campo, são listados vários outros
repositórios configurados no Nexus.
Vamos realizar esta mudança guiando-nos pelos testes. Ou seja, vamos praticar Test Driven Development
(TDD). Então, iniciaremos nossas mudanças pela classecom.ladoservidor.TestHomePage. O comando
a seguir altera a linha 28 desta classe Java para o seu novo conteúdo (de acordo com o requisito):
$ sed -i '28s/"If.*running"/com.ladoservidor.App.MESSAGE/'
src/test/java/com/ladoservidor/TestHomePage.java
$ sed -i '25s/"If.*running"/com.ladoservidor.App.MESSAGE/'
src/main/java/com/ladoservidor/HomePage.java
$ sed -i '
/<dependencies>/ a\
\t\t<!-- Lado Servidor DEPENDENCIES -->\
\t\t<dependency>\
\t\t\t<groupId>ladoservidor</groupId>\
\t\t\t<artifactId>teste</artifactId>\
\t\t\t<version>1.0-SNAPSHOT</version>\
\t\t</dependency>
' pom.xml
Não vai dar para compilar a aplicação Web pois ela passou a depender de um pacote que ainda não está
instalado no repositório local do usuário web. A fim de instalá-lo, poderíamos pedir ao usuário negocios para,
após efetuar as mudanças necessárias, gerar e nos enviar o pacote para que pudessemos instalá-lo via "mvn
install". O problema desta abordagem é que ele é muito manual! A alternativa automática (e mais correta)
seria o usuário negocios gerar o pacote e implantá-lo em um repositório remoto e compartilhado. Desta forma,
com as configurações de acesso a este repositório realizadas pelo usuário web, ele poderia baixar o pacote
deste repositório remoto e instalá-lo localmente. Sendo assim, estes serão os próximos passos apresentados.
Voltando ao shell aberto para a execução dos comandos do usuário negocios, vamos alterar o
projeto testes, adicionando a linha que declara a constante MESSAGE na classecom.ladoservidor.App:
$ sed -i '8 a\
public static final String MESSAGE = "Hello World!";
' src/main/java/com/ladoservidor/App.java
O comando para isto é apesentado abaixo. Tentando execuá-lo, obtemos um erro do Maven informando que o
projeto ainda não foi configurado adequadamente para que o deploy possa ser relizado. A diferença básica
entre o comando "mvn install" e o comando "mvn deploy" é o tipo de instalação executada. No
primeiro caso, a instalação do artefato é no repositório local (conhecidamente ~/.m2/repository). No
segundo, a instalação é realizada num repostiório remoto que precisa estar configurado!
$ mvn deploy
Obviamente, o nosso próximo passo então, é configurar o pom.xml informando a localização deste repositório
remoto! O comando a seguir realiza esta tarefa alterando o arquivo pom.xml adequadamente:
$ sed -i '
/<\/dependencies>/ a\
<distributionManagement>\
<repository>\
<id>releases</id>\
<url>http://localhost:8081/nexus/content/repositories/releases</url>\
</repository>\
<snapshotRepository>\
<id>snapshots</id>\
<name>Internal Snapshots</name>\
<url>http://localhost:8081/nexus/content/repositories/snapshots</url>\
</snapshotRepository>\
</distributionManagement>
' pom.xml
Cada um dos repositórios também tem a sua URL de acesso para o download de artefatos configurada neste
arquivo.
Somente as informações acima não bastam, pois, caso seja reexecutado o comando "mvn deploy" iremos
tomar um erro de autenticação na publicação dos artefatos gerados no repositório remoto! Então, para não
termos este problema, será necessária também a cofiguração do arquivo ~/.m2/settings.xml. Como será
criado o arquivo settings.xml especificamente para o usuário negocios, o seu conteúdo será gerado através
do comando a seguir:
Após realizada a configuração acima, a cada nova execução do comando abaixo será gerado um snapshot no
repositório remoto. Este snapshot poderá então ser baixado pelo Maven quando outro artefato depender de sua
utilização. A próxima parte deste tutorial apresenta maiores detalhes sobre como um snapshot é armazenado no
repositório remoto e as diferenças com relação a um release.
$ mvn deploy
O comando acima irá baixar o snapshot gerado pelo usuário negocios e instalá-lo no repositório local para
que a aplicação possa então ser compilada com sucesso. Quando em execução pelo Jetty, a aplicação poderá
novamente ser acesssada pela URL http://localhost:8080/teste-wicket. Agora, a tela deverá apresentar a
mensagem "Hello World".