domingo, 15 de junho de 2014

Maven Pro Lição 2: O buraco é mais embaixo


Cara, na boa, você não pensou que Maven era só aquilo, certo? Tem mais... E nessa lição, vamos detalhar mais o pom.xml e como você pode organizar seus projetos. vamoquevamo!

O Vídeo dessa lição está PUBLICADO AQUI!

O buraco é mais embaixo


- Plugins;
- Escopo de dependências;
- Propriedades;
- Herança;
- Módulos;
- Settings e profiles;

Antes de estudar a lição, assista ao vídeo AQUI!


Plugins

Tudo o que o Maven faz é executar plugins. Todas as suas funções são executadas através deles. As fases, na verdade, são compostas pela execução combinada de plugins. 

Um plugin Maven é uma classe Java apelidada de MOJO (Maven plain Old Java Object), que é a API disponibilizada para criar "goals" a serem executados pelo Maven. A palavra "mojo" também quer dizer "charme" ou "sex appeal". 

A minha ferramenta de análise de código, o jQana, é um MOJO que roda sob o Maven.

Todo plugin pode ser executado diretamente, se você souber quais são os seus "goals" e parâmetros. Por exemplo: 

mvn clean:clean jar:jar

Estamos mandando o Maven executar dois "goals" de dois plugins diferentes: 
  • Plugin "clean", "goal" "clean": Limpa a pasta "target";
  • Plugin "jar", "goal" "jar": Cria um arquivo "jar" com o projeto compilado;
Na linha de comando Maven podemos especificar fases, plugins ou uma combinação de ambos. E podemos fazer isso no eclipse também! Basta selecionar o projeto e clicar com o botão direito, escolhendo: "Run As Maven Build...", então, basta escrever a lista de fases e plugins que desejarmos executar: 



Nós já usamos isso, quando mandamos o Maven criar um projeto usando um arquétipo: 

mvn archetype:generate \
-DgroupId=com.exemplo.projetoteste \
-DartifactId=projetoteste \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false

Um bom exemplo de uso de plugins é para ver a árvore de dependências de um projeto: 

mvn dependency:tree

[INFO] [dependency:tree {execution: default-cli}]
[INFO] com.exemplo.projetoteste:projetoteste:jar:1.0-SNAPSHOT
[INFO] \- junit:junit:jar:3.8.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 19 seconds
[INFO] Finished at: Thu Jun 12 05:52:23 BRT 2014
[INFO] Final Memory: 10M/81M
[INFO] ------------------------------------------------------------------------


Como você já deve ter notado, as dependências de um projeto formam uma árvore, pois cada dependência é, por sua vez, criada por um Projeto Maven, logo, possui dependências de outros projetos Maven. Já já voltaremos a falar disso.

As fases do Ciclo de Vida de Construção Maven são, na verdade, implementadas por plugins:

process-resourcesresources:resources
compilecompiler:compile
process-test-resourcesresources:testResources
test-compilecompiler:testCompile
testsurefire:test
packagejar:jar
installinstall:install
deploydeploy:deploy

Você pode reconfigurar esses plugins, mudando versão ou parâmetros, ou pode mesmo adicionar outros plugins, indicando quando devem ser executados. Podemos fazer isso dentro do "pom.xml", no tag "<build>":

<build>
<finalName>microblog</finalName>
<plugins>
<!-- Ignorar os testes de integração -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.1</version>
<configuration>
<excludes>
<exclude>**/integracao/**</exclude>
</excludes>
</configuration>
</plugin>
        ...



Neste exemplo estamos configurando o plugin "surefire", que executa os testes do projeto, para ignorar todos os testes que estejam em uma subpasta "integracao". Esta é uma técnica que usamos quando queremos separar testes unitários dos de integração.

Dentro do tag "<plugins>" configuramos todos os plugins cujo comportamento ou versão desejamos alterar. Note que os plugins são como Dependências Maven, sendo identificados por suas "Coordenadas Maven" (groupId, artifactId e version). O tag "<configuration>" permite mudarmos as configurações de cada plugin,

Mas como eu posso saber quais configurações um plugin aceita? 

Isso é com o autor do plugin! Se for um plugin conhecido, então deverá ter um site com informações de uso "usage page". Por exemplo, se "googarmos" "maven surefire plugin", veremos que ele tem várias configurações possíveis:

plugins>
    [...]
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.17</version>
        <configuration>
          <parallel>methods</parallel>
          <threadCount>10</threadCount>
        </configuration>
      </plugin>
    [...]
</plugins>

O Surefire, por default, executa TODOS os test-cases definidos dentro da pasta "src/test/java", sempre que invocarmos a fase "test" ou as posteriores ("package", "install" ...).

Se você quiser adicionar um novo plugin (que não esteja previsto no ciclo de vida) ou configurar novas execuções de um plugin, é possível fazer isso com o tag "<executions>":

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>2.11</version>
        <executions>
          <execution>
            <goals>
              <goal>integration-test</goal>
              <goal>verify</goal>
            </goals>
          </execution>
        </executions>

      </plugin>

Neste exemplo, estou configurando o plugin "FailSafe", que executa testes de integração, para rodar dois "goals" sempre: "integration-test" e "verify".

E é possível indicar em qual fase do ciclo de vida o plugin deve ser invocado:

<plugin>
   <groupId>com.github.github</groupId>
   <artifactId>site-maven-plugin</artifactId>
   <version>0.9</version>
   <configuration>
...
   </configuration>
    <executions>
              <execution>
                <goals>
                  <goal>site</goal>
                </goals>
                <phase>deploy</phase>
              </execution>
     </executions>
</plugin>
Neste exemplo, estou associando o "goal" "site", do plugin "site-maven-plugin", para ser executado na fase "deploy".

Existem muitos plugins para o Maven. Se você quiser ver uma lista dos mais comuns, clique AQUI.

Dependências

Eu sei que você ainda está bolado(a) com as dependências. Eu sei, pois também fiquei assim quando aprendi o Maven. Para começar, as dependências são projetos Maven, logo, possuem seus próprios arquivos "pom.xml". E, se isso é verdade, elas também possuem dependências! Sim! Na verdade, um projeto Maven possui uma "árvore" de dependências:


As dependências transitivas são as dependências que as suas dependências possuem. Isso pode ser visto com o plugin "mvn dependency:tree", como já vimos:

[INFO] Building Microblog
[INFO]    task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree {execution: default-cli}]
[INFO] com.galaticempire.guia.microblog:microblog:war:0.0.1-SNAPSHOT
[INFO] +- log4j:log4j:jar:1.2.14:compile
[INFO] +- junit:junit:jar:4.5:test
[INFO] +- javax.servlet:servlet-api:jar:2.5:provided
[INFO] +- javax.servlet.jsp:jsp-api:jar:2.1:provided
[INFO] +- javax.el:el-api:jar:1.0:provided
[INFO] +- com.sun.facelets:jsf-facelets:jar:1.1.15:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.4.2:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.4.2:compile
[INFO] +- org.apache.myfaces.core:myfaces-api:jar:1.2.9:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] +- org.apache.myfaces.core:myfaces-impl:jar:1.2.9:compile
[INFO] |  +- commons-collections:commons-collections:jar:3.2:compile
[INFO] |  +- commons-discovery:commons-discovery:jar:0.4:compile
[INFO] |  +- commons-codec:commons-codec:jar:1.3:compile
[INFO] |  +- commons-beanutils:commons-beanutils:jar:1.7.0:compile
[INFO] |  \- commons-digester:commons-digester:jar:1.8:compile
[INFO] +- org.apache.derby:derbyclient:jar:10.8.2.2:compile
[INFO] \- org.hibernate:hibernate-entitymanager:jar:3.5.3-Final:compile
[INFO]    +- org.hibernate:hibernate-core:jar:3.5.3-Final:compile
[INFO]    |  +- antlr:antlr:jar:2.7.6:compile
[INFO]    |  +- dom4j:dom4j:jar:1.6.1:compile
[INFO]    |  |  \- xml-apis:xml-apis:jar:1.0.b2:compile
[INFO]    |  \- javax.transaction:jta:jar:1.1:compile
[INFO]    +- org.hibernate:hibernate-annotations:jar:3.5.3-Final:compile
[INFO]    |  \- org.hibernate:hibernate-commons-annotations:jar:3.2.0.Final:compile
[INFO]    +- cglib:cglib:jar:2.2:compile
[INFO]    |  \- asm:asm:jar:3.1:compile
[INFO]    +- javassist:javassist:jar:3.9.0.GA:compile
[INFO]    \- org.hibernate.javax.persistence:hibernate-jpa-2.0-api:jar:1.0.0.Final:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7 seconds
[INFO] Finished at: Fri Jun 13 06:21:47 BRT 2014
[INFO] Final Memory: 16M/81M

[INFO] ------------------------------------------------------------------------

E também pode ser visto no Eclipse, bastando abrir o arquivo "pom.xml". Normalmente, ele abre com o Maven Pom Editor, mas, se isso não acontecer, é só selecionar "Open with..." e indicar o "Maven Pom Editor". Existe uma aba "Dependency Hierarchy" que já mostra exatamente a árvore de dependências:

Mas, para que eu uso isso? 

Excelente pergunta! Você usa a hierarquia de dependências para analisar se existem conflitos de dependências em sua árvore! As dependências transitivas podem causar problemas, já que pode haver duas versões diferentes do mesmo artefato Maven no Classpath, e só Deus sabe qual delas a JVM vai carregar. Veja aquela nossa figura novamente:



Notou algum problema? O Artefato "MNT" está sendo usado como dependência dos artefatos "TPD" e "ABCD". Normalmente, isso não seria um problema, mas, como são de versões diferentes, poderá haver um problema quando eles forem usar as classes de "MNT".

O que fazer nesses casos? 

Temos algumas alternativas:

  1. Remover a dependência transitiva mais antiga. Podemos excluir a dependência "MNT" versão 1.0.0, e esperar que o componente "TPD" funcione corretamente com a versão "2.4.0", que é exigida pelo componente "ABCD";
  2. Procurar uma versão mais nova do componente "TPD", que funcione com a versão "2.4.0" do "MNT";
A primeira alternativa pode ser configurada no "pom.xml", no momento em que configurarmos a dependência "TPD" em nosso projeto: 

<dependencies>
   ...
   <dependency>

      <groupId>com.teste.projeto</groupId>
      <artifactId>TPD</artifactId>
      <version>3.4.4</version>
      <exclusions>
         <exclusion>
              <groupId>com.exemplo</groupId>
              <artifactId>MNT</artifactId>
        </exclusion>
     </exclusions>
  </dependency>
...

Escopo!

É... Esse esquema de dependências é meio "cheirado" mesmo... Na verdade, o que o Maven faz é colocar no "Classpath" o JAR da dependência. Assim, quando você compilar ou testar, as classes esperadas estarão disponíveis.

Mas atenção: O Maven NÃO COPIA OS JARS DAS DEPENDÊNCIAS! Ele não cria uma pasta "lib" e copia tudo para dentro dela! (E, por falar nisso, um "jar" não pode ter outros ˜jars" dentro dele!)

Quando você empacota um artefato como "jar", é esperado que TODAS as suas dependências estejam no Repositório Local Maven (a pasta ".m2").

A única exceção é quando seu projeto tem um empacotamento do tipo "WAR" ou "EAR" (o tag "packaging" define o tipo de empacotamento do Projeto). Neste caso, o Maven vai copiar os "jars" para a pasta "WEB-INF/lib".

Porém, há mais de um "Classpath" a ser considerado: Compilação, Teste e Execução. Cada "Classpath" é considerado um "escopo" de dependências, e podemos especificar quais artefatos estarão em quais classpaths com o tag "<scope>":

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>

</dependency>

Temos aqui três dependências, cada uma com um escopo diferente. A primeira, "log4j" não tem escopo identificado, logo, estará em todos os classpaths. A segunda, "junit", só estará presente no classpath de teste, e a última, "servlet-api", estará no classpath de compilação, porém, no teste e execução, é considerada como "presente", só que o Maven não a fornecerá.

Calma! Respire fundo e vamos ver como isso funciona:

  • Sem escopo: É a mesma coisa de colocar: "<scope>compile</scope>", ou seja, a dependência será colocada pelo Maven em TODOS os classpaths;
  • "test": A dependência será colocada pelo Maven no classpath quando você mandar executar os testes ("mvn test" ou "mvn package");
  • "provided": A dependência estará disponível nos classpaths de compilação e teste, mas, na execução, é responsabilidade de outros fornecerem a dependência;
  • "system": NÃO USE! Serve para "engravidar" projetos, da mesma forma que fazíamos com o "lib". Você pode indicar a pasta onde a dependência está, exatamente como fazia com a pasta "lib";
O escopo "provided" é importante em projetos Web, por exemplo. É esperado que o Container Java EE forneça a implementação de alguns componentes, como a "servlet-api", do exemplo. Quando empacotarmos o "WAR", ela não será colocada em "WEB-INF/lib".

O escopo "test" nos permite colocarmos versões específicas para execução de testes, o que é bem útil. 

Propriedades

O "pom.xml" pode utilizar propriedades ao invés de valores explícitos. Podemos usar variáveis de ambiente, propriedades do arquivo "settings.xml" e propriedades Java.Podemos também criar e usar nossas propriedades no "pom.xml". Veja esse exemplo:

...
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java-version>1.6</java-version>
  </properties>   
  <dependencies>
... 
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>${java-version}</source>
          <target>${java-version}</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

...

Aqui, nós definimos o valor de duas propriedades e usamos uma delas (java-version) explicitamente em outra parte do "pom.xml". O Tag "<properties>" nos permite fazer isso. Para usar o valor de uma propriedade é só digitar seu nome entre "${" e "}". No exemplo anterior, a propriedade "<java-version>" é utilizada através da referência: "${java-version}". Podemos criar propriedades com o nome que quisermos, como eu fiz no exemplo, ou podemos definir valores de propriedades Maven, como também fizemos.

Variáveis de ambiente como propriedades

É isso mesmo! Você pode definir variáveis de ambiente externamente e usar como propriedades dentro do "pom.xml", bastando começar a referência por "env.". Use sempre maiúsculas para variáveis de ambiente! Veja esse exemplo:


export $JAVA-VERSION=1.6
ou
set %JAVA_VERSION%=1.6

       <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>${env.JAVA-VERSION}</source>
          <target>${env.JAVA-VERSION}</target>
        </configuration>

      </plugin>

Propriedades Maven: Builtin

O Maven exporta automaticamente algumas propriedades para nós, cujos valores podemos usar sem ter que definir no tag "<properties>". As propriedades "builtin", por exemplo são:




  • ${basedir}: A pasta do projeto, que contém o arquivo "pom.xml";


  • ${version}: A "Coordenada Maven" que informa a versão do Projeto, como no tag "<version>";

  •  Propriedades Maven: Tags do "pom.xml"

    Todos os Tags do modelo do "pom.xml" podem ter seu valor definido como se fossem propriedades. Eu fiz isso no exemplo abaixo:

    ...
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>   

    Isto equivale a mudar o valor do tag "<sourceEncoding>", como demonstro aqui:

    <project>
    ...
      <build>
         <sourceEncoding>UTF-8</sourceEncoding>
         ...
      </build>
    ...

    Propriedades Maven: Definidas por plugins

    Alguns plugins exportam propriedades, as quais também podem ter seu valor definido ou utilizado como se fossem propriedades comuns. Por exemplo, ao invés de definir as versões "source" e "target" do Java dentro da configuração do plugin "maven-compiler-plugin", podemos fazer assim:

    ...
    <properties>
       <maven.compiler.source>1.6</maven.compiler.source>
       <maven.compiler.target>1.6</maven.compiler.target>
    </properties>
    ...

    Geralmente, na documentação dos plugins há informação sobre quais propriedades eles definem.

    Propriedades Maven: Herdadas

    Como veremos mais adiante, pode existir uma relação de herança entre projetos, com um "super pom" e seus "pom.xml" derivados. Neste caso, podemos usar propriedades herdadas usando "${project.parent.xxxxxx}":

    <version>${project.parent.version}</version>

    Propriedades: Java System Properties

    Podemos usar qualquer propriedade Java (aquelas que obtemos com: "System.getProperty()":
    • ${java.home}
    Também podemos passar valores de "System Properties" usando a opção "-D", na linha de comando.

    Propriedades: Settings.xml

    Ainda não falamos disso, mas o Maven nos permite criar um arquivo especial de configuração, chamado: "settings.xml", que fica dentro da pasta ".m2" de cada usuário. Ele também pode ser passado por linha de comando, se quisermos mudar a configuração. Mas, por enquanto, só quero mostrar como definir propriedades dentro dele. Nós as utilizamos no "pom.xml" como: "${settings.xxxx}".

    Propriedades que todo "pom.xml" deveria definir

    Sempre que você criar um projeto, acostume-se a usar: 

    ...
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <maven.compiler.source>xxxxx</maven.compiler.source>
        <maven.compiler.target>xxxxx</maven.compiler.target>

    </properties>
    ...

    UTF-8 é o padrão de codificação de caracteres, e você deve utilizá-lo em sua IDE. Configure o eclipse usando o menu "Window / Preferences", e depois: "General / Editors" e mude o valor de "Character Encoding" do "Text Editor. Se informar isso no "pom.xml" evitará problemas com arquivos fonte que definam literais string.

    E sempre informe o "source" e "target" que o seu código Java espera. Nunca deixe por conta do Maven.

    Se você tem um projeto grande, pode usar a Herança para isso.

    Herança

    Como já mencionamos antes, é possível criar uma relação de herança entre dois arquivos "pom.xml". Isso significa que podemos definir a maioria das coisas em um "pom.xml" geral do projeto, deixando os outros herdarem tudo, desde o "groupId" até as dependências.

    Por exemplo, considere esse Projeto, representado por esse "pom.xml":

      <modelVersion>4.0.0</modelVersion>
      <groupId>com.teste.projeto</groupId>
      <artifactId>Projeto2</artifactId>
      <packaging>pom</packaging>
      <version>1.0.0-SNAPSHOT</version>
      <name>Projeto2</name>
      <url>http://maven.apache.org</url>
      <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <maven.compiler.source>1.6</maven.compiler.source>
          <maven.compiler.target>1.6</maven.compiler.target>
      </properties>
      <dependencies>
          <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-core</artifactId>
              <version>4.3.5.Final</version>
          </dependency>
          
          
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.10</version>
          <scope>test</scope>
        </dependency>
        
      </dependencies>
    </project>

    Definimos um projeto principal, cujo detalhe é o "<packaging>pom</packaging>". Depois, podemos definir vários projetos "filhos" dele, como esse, por exemplo:

      <modelVersion>4.0.0</modelVersion>
      <parent>
          <groupId>com.teste.projeto</groupId>
          <artifactId>Projeto2</artifactId>
          <version>1.0.0-SNAPSHOT</version>
      </parent>
      <artifactId>Projeto3</artifactId>
      <packaging>jar</packaging>
      <name>Projeto3</name>
      <url>http://maven.apache.org</url>


    </project>

    Este projeto herdou tudo do seu "pai", inclusive o "groupId" e o "version". Podemos comprovar isso rodando o "goal" "mvn help:effective-pom":

    <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/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>com.teste.projeto</groupId>
        <artifactId>Projeto2</artifactId>
        <version>1.0.0-SNAPSHOT</version>
      </parent>
      <groupId>com.teste.projeto</groupId>
      <artifactId>Projeto3</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <name>Projeto3</name>
      <url>http://maven.apache.org</url>
      <properties>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
      <dependencies>
        <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-core</artifactId>
          <version>4.3.5.Final</version>
          <scope>compile</scope>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.10</version>
          <scope>test</scope>

        </dependency>
    ...

    Como podem notar, o "pom" efetivo tem propriedades e dependências que nós não especificamos. Eu marquei em negrito tudo o que foi herdado.

    Mesmo que você não especifique um "parent", todo "pom.xml" herda do "Super Pom", que é padrão para cada implementação do Maven.

    Tudo o que for especificado no "pom.xml" filho, será sobreposto ao que foi herdado. Podemos, desta forma, controlar o que será herdado ou não.

    Geralmente, o projeto "pai" fica no repositório local. Porém, se esse não for o caso, podemos especificar a localização do "pom pai" através do tag: "<relativePath>../diretorio</relativePath>", dentro do tag "<parent>".

    O que é herdado:
    • "groupId";
    • "version";
    • dependências;
    • plugins;
    Se você quiser definir configurações de plugins para todos os projetos-filhos, então deve usar o tag "<pluginManagement>":


      <build>
        ...
        <pluginManagement>
          <plugins>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-jar-plugin</artifactId>
              <version>2.2</version>
              <executions>
                <execution>
                  <id>pre-process-classes</id>
                  <phase>compile</phase>
                  <goals>
                    <goal>jar</goal>
                  </goals>
                  <configuration>
                    <classifier>pre-process</classifier>
                  </configuration>
                </execution>
              </executions>
            </plugin>
          </plugins>
        </pluginManagement>
        ...
      </build>


    Conforme a documentação do Maven especifica, o tag "<pluginManagement>" não invoca os plugins, mas apenas indica sua configuração para que os "pom.xml" derivados as herdem.

    Módulos

    Projetos grandes podem ser divididos em módulos, cada um sendo um sub-projeto. O Maven processará todos os módulos de um projeto de uma só vez, como um grupo. Você pode ter todas as vantagens da herança, pois as configurações e dependências podem ser transferidas para os módulos.

    Um projeto "agregador" é um "pom.xml" com "<packaging>pom</packaging>", que define seus módulos. Cada módulo é uma subpasta dentro da pasta do projeto principal. 

    Por exemplo, vamos criar um projeto principal, chamado "ProjetoModular":


      <modelVersion>4.0.0</modelVersion>
      <groupId>com.teste.projeto</groupId>
      <artifactId>ProjetoModular</artifactId>
      <packaging>pom</packaging>
      <version>1.0.0-SNAPSHOT</version>
      <name>ProjetoModular</name>
      <url>http://maven.apache.org</url>
      <modules>
          <module>Modulo1</module>
          <module>Modulo2</module>
      </modules>
      <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <maven.compiler.source>1.6</maven.compiler.source>
          <maven.compiler.target>1.6</maven.compiler.target>
      </properties>
      <dependencies>
          <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-core</artifactId>
              <version>4.3.5.Final</version>
          </dependency>
          
          
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.10</version>
              <scope>test</scope>
          </dependency>

      </dependencies>
    </project>

    Ele será um projeto agregador e também um projeto "pai", contendo propriedades e dependências a serem herdadas. Como ele possui dois módulos, é esperado que existam duas subpastas logo abaixo da pasta principal dele: "../Modulo1" e "../Modulo2". Eis os "pom.xml" de ambos os módulos:

      <modelVersion>4.0.0</modelVersion>
      <parent>
          <groupId>com.teste.projeto</groupId>
          <artifactId>ProjetoModular</artifactId>
          <version>1.0.0-SNAPSHOT</version>
      </parent>
      <artifactId>Modulo1</artifactId>
      <packaging>jar</packaging>
      <name>PModulo1</name>
      <url>http://maven.apache.org</url>

    </project>

      <modelVersion>4.0.0</modelVersion>
      <parent>
          <groupId>com.teste.projeto</groupId>
          <artifactId>ProjetoModular</artifactId>
          <version>1.0.0-SNAPSHOT</version>
      </parent>
      <artifactId>Modulo2</artifactId>
      <packaging>jar</packaging>
      <name>PModulo2</name>
      <url>http://maven.apache.org</url>

    </project>

    Agora, se entrarmos na pasta "ProjetoModular" (o agregador) e dermos o comando "mvn clean install", ambos os módulos serão compilados e instalados: 

    [INFO] ------------------------------------------------------------------------
    [INFO] Reactor Summary:
    [INFO] 
    [INFO] ProjetoModular .................................... SUCCESS [  0.380 s]
    [INFO] PModulo1 .......................................... SUCCESS [  2.449 s]
    [INFO] PModulo2 .......................................... SUCCESS [  0.599 s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 3.528 s
    [INFO] Finished at: 2014-06-14T20:27:34-03:00
    [INFO] Final Memory: 11M/81M
    [INFO] ------------------------------------------------------------------------


    Como o projeto "ProjetoModular" é um agregador, possui "packaging" "pom", logo, o Maven vai procurar os módulos e compilá-los. E, como ele também é um "parent", suas propriedades e dependências serão herdadas pelos projetos filhos (que, por acaso, são os módulos).

    Settings e Profiles

    O arquivo "settings.xml" configura várias opções para o Maven, entre elas:
    • Usuário e senha para usar repositórios;
    • Propriedades;
    • Profiles.
    Toda instalação de Maven tem um arquivo "settings.xml" dentro da pasta "/conf". Este é o arquivo Global de configurações, que é utilizado para TODOS os usuários do computador. Também podemos especificar um arquivo "settings.xml" dentro da pasta ".m2", contendo configurações específicas para cada usuário (a pasta ".m2" fica dentro da pasta "home" de cada usuário). 

    A localização do "settings.xml", tanto Global, como Local, pode ser alterada através de opções de linha de comando do Maven:
    • -s <localização do settings.xml local>;
    • -gs <localização do settings.xml global>;
    Um dos usos mais importantes do "settings.xml" é definir autorização para acesso a repositórios (veremos na próxima aula).

    Outro uso importante é definir "profiles" ou perfís de compilação Maven.

    Eis um arquivo "settings.xml" típico:

                          http://maven.apache.org/xsd/settings-1.0.0.xsd">
      <servers>
           <server>
               <id>github</id>
               <username>XXXX</username>
               <password>XXXX</password>
           </server>
      </servers>
      <profiles>
        <profile>
           <id>sonar</id>
             <activation>
                 <activeByDefault>true</activeByDefault>
             </activation>
           <properties>
             <sonar.host.url>http://localhost:9490</sonar.host.url>
           </properties>
        </profile>
      </profiles>
    </settings>

    Neste arquivo "settings.xml", estou definindo as credenciais de um Servidor, cujo "id" é "github", pois eu faço "deploy" para ele. O Maven separa a URL das credenciais, por segurança. A URL dos servidores fica no "pom.xml" e as credenciais, no "settings.xml".

    Também criei um perfil ("profile") para uso do Sonar ("SonarQube"). Este perfil muda a propriedade "sonar.host.url" em todos os projetos Maven compilados sob a influência desse arquivo "settings.xml", e está ativo por default.

    Como eu disse antes, você tem um "settings"global, que fica dentro da pasta do Maven, na supasta "conf", e um "settings" Local, que fica dentro da pasta ".m2", de cada usuário do Computador. Se existirem dois "settings", um Global e um Local, o conteúdo dos dois será intercalado em todos os "builds", só que as configurações do "settings" local prevalecem sobre as do "settings" global, caso haja conflito.

    E, como vimos, você pode suprir "settings", tanto Globais, como Locais, por parâmetros de linha de comando, no momento da execução dos "builds".

    Tá, mas fala mais sobre "profiles"...

    Um "profile", ou perfil, é uma maneira de alterarmos configurações do "pom.xml", dependendo de condições específicas. O resultado é que podemos mudar: Dependências, Plugins e Propriedades, automaticamente, fazendo o "build" se ajustar às condições especificadas. Então, um "profile" serve, em última análise, para aumentarmos a "portabilidade" do nosso projeto, alterando características específicas de cada plataforma utilizada.

    Por exemplo, podemos ter um "profile" que acrescente dependências específicas de um Container Java EE, como o "tomcat", mas que seja diferente para outros, como o "JBoss". Outro exemplo de uso é mudarmos propriedades de acordo com o Sistema Operacional do Computador onde está sendo executado o "build".

    O seguinte "profile" altera o valor de uma propriedade Maven:

      <profiles>
        <profile>
           <id>sonar</id>
             <activation>
                 <activeByDefault>true</activeByDefault>
             </activation>
           <properties>
             <sonar.host.url>http://localhost:9490</sonar.host.url>
           </properties>
        </profile>
      </profiles>

    Se ele estiver ativo, o valor da propriedade "sonar.host.url" será mudado para o especificado nele. Dependendo de onde o "profile" for criado, podemos mudar várias características do projeto.

    Onde definimos "profiles"?

    Bem, podemos ter "profiles" específicos de cada Projeto, definidos dentro do "pom.xml" (parent ou filhos). E também podemos ter "profiles" Globais, definidos dentro do "settings.xml", com algumas restrições:

    • Profiles Globais: Definidos dentro do "settings.xml" Global (dentro da subpasta "cont" da instalação do Maven);
    • Profiles Por Usuário: Definidos dentro do "settings.xml" Local, de cada usuário (pasta ".m2");
    • Profiles por Projeto: Definidos dentro do "pom.xml".
    Quando definimos "profiles" por Projeto, podemos mudar várias coisas: 

    • <repositories>
    • <pluginRepositories>
    • <dependencies>
    • <plugins>
    • <properties>
    • <modules>
    • <reporting>
    • <dependencyManagement>
    • <distributionManagement>
    • Elementos do  <build>:
      • <defaultGoal>
      • <resources>
      • <testResources>
      • <finalName>
    Porém, quando definimos um "profile" dentro do "settings.xml", podemos mudar um conjunto mais restrito de características: 

    • <repositories>
    • <pluginRepositories>
    • <properties>

    E como usamos um profile?

    Um "profile"entra em uso quando é ativado. Isso pode acontecer de várias formas:

    • Sempre: Quando o "profile" é ativado por default;
    • Explicitamente: Quando nós mandamos executar o "build" ativando determinados "profiles";
    • Através do "settings.xml": Quando nós configuramos quais "profiles" devem ficar ativos;
    • Variáveis de ambiente: Dependendo de seus conteúdos;
    • OS: Dependendo do Sistema Operacional;
    • Presença de arquivos: Quando determinados arquivos existirem.

    A condição de ativação de um "profile" é dada pelo tag "<activation>". Por exemplo:

      <profiles>
        <profile>
           <id>sonar</id>
             <activation>
                 <activeByDefault>true</activeByDefault>
             </activation>
           <properties>
             <sonar.host.url>http://localhost:9490</sonar.host.url>
           </properties>
        </profile>
      </profiles>

    No exemplo anterior, o "profile" "sonar" está ativo por default.

    Vamos ver exemplos de outros tipos de ativação:

    Ativação explícita por linha de comando: 
    - Se o "profile" não tiver o tag "<activation>" ele só poderá ser ativado explícitamente. Eis um exemplo:

      <profiles>
        <profile>
           <id>sonar</id>
           <properties>
             <sonar.host.url>http://localhost:9490</sonar.host.url>
           </properties>
        </profile>
      </profiles>

    "mvn -P sonar package"

    Podemos ativar uma série de profiles, informando seus nomes entre vírgulas. Para desativar profiles explicitamente, usamos uma exclamação (negação) antes de seu nome:

    "mvn -P !sonar package"

    No "Eclipse" podemos ativar "profiles" escrevendo seus nomes no campo "Profiles", da janela "Edit Configuration". É só selecionar o Projeto e, com o botão direito, selecionar "Run As / Maven Build...":



    Ativação explícita por "settings.xml":
    O "settings.xml" tem um tag "<activeProfiles>" que nos permite listar quais "profiles" deverão ser ativados:

    <settings>
      ...
      <activeProfiles>
        <activeProfile>tomcat</activeProfile>
      </activeProfiles>
      ...
    </settings>

    Ativação quando uma propriedade tiver um valor específico:
    Podemos ativar um "profile" sempre que uma propriedade for especificada com determinado valor:

          <activation>
            <property>
              <name>versaorelease</name>
              <value>true</value>
            </property>
          </activation>

    A propriedade pode ser definida dentro do "pom.xml" ou pode ser fornecida como uma propriedade Java, por exemplo:

    "mvn -Dversaorelease=true clean deploy"


    Ativação dependendo de variável de ambiente:
    Por exemplo, vamos supor que nós criamos a seguinte variável de ambiente:

    export $TESTE=false
    ou 
    set %TESTE%=false

    Podemos ativar um "profile" baseado nela desta maneira:

          <activation>
            <property>
              <name>env.TESTE</name>
              <value>true</value>
            </property>
          </activation>

    No exemplo acima, o "profile" não estará ativo, pois o valor da variável foi definido como "false". Se quisermos ativá-lo, basta definir como "true".

    Ativação dependendo da versão do JDK: 
    Podemos ativar um "profile" baseado na versão do Java JDK que esteja configurada:

          <activation>
              <jdk>1.6</jdk>
         </activation>

    Ativação dependendo do Sistema Operacional: 
    Podemos ativar um "profile" dependendo do sistema operacional, por exemplo: 

        <activation>
          <os>
            <name>Windows XP</name>
            <family>Windows</family>
            <arch>x86</arch>
            <version>5.1.2600</version>
          </os>

        </activation>

    O "profile" será ativado se o sistema operacional tiver as características listadas.

    Existem mais condições de ativação, que você pode consultar AQUI


    Exercício

    Ok, já chega! Essa lição foi muito grande e você já deve estar de saco cheio de ficar lendo coisas... Vambora fazer um exercício: 

    Crie um projeto modular com herança, e pelo menos dois módulos. Estes projetos deverão ter: 

    • Pelo menos um profile ativo, pode ser definido no "settings.xml" ou no "pom.xml" do projeto pai;
    • Pelo menos uma propriedade;
    Verifique, em cada módulo gerado, o POM efetivo! Veja se o profile está ativo e se ele herdou dependências do projeto pai. E faça o "build" principal a partir do projeto agregador!


    Se você jogar a toalha, veja AQUI a resposta. 

    Acompanhe as lições do curso Maven Pro:

    http://www.obomprogramador.com/p/cursos.html

    Aproveite e use o Maven profissionalmente

    http://www.obomprogramador.com/p/livros.html#qualidade









    Nenhum comentário:

    Postar um comentário