Saturday, July 20, 2013

Testes integrados com JUnit, Maven e Jetty

Nesse post descrevo uma alternativa para configurar e executar testes unitários e integrados com JUnit através do Maven. O Maven, em seu ciclo de build, define a fase (phase) integration-test. Essa fase realiza os procedimentos do deploy com a possibilidade de executar rotinas de testes após a implantação do artefato.

O contexto que utilizo aqui é de uma aplicação web com serviços REST. Nesse caso seria útil implementar testes de integração para testar o funcionamento desses serviços REST. É possível no mesmo projeto, dentro do diretório src/test/java implementar rotinas de testes unitários e integrados. No Maven podemos segmentar os tipos de testes, na configuração do plugin maven-surefire-plugin. No pom.xml indicamos quais são os testes que devem ser executados após o deploy, na fase integration-test.

A seguir o techo do pom.xml, aonde configuro o plugin, indicando quais são os componentes de teste unitário e quais são de testes integrados.
<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">
...
  <build>
    ...

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.15</version>
        <configuration>
          <excludes>
            <exclude>**/integration/*.java</exclude>
          </excludes>
       </configuration>
       <executions>
         <execution>
            <id>integration-test</id>
            <goals>
              <goal>test</goal>
            </goals> 
            <phase>integration-test</phase>
            <configuration>
              <excludes>
                <exclude>none</exclude>
              </excludes>
              <includes>
                <include>**/integration/*.java</include>
              </includes>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

...
</project>

A estratégia foi criar um pacote com conteúdo integration, e nele armazenar os componentes que implementam os testes de integração. Para executar os testes integrados bastar executar a instrução:
mvn integration-test

O build do Maven valida, compila, faz o pacote, realiza o deploy do pacote e por fim processa os testes integrados. Compartilhei no meu github um projeto, uma aplicação web c/ RESTEasy, que utiliza essa alternativa para processar testes integrados com Maven.

http://twitter.com/edermag
http://www.yaw.com.br

Wednesday, July 17, 2013

Maven e Jetty uma excelente combinação p/ desenv web

Minhas experiências no desenvolvimento de projetos web com Maven e Jetty, até agora, foram positivas. Essa combinação é eficiente e produtiva, favorece o fluxo de trabalho codifica + compila + executa + testa.

Trabalhar com o Jetty, em um projeto gerenciado pelo Maven, é algo bem simples. Basta configurar o pom.xml com o plugin jetty-maven-plugin. O trecho a seguir é um exemplo de como habilitar o plugin no projeto:

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

  ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>8.1.11.v20130520</version>
      </plugin>
    </plugins>
  </build>

</project>

Após configurar o Maven, existem várias formas para trabalhar com o Jetty. Um dos goals mais interessantes é o jetty:run, com ele o Maven roda o projeto sem gerar o war, ou seja, os fontes são implantados "diretamente" no Jetty:
mvn jetty:run

Depois dessa instrução o Jetty é inicializado (por default na porta 8080) e os fontes do projeto são implantados, acessível pela url http://localhost:8080/. É possível encerrar a execução do Jetty com ctrl-c na console, ou em alguma outra aba acionar o goal jetty:stop, veja:
mvn jetty:stop

Essa estratégia é interessante para produtividade durante o desenvolvimento, caso alguma classe seja modificada o plugin do Jetty implanta a mudança, realiza o hot deploy.

Outra alternativa é executar o Jetty e realizar o deploy do war, após a geração do pacote pelo Maven.
mvn jetty:run-war

Essa abordagem faz sentido quando não é necessário realizar hot deploy de mudanças após o deploy no Jetty.

O plugin suporta diversas configurações, como por exemplo a porta de execução do Jetty e tempo para verificar fontes (hot deploy). Veja um exemplo do plugin com configurações complementares:
...
  <plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>8.1.11.v20130520</version>
    <configuration>
      <scanIntervalSeconds>10</scanIntervalSeconds>
      <webApp>
        <!-- Contexto da aplicação -->
        <contextPath>/${project.artifactId}</contextPath>
      </webApp>
      <connectors>
        <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
          <!-- Porta do Jetty -->
          <port>8000</port>
          <maxIdleTime>60000</maxIdleTime>
        </connector>
      </connectors>
    </configuration>
  </plugin>
...

De acordo com a configuração do plugin acima, o Jetty vai rodar na porta 8000 utilizando o nome do artefato como contexto web, sendo que cada conexão com container (Socket) tem um timeout de 60 segundos. Além disso o plugin verifica modificações no código a cada 10 segundos, para realizar o hot deploy.

Compartilhei um projeto Java web no github, que demonstra como funciona a integração do Maven com o Jetty. Saiba mais detalhes sobre o jetty-maven-plugin, na página do plugin.

http://twitter.com/edermag
http://www.yaw.com.br

Wednesday, July 10, 2013

Configurar o Datasource do MySQL no Jetty

O Jetty é um web server e servlet container, com ele é possível servir conteúdo estático e/ou dinâmico (Java EE Web). Trata-se de um projeto open source, atualmente mantido pela fundação Eclipse, uma solução escolhida por muitos desenvolvedores pela simplicidade e eficiência.

No Jetty é possível configurar datasource com o banco de dados de várias formas. Aqui nesse post, vou descrever os passos para realizar a configuração do datasource do MySQL no Jetty, e como acessá-lo via JPA/Hibernate.

Uma boa prática é utilizar um pooling de conexões com o banco de dados na definição de datasources, otimizando o consumo de memória e velocidade de requisições com o banco. Por isso vou utilizar um plugin c3p0 com Jetty na configuração do datasource.

A estratégia é adicionar um novo arquivo de configuração, xml, na pasta WEB-INF do projeto web. O nome do arquivo com a configuração do datasource é jetty-env.xml. Veja o exemplo
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure 
  PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" 
  "http://jetty.mortbay.org/configure.dtd">

<Configure id="wac" class="org.eclipse.jetty.webapp.WebAppContext">

  <!-- MySQL DataSource -->
  <New id="MysqlDS" class="org.eclipse.jetty.plus.jndi.Resource">
    <Arg></Arg>
    <Arg>jdbc/MysqlDS</Arg>
    <Arg>
      <New class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <Set name="driverClass">com.mysql.jdbc.Driver</Set>
        <Set name="jdbcUrl">jdbc:mysql://localhost:3306/db</Set> <!--url jdbc-->
        <Set name="user">root</Set> <!-- usuario do banco de dados -->
        <Set name="password">root</Set> <!-- senha do banco de dados -->
      </New>
    </Arg>
  </New>
</Configure>

Note que que no jetty-env.xml defino o JNDI name jdbc/MySQL, configure as as credenciais e a url jdbc de acordo com sua instalação mySQL. Agora é necessário fazer a busca do datasource pelo JNDI name configurado, a seguir o exemplo de como fazer em isso em JPA, no arquivo persistence.xml (JPA):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="estoqueUnit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <non-jta-data-source>jdbc/MysqlDS</non-jta-data-source>
    <properties>
      <!-- configurações do Hibernate -->
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
      
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>
  </persistence-unit>
</persistence>

Um detalhe importante, é necessário garantir os jars do driver JDBC MySQL e c3p0 na distribuição do projeto web (WEB-INF/lib). No Maven basta apenas adicionar as duas dependências no pom.xml, como a seguir:
<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">

  ...

  <dependencies>
    ...
    <!-- MySQL JDBC -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.25</version>
    </dependency>

    <!-- C3p0 -->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
    ...
  </dependencies>

  ...
</project>

Mais detalhes na documentação do Jetty.

http://twitter.com/edermag
http://www.yaw.com.br