Tutorial: Utilizando Maven, Nexus e Subversion em projetos Java (Parte I)

Autor: Paulo Jerônimo

Última atualização: 07/07/2010 às 08:10
Comentários: http://j.mp/mvnnexus-1

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 um SCM, 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 do Cygwin para a execução das tarefas deste tutorial.

Motivando o uso do Maven

O Maven é uma ferramenta que, no contexto dos problemas apresentados na introdução deste tutorial, trabalha com as seguintes questões:

Uma introdução ao Maven

Criando os usuários que irão trabalhar com o Maven

Vamos iniciar o tutorial criando dois usuários que representarão um membro da equipe de negócios e outro da equipe web. A execução do comando a seguir criará as contas necessárias:

Copie do início da palavra sudo até o apóstrofo (') da última linha. A seguir, cole em seu shell:

$ sudo bash -c '
useradd -m -s /bin/bash web
useradd -m -s /bin/bash negocios 
'

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:

$ sudo bash -c '
cd /opt
wget -c http://mirror.pop-sc.rnp.br/apache/maven/binaries/apache-maven-3.0-beta-1-bin.tar.gz
tar xvfz apache-maven-3.0-beta-1-bin.tar.gz
ln -s apache-maven-3.0-beta-1 maven
'

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:

$ cat > /tmp/bashrc <<'EOF'
export JAVA_HOME=/usr/lib/jvm/java-6-openjdk
export M2_HOME=/opt/maven
export PATH=$M2_HOME/bin:$PATH
EOF
$ for user in negocios web; do sudo su -c 'cat /tmp/bashrc >> ~/.bashrc' $user; done
Um detalhe que talvez chame a atenção: no Ubuntu (10.04), o JDK que está sendo utilizado por padrão é o OpenJDK (o pacote padrão anteriormente era o sun-java6-jdk). Se desejarmos executar o JDK com este último pacote, os procedimentos são apontados por este link.

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

Gerando um projeto de negócios

Iniciaremos um novo projeto no Maven através do uso do plugin archetype. Archetypes são esqueletos gerados pelo Maven para a codificação de novos projetos.

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:

[INFO] Generating project in Interactive mode
Define value for property 'groupId': : ladoservidor
Define value for property 'artifactId': : teste
Define value for property 'version':  1.0-SNAPSHOT: : 
Define value for property 'package':  ladoservidor: : com.ladoservidor
Confirm properties configuration:
groupId: ladoservidor
artifactId: teste
version: 1.0-SNAPSHOT
package: com.ladoservidor
 Y: :

Veja a estrutura do projeto que foi criado:

$ tree -a teste
teste
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── ladoservidor
    │               └── App.java
    └── test
        └── java
            └── com
                └── ladoservidor
                    └── AppTest.java

9 directories, 3 files

Compilando, testando, instalando e implantando artefatos

O esqueleto gerado pelo Maven contém o arquivo pom.xml. Ele é o principal arquivo utilizado pelo Maven. O nome do arquivo leva as iniciais de "Project Object Model" e o seu conteúdo pode ser visualizado com o comando abaixo. Nele são definidas todas as questões principais acerca do projeto. Especificamente neste caso, o tipo de pacote que será gerado no processo de contrução pelo Maven será um JAR (definido pelo elemento packaging). Para que este JAR possa ser gerado, o maven executa fases no processo de contrução e, uma delas, é a fase de testes. Quando estiver nesta fase (que obviamente ocorre após a compilação) o Maven precisará baixar uma dependência, que é o JUnit. Então, ele fará o download do JUnit, na versão 3.8.1, buscando o JAR a partir de um dos seus repositórios públicos já pré-configurados. Em seguida, o arquivo baixado será instalado no repositório local do usuário e, desta forma, ele poderá então utilizar esta dependência na execução do TestCase codificado em com.ladoservidor.AppTest.

Mudando para o diretório do projeto e listando o conteúdo do arquivo pom.xml:

$ cd teste; cat pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>ladoservidor</groupId>
  <artifactId>teste</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>teste</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

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

A árvore acima é organizada utilizando-se os valores dos elementos groupId/artifactId/versionId.

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:

$ mvn compile test package install clean

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 fase package é 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:

$ mvn install clean

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.

Gerando um projeto Web que utiliza o framework Wicket

Vamos agora gerar outro projeto (artefato) que será uma aplicação Web. Para tornar as coisas um pouco mais divertidas esta aplicação utilizará o framework Apache Wicket. Mais a frente neste tutorial, iremos tornar a construção desta aplicação dependente do artefato (projeto) JAR gerado no tópico anterior.

Iremos gerar a aplicação Wicket no $HOME do usuário web utilizando também um archetype. Sendo assim, abriremos um novo shell e executaremos os comandos seguir:

$ 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:

A estrutura de diretórios que foi montada é apresentada a seguir:

$ cd teste-wicket && tree -a
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── ladoservidor
    │   │           ├── HomePage.html
    │   │           ├── HomePage.java
    │   │           └── WicketApplication.java
    │   ├── resources
    │   │   └── log4j.properties
    │   └── webapp
    │       └── WEB-INF
    │           └── web.xml
    └── test
        └── java
            └── com
                └── ladoservidor
                    ├── Start.java
                    └── TestHomePage.java

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).

Compreendendo alguns detalhes no pom.xml do projeto Web

Vamos nos ater a alguns detalhes do pom.xml para o projeto gerado. Como este é apenas uma tutorial introdutório, não iremos explorar todos os vários recursos de configuração do Maven mas, apresentaremos alguns deles.

As linhas de 4 a 7 do pom.xml apresentam os atributos essenciais do POM. Observe que o tipo de empacotamento agora é diferente (produz um arquivo WAR):

$ sed -n '4,7p' pom.xml
  <groupId>com.ladoservidor</groupId>
  <artifactId>teste-wicket</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>

Na execução do comando abaixo, apresentamos as linhas que configuram a dependência JUnit. O detalhe aqui é o elemento scope indicando que a dependência junit só é necessária quando o Maven for realizar testes da aplicação.

$ sed -n '47,53p' pom.xml
    <!--  JUNIT DEPENDENCY FOR TESTING -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.2</version>
      <scope>test</scope>
    </dependency>

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:

$ sed -n '56,61p' pom.xml
    <dependency>
      <groupId>org.mortbay.jetty</groupId>
      <artifactId>jetty</artifactId>
      <version>${jetty.version}</version>
      <scope>provided</scope>
    </dependency>

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:

$ sed -n '129,132p' pom.xml
  <properties>
    <wicket.version>1.4.9</wicket.version>
    <jetty.version>6.1.4</jetty.version>
  </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:

$ sed -n '116,119p' pom.xml
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
      </plugin>

Além destes, vários outros detalhes de configuração também poderiam ser explorados aqui, mas vamos seguir em frente.

Executando a aplicação Web

Para mandar executar a aplicação o comando é simples:

$ mvn install jetty:run

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:-(.

A execução do comando acima na realidade cuida de várias tarefas:

Com a aplicação sendo executada, podemos agora, acessá-la no browser através da URL http://localhost:8080/teste-wicket. Esta aplicação não faz nada além do que qualquer outra aplicação do tipo "Hello World" faria mas, nós iremos atualizá-la para entender o mecanismo de deploy utilizado pelo Maven. Para interrromper sua execução, iremos na console que está executando o comando teclaremos <Ctrl-C>.

Vendo a árvore de dependências da aplicação Web

Como este projeto (artefato WAR) possui várias dependências, se desejássemos imprimir a árvore das dependências, poderíamos executar o comando a seguir e observar sua saída (mais downloads :-():

$ mvn dependency:tree | sed -n '/Building/,/BUILD/p'
[INFO] Building quickstart 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ teste-wicket ---
[INFO] com.ladoservidor:teste-wicket:war:1.0-SNAPSHOT
[INFO] +- org.apache.wicket:wicket:jar:1.4.9:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.5.8:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.4.2:compile
[INFO] +- log4j:log4j:jar:1.2.14:compile
[INFO] +- junit:junit:jar:3.8.2:test
[INFO] +- org.mortbay.jetty:jetty:jar:6.1.4:provided
[INFO] |  \- org.mortbay.jetty:servlet-api-2.5:jar:6.1.4:provided
[INFO] +- org.mortbay.jetty:jetty-util:jar:6.1.4:provided
[INFO] \- org.mortbay.jetty:jetty-management:jar:6.1.4:provided
[INFO]    +- mx4j:mx4j:jar:3.0.1:provided
[INFO]    \- mx4j:mx4j-tools:jar:3.0.1:provided
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

Note que cada linha da árvore de dependências é apresentada no seguinte formato: groupId:artifactId:packaging:version:scope. Esta saída apresenta toda a informação necessária para se obter os detalhes de que artefatos são necessários em cada estágio (escopo) do processo de contrução/implantação de um artefato.

Uma introdução ao Nexus

Motivando o uso do Nexus

O Nexus é um gereciador de repositórios para o Maven.

Gerenciadores de repositórios tem dois propósitos primordiais:

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:

$ sudo useradd -m -s /bin/bash nexus
$ sudo su - nexus

Agora, vamos fazer o seu download e descompactação:

$ 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

Ao descompactar o instalador do nexus, serão gerados dois diretórios: nexus-oss-webapp-1.7.0 e sonatype-work. O primeiro armazena os binários de execução do Nexus. Isto é assim para facilitar o upgrade de uma versão do nexus de forma a não ser necessária a maninulação do seu diretório de dados.

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

Após o servidor ter iniciado, podemos acessar a URL http://localhost:8081/nexus que apresenta a interface administrativa do Nexus. A tela inicial é mostrada abaixo. Observe que ao lado esquerdo da tela, quando clicamos no link "Repositories" podemos observar uma lista de repositórios já cadastrados e gerenciados pelo Nexus. Um repositório é um local aonde os artefatos consumidos ou produzidos pelo Maven serão armazenados. Na tela que apresenta os repositórios, podemos notar a presença de alguns tipos de repositórios: group, hosted, proxy e virtual.

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.

Alterando as configurações do Maven para que ele use o Nexus

Para que o Maven possa atuar com o Nexus em seus dois propósitos primordiais ele precisa ter seu arquivo de configuração (settings.xml) ajustado. Isto pode ser realizado para um usuário específico (~/.m2/settings.xml) ou para todos os usuários ($M2_HOME/conf/settings.xml). Como neste tutorial o Maven está sendo utilizado por dois usuários (negocios e web) iremos criar um arquivo de configurações ao nível de instalação do Maven. Para isto, abra um quarto shell e execute o comando a seguir:

$ sudo bash -c '
cat > /opt/maven/conf/settings.xml <<EOF
<settings>
  <mirrors>
    <mirror>
      <!--This sends everything else to /public -->
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>http://localhost:8081/nexus/content/groups/public</url>
    </mirror>
  </mirrors>
  <profiles>
    <profile>
      <id>nexus</id>
      <!--Enable snapshots for the built in central repo to direct -->
      <!--all requests to nexus via the mirror -->
      <repositories>
        <repository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
     <pluginRepositories>
        <pluginRepository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <!--make the profile active all the time -->
    <activeProfile>nexus</activeProfile>
  </activeProfiles>
</settings>
EOF
'

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 elemento activeProfiles, 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.

Alterando o projeto web gerado inicialmente

Vamos alterar o projeto Web para que ele utilize o projeto de negócios como dependência. A alteração será muito simples: ao invés de imprimir a mensagem padrão apresentada pela aplicação em sua página principal "If.*running", queremos que seja impresso o conteúdo da constante MESSAGE que será declarada na classe de negócios com.ladoservidor.App.

Vamos realizar esta mudança guiando-nos pelos testes. Ou seja, vamos praticar Test Driven Development (TDD). Então, iniciaremos nossas mudanças pela classe com.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

Com esta mudança, o código da classe de testes agora dependerá da classe de negócios com.ladoservidor.App. Também faremos a mudança necessária no código da classe com.ladoservidor.HomePage, na linha 25:

$ sed -i '25s/"If.*running"/com.ladoservidor.App.MESSAGE/' src/main/java/com/ladoservidor/HomePage.java

Como agora o projeto depende da classe com.ladoservidor.App, precisaremos informar o JAR que contém esta dependência no arquivo pom.xml. Isto pode ser realizado através do seguinte comando:

$ 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.

Alterando o projeto negócios gerado inicialmente

Vamos alterar o projeto de negócios (que gera o JAR) para que ele contenha a constante que é utilizada pelo projeto Web.

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 classe com.ladoservidor.App:

$ sed -i '8 a\
    public static final String MESSAGE = "Hello World!";
' src/main/java/com/ladoservidor/App.java

Efetuando o deploy do projeto de negócios

Vamos efetuar a implantação do projeto de negócios para torná-lo disponível para uso do projeto Web.

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

Fazendo as alterações acima, estamos agregando duas informações ao projeto:

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:

$ cat > ~/.m2/settings.xml <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <servers>
    <server>
      <id>snapshots</id>
      <username>deployment</username>
      <password>deployment123</password>
    </server>
    <server>
      <id>releases</id>
      <username>deployment</username>
      <password>deployment123</password>
    </server>
    <server>
      <id>thirdparty</id>
      <username>deployment</username>
      <password>deployment123</password>
    </server>
  </servers>
</settings>
EOF

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

Finalizando a construção do projeto Web

Voltando ao shell da aplicação Web, podemos agora solicitar que ela seja construída pelo maven e executada pelo Jetty. Para fazer isto:

$ mvn install jetty:run

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".

Extras