quinta-feira, 22 de maio de 2014

Então, você tem problemas com Maven?




Então, você tem problemas com um projeto Maven?
Cara, o Maven é uma ferramenta de gestão de compilação muito estável e utilizada no mundo inteiro. Provavelmente, é você que fez algo errado. Assuma isso ANTES de culpar o Maven. Mas vou te dar umas dicas simples que poderão aliviar seu sofrimento...


É claro que estou assumindo que você já conhece Maven, ou seja, fez algum treinamento e/ou já tem alguma experiência com ele. Se não é o caso, então pare tudo e vá estudar, pois o uso de Maven não é simples nem intuitivo. É necessária alguma capacitação e experiência.

Em minha experiência, a esmagadora maioria dos “problemas com Maven” que eu já vi, foram resolvidas seguindo esses passos.

1 – Seus projetos e dependências estão com versões corretas?


Entenda que, se você está desenvolvendo alguma coisa, ela deve ter a versão snapshot (“x.x.x-SNAPSHOT). Snapshots são sempre associados a um “build timestamp” e a última versão é controlada melos metadados do Repositório.

Se você está fazendo uma manutenção corretiva ou evolutiva, o procedimento correto é:

a) Determine qual será a nova versão do artefato:

  • Major: Grandes mudanças, que implicam em novas funcionalidades. Por exemplo: de 1.5.3 para 2.0.0;
  • Minor: Muitas correções de bug, adaptações, mas muitas novidades. Por exemplo: de 4.3.3 para 4.4.0;
  • Release: Pequenos bug fixes ou adaptações. Por exemplo: de 2.2.1 para 2.2.2;

b) Crie a nova versão como Snapshot. Por exemplo, se você vai atualizar o artefato XPTO, versão 1.2.7 para a versão 1.3.0, então a versão do XPTO será: 1.3.0-SNAPSHOT;

c) Se houver atualização nas dependências do artefato em que você está trabalhando, siga (ou peça para seguirem) o mesmo procedimento anterior;

d) Use “properties” maven para especificar as versões do seu projeto e das suas dependências. Assim, fica mais fácil alterar tudo em um só lugar;


2 – Você está “engravidando” o projeto com “libs”?




Os caras que criaram o Maven quiseram ser “politicamente corretos” e mantiveram as maldidas “system scoped dependencies” até no Maven 3. Na verdade, são uma excressência que deveria ser removida da face da Terra.

Dependências com escopo “system” não ficam armazenadas no Repositório Maven, logo, não são administradas por ele. É muito comum as pessas criarem pastas “lib” dentro do projeto, copiarem arquivos “jar” para lá e criarem dependências com escopo “system”, apontando para o caminho dentro do projeto.

Isso é exatamente o comportamento para o qual o Maven foi criado para combater. Dependências “system” não possuem controle de versão e de origem, logo, cada desenvolvedor pode estar usando uma versão diferente.

Ao invés disso, faça o seguinte:

a) Verifique se a dependência já está no Maven Repository. Use o site “mvnrepository.com” e pesquise por ela. Talvez já esteja lá e com várias versões para você escolher;

b) Se não existir, tente falar com quem criou o projeto, de modo que o transforme em projeto Maven e, de preferência, coloque em um repositório;

c) Se não for possível, use o plugin “install”: “mvn install:install-file” para instalá-lo em um repositório. Se você usa um repositório centralizado, peça ao seu administrador para que instale o Jar nele;

3 – Você está usando um repositório de artefatos?


Usar o Maven sem usar um repositório é a mesma coisa que “comer bala com papel”. A grande vantagem do Maven é a centralização das dependências, com controle de versão e origem. Isso só pode ser alcançado se você usar um repositório. Confiar apenas nos repositórios externos pode ser problemático, pois, se você tiver um problema com a Internet, poderá ficar sem dar “build”.


O Maven cria um repositório no seu Computador automaticamente. Se você não fizer “besteira” na instalação, ele ficará na pasta “.m2”, que, dependendo do seu sistema operacional, fica em:

  • “C:\Users\<usuário>\.m2”, em sistemas Windows;
  • “/home/<usuário>/.m2”, em sistemas Linux;


Esse repositório é bom se você trabalha sozinho. Ele é individual e NUNCA deve ser copiado para outros desenvolvedores. Essa é uma prática ruim e que, geralmente, resulta em problemas.

Se você trabalha com uma Equipe, considere usar um repositório interno (não confunda com o repositório local da sua máquina), que pode ser gerenciado por um software como o NEXUS (eu não recomendo o uso do Apache Archiva).


Você deve usar seu repositório interno para:

a) Servir como “mirror” dos repositórios externos, fazendo um “cache” interno das suas dependências. Isso deve ser configurado no arquivo “settings.xml” da sua máquina, no tag “<mirror>”. Com isso, o seu Maven sempre buscará no repositório interno ANTES de ir no externo. Por exemplo, para fazer cache de TODOS


<mirrors>

    <mirror>

      <id>internal-repository</id>

      <name>Maven Repository Manager running on repo.mycompany.com</name>

      <url>http://repo.mycompany.com/proxy</url>

      <mirrorOf>*</mirrorOf>

    </mirror>

  </mirrors>


b) Armazenar os artefatos que você (ou sua empresa) cria. Assim, os outros projetos poderão buscar seus artefatos na versão correta, com segurança. Para isso, é necessário configurar seu arquivo “settings.xml” e os arquivos “pom.xml” dos projetos:

No “pom.xml”, configure o tag “<distributionManagement>”, informando o(s) repositório(s) para o qual você quer fazer deploy. Tenha certeza de separar repositórios para artefatos finalizados (“internal”) e repositórios para artefatos em manutenção (“snapshots”):


<distributionManagement>

    <repository>

      <uniqueVersion>false</uniqueVersion>

      <id>corp1</id>

      <name>Corporate Repository</name>

      <url>scp://repo/maven2</url>

      <layout>default</layout>

    </repository>

    <snapshotRepository>

      <uniqueVersion>true</uniqueVersion>

      <id>propSnap</id>

      <name>Propellors Snapshots</name>

      <url>sftp://propellers.net/maven</url>

      <layout>legacy</layout>

    </snapshotRepository>

    ...

  </distributionManagement>


Configure seu arquivo “settings.xml” para atribuir permissão de gravação no respectivo repositório, caso contrário, poderá tomar um erro “not authorized” ao tentar rodar o comando “mvn deploy”:


  <servers>

    <server>

      <id>corp1</id>

      <username>my_login</username>

      <password>my_password</password>

    </server>

  </servers>


Configure a frequência com a qual o Maven deve verificar novas versões de artefatos nos repositórios Externos e Internos. Para isso, no “pom.xml”, configure os tags “<repository>” e “<pluginRepository>”, com a frequência desejada. Se não fizer nada, o default é “daily”, ou seja, o Maven vai comparar o metadado do artefato no Repositório Local (.m2) com o do repositório remoto (Externo ou Interno) diariamente. Se você precisa de uma frequência maior, pode mudar a configuração “updatePolicy” para: “always” (sempre) ou “interval:n” (“n” é o intervalo em minutos, por exemplo: “interval:120” para verificar a cada duas horas).

Você pode e deve declarar repositórios para snaphots. E, se você usa dependências que são snapshots, deve marcar o “updatePolicy” para “always”, como no exemplo:


      <repositories>

        <repository>

          <id>mysnapshots</id>

          <name>Internal Snapshots</name>

          <releases>

            <enabled>false</enabled>

            <updatePolicy>always</updatePolicy>

            <checksumPolicy>warn</checksumPolicy>

          </releases>

          <snapshots>

            <enabled>true</enabled>

            <updatePolicy>always</updatePolicy>

          </snapshots>

          <url>http://internal.repository.url</url>

          <layout>default</layout>

        </repository>

      </repositories>


Finalmente, procure fazer “releases” dos seus artefatos. Quando terminar o desenvolvimento, use o comando “mvn release:prepare” / “mvn release:perform” para criar a versão “internal” dos seus artefatos. Isso significa, entre outras coisas:

Que a versão ficará definitiva, por exemplo: de 1.3.7-SNAPSHOT para 1.3.7;

Que ele será instalado no repositório de componentes prontos (“internal”) e não no de componentes em desenvolvimento (“snapshots”). Verifique sua configuração de repositórios (“<repository>”) e seu “<distributionManagement>”;

Que TODAS as suas dependências também são “internal”. Um artefato não deve ser liberado para “internal” se ainda contém dependências instáveis.

4 - Você está usando o Maven correto?


Atualmente, existem duas grandes versões do Maven: 2 e 3. Em breve, haverá uma versão 4. Isso é problemático... Há diferenças entre as versões 2 e 3 que poderão causar problemas para você. Veja a relação de problemas de compatibilidade entre as duas versões, preparada pelos criadores do Maven:

https://cwiki.apache.org/confluence/display/MAVEN/Maven+3.x+Compatibility+Notes

Todos os membros da sua equipe devem estar usando a mesma versão de Maven, seja ela 2 ou 3. E, se você usa um mecanismo de Integração Contínua, tenha certeza que ele também esteja usando a mesma versão. Na verdade, essa versão do Maven deveria fazer parte do seu Documento de Arquitetura de Software.

Uma das causas de problemas é a configuração incorreta das IDEs de desenvolvimento. Por exemplo, o “eclipse” usa o plugin m2e, com um Maven “embutido” por default. Se você nada fizer, é esse o Maven que o “eclipse” vai usar para dar “build” no seu projeto. Para verificar isso, abra o menu “window / preferences / maven” e abra o item “installations”:



Até a versão “Kepler”, o padrão é usar o Maven “embutido” versão 3.0.4. Você pode adicionar uma instalação local de Maven com o botão “add” e torná-la padrão. Recomendo que você SEMPRE use uma instalação de Maven externa, na versão que seu projeto precisa usar.

Infelizmente, não é só a versão do Maven que pode te causar problemas, mas as versões dos plugins também. Por exemplo, se você usa o Maven 3, a versão do “maven-compiler-plugin” tem que ser (no mínimo) 2.0.2. Você pode verificar as versões nas duas URLs abaixo:




5 – Você só faz build através de IDEs?


IDEs são sempre uma “faca de dois gumes”. Geralmente, abstraem os “problemas” de uso do Maven, criando uma interface bonitinha, com janelinhas e iconezinhos, para que você se sinta confortável.

O problema é que, para usar o Maven, elas usam plugins próprios, os quais costuma apresentar problemas. Um exemplo clássico é o m2e (usado na IDE “eclipse”). Volta e meia, vejo equipes ficarem paralisadas por problemas com “build” e “deploy”:

Você faz “mvn clean package” ou “mvn clean install” e a versão de alguma dependência não foi atualizada;

Você atualiza, compila e, quando vai executar no Servidor acoplado à IDE (Jboss, por exemplo), a versão antiga ainda aparece ou tem um grande número de erros (o mais comum é ClassNotFound);


Isso é porque, no caso do “eclipse”, muitas informações são gravadas na pasta da “Workspace”, em arquivos e pastas ocultos, e, muitas vezes, esses arquivos “bicham” e você não consegue fazer mais nada direito. Se essas coisas “esquisitas” começarem a acontecer, faça o seguinte:

Saia da IDE, de preferência feche;

  • No Terminal (ou command prompt) rode o “build” diretamente no maven. Tenha certeza de configurar as variáveis “JAVA_HOME” (com a JDK adequada. ATENÇÃO: JDK E NÃO JRE!), e M2_HOME;
  • Copie o pacote (WAR ou EAR) da pasta “target”, para a pasta de “deploy” do servidor. Eu costumo entrar lá, apagar tudo e só depois copiar;


Além disso, eu recomendo que você SEMPRE analise os “logs” do Maven, para cada artefato que você esteja mantendo. Veja se as versões corretas foram usadas, inclusive dos Snapshots, abra o repositório local (.m2) e veja se tudo está correto.


Se tiver alguma exception, ou erro, você poderá ver mais detalhes com as opções “-e” (para mostrar os erros com detalhes) e “-X” (para mostrar informações de depuração).

6 – Você faz Integração Contínua?


Cara, na boa, Integração Contínua é a melhor saída para resolver TODOS os problemas com Maven. Sabe por que? Simplesmente, porque você terá um ambiente controlado para executar os builds do seu projeto. Logo, não ficará dependendo das idiossincrasias dos ambientes dos vários desenvolvedores.


Não é muito complicado fazer isso... Para começar, você deve ter seu projeto armazenado em um repositório SCM (Software Configuration Management), como o SVN, o Git ou o CVS. Então, você configura um Servidor de IC para:

  • Recuperar os fontes do seu projeto. Pode ser o “trunk”, ou um “branch” ou mesmo um “tag” específico, para o qual você quer fazer integração;
  • Compilar tudo;
  • Gerar relatórios do projeto (mvn site);
  • Executar testes e obter métricas;
  • Instalar em um repositório interno.


Você pode agendar os builds para ocorrerem periodicamente ou sob demanda. Por exemplo, um build diário pode revelar problemas de integração com os vários componentes do seu sistema. Um build sob demanda pode instalar o artefato no ambiente de produção etc.

Eu recomendo o Jenkins, que é utilizado até mesmo por empresas que produzem software para vender.


Usando um repositório interno, com “mirror”, um servidor de SCM, e um servidor de IC, você terá sempre versões confiáveis do seu projeto, que poderão ser instaladas em um ambiente de teste funcional, teste de aceitação, teste de carga ou mesmo em produção.