Processo de build com o Maven

Por Lucas Cavalcanti em 07/07/08

O Maven é uma ferramenta de gerenciamento, construção e implantação de projetos muito interessante, que te ajuda no processo de gerenciamento de dependências e no de build, geração de relatórios e de documentação. Na Caelum esta é a ferramenta usada em todos os projetos internos e nas consultorias.

Muitas pessoas migram seus projetos para o Maven, mas acabam arrumando mais problemas que soluções, pois não conseguem configurá-lo corretamente, e acabam desistindo e fazendo tudo na mão, ou voltando para o Ant. Mas se você conseguir ajustar as configurações, o Maven vai te ajudar muito e vai compensar todos os (poucos) problemas que ele eventualmente causa. No início do uso do Maven, espere formar com ele uma relação de amor e ódio.

Para começar a usar o Maven, tudo o que você precisa fazer é baixá-lo e configurar umas poucas variáveis de ambiente. Depois de ter feito isso, é só digitar mvn [target] na linha de comando. Alguns sistemas operacionais já te oferecem essa instalação através do macport ou apt-get.

A unidade básica de configuração do Maven é um arquivo chamado pom.xml, que deve ficar na raiz do seu projeto. Ele é um arquivo conhecido como Project Object Model: lá você declara a estrutura, dependências e características do seu projeto. A idéia é bem parecida com o build.xml do Ant: você deixa o pom.xml na raiz do seu projeto para poder chamar as targets de build do seu projeto. O menor arquivo pom.xml válido é o seguinte:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>br.com.caelum</groupId>
  <artifactId>teste</artifactId>
  <version>1.0</version>
</project>

Que contém apenas a identificação do projeto, e uma informação a mais: modelVersion, que é a identificação da versão do arquivo pom.xml e deve ser sempre 4.0.0. A identificação do projeto consiste em três informações:

  • groupId: um identificador da empresa/grupo ao qual o projeto pertence. Geralmente o nome do site da empresa/grupo ao contrário. Ex: br.com.caelum.
  • artifactId: o nome do projeto. Ex: teste.
  • version: a versão atual do projeto. Ex: 1.0-SNAPSHOT.

Essas informações são usadas em muitos lugares, ccomo o controle de dependências que é, na minha opinião, a funcionalidade mais útil do Maven. Por exemplo, para dizer que o log4j 1.2.15 é uma dependência da sua aplicação é só acrescentar no seu pom as linhas:

<project>
...
  <dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.15</version>
    </dependency>
  </dependencies>
...
</project>

Quando necessário, o Maven vai baixar pra você o jar do log4j 1.2.15, e todas as suas dependências, e vai colocá-las no classpath da sua aplicação durante os builds, testes, etc. Ou seja, você não precisa mais entrar no site do log4j, baixar um zip com vários jars e ter que procurar quais jars devem ser colocados no classpath! No Repositório de Bibliotecas do Maven você encontra os jars que você pode colocar como dependência do seu projeto, e o pedaço de xml que você deve copiar e colar dentro da tag dependencies do seu pom para incluir essas bibliotecas.

Todos os jars baixados pelo Maven são guardados na pasta repository dentro da M2_HOME que você configurou quando instalou o Maven. Assim, se mais de um projeto seu depende do mesmo jar, ele não é baixado de novo.

A grande diferença entre o build.xml do Ant e o pom.xml do Maven é o paradigma. No Ant usamos esse XML praticamente como uma linguagem de programação, onde você da comandos em relação ao build do projeto. No Maven usamos o XML para definir a estrutura do projeto, e a partir dessas declarações o Maven possui targets bem definidos que usam essas informações para saber como realizar aquela tarefa. Um exemplo: para compilar com o Ant criamos um target que chama o javac, mas para compilar com o Maven usamos um target já existente (não o criamos), e ele vai usar a informação que define onde está o código fonte e para onde ele deve ser compilado (sendo que muitas dessas informações possuem convenções e defaults, e nem precisam ser configuradas).

Além dos principais targets do Maven, você pode executar targets de plugins. Você só precisa digitar na linha de comando:

mvn [nomedoplugin]:[target]

e então o Maven baixa o plugin, se necessário, e executa a target pra você. Existe uma lista bem grande de plugins do Maven e uma boa parte desses plugins podem ser usados sem nenhuma configuração adicional no seu pom.

Para dar um exemplo de plugin do Maven nada melhor do que o plugin que cria um protótipo de projeto do Maven: o Archetype. É bem parecido com o scaffold do Ruby: ele cria um protótipo de projeto a partir de um modelo escolhido. O jeito mais fácil de usar esse plugin é digitando na linha de comando:

mvn archetype:create

E então o Archetype vai perguntar qual é o tipo de projeto que você deseja, o groupID, artifactID, version e o pacote referentes ao seu projeto. Depois disso você terá uma estrutura de projeto pronta para ser usada.
Por exemplo se você escolheu o tipo de projeto maven-archetype-quickstart, o Archetype vai criar uma estrutura de pastas parecidas com a seguinte:


teste
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- br
    |           `-- com
    |               `-- caelum
    |                   `-- teste
    |                       `-- App.java
    `-- test
        `-- java
            `-- br
                `-- com
                    `-- caelum
                        `-- teste
                            `-- AppTest.java

E então é só continuar o seu projeto a partir daí. O código de teste já vem separado do código principal, e o junit já vem como dependência da aplicação. Você também pode criar as pastas src/main/resources e src/test/resources para colocar os recursos (arquivos de configuração, de teste, e etc) do código principal e do de testes, respectivamente. Tudo que estiver dentro dessas pastas é copiado diretamente para o diretório onde as classes são compiladas, sem que seja necessário fazer nenhuma configuração adicional.

Se você, por algum motivo, não gostou da estrutura que o Maven criou, ou está querendo migrar um projeto para o Maven que não segue essa estrutura, você pode configurar os diretórios do projeto
acrescentando algumas linhas no pom:

<project>
...
<build>
    <sourceDirectory>
      ${project.basedir}/src/java/main
    </sourceDirectory>
    <testSourceDirectory>
      ${project.basedir}/src/java/test
    </testSourceDirectory>
    <resources>
          <resource>
                 <directory>
                   ${project.basedir}/src/resources/main
                 </directory>
          </resource>
    </resources>
    <testResources>
          <testResource>
                 <directory>
                   ${project.basedir}/src/resources/test
                 </directory>
          </testResource>
    </testResources>
</build>

...
</project>

Nesse exemplo o diretório principal de código e de recursos estarão em src/java/mainsrc/resources/main respectivamente, e os diretorios de teste em src/java/test e src/resources/test.

Agora com um projeto Maven já preparado, vamos para a principal funcionalidade: o build. O build do Maven é baseado no conceito de ciclo de vida: o processo de construção e distribuição da sua aplicação é dividido em partes bem definidas chamadas fases, seguindo um ciclo. O ciclo padrão é o seguinte:

  • compile - compila o código fonte do projeto
  • test - executa os testes unitários do código compilado, usando uma ferramenta de testes unitários, como o junit.
  • package - empacota o código compilado de acordo com o empacotamento escolhido, por exemplo, em JAR.
  • integration-test - processa e faz o deploy do pacote em um ambiente onde os testes de integração podem ser rodados.
  • install - instala o pacote no repositório local, para ser usado como dependência de outros projetos locais
  • deploy - feito em ambiente de integração ou de release, copia o pacote final para um repositório remoto para ser compartilhado entre desenvolvedores e projetos

Você pode invocar qualquer dessas fases na linha de comando, digitando:

mvn [fase]

Por exemplo se você digitar mvn package o Maven vai executar todas as fases anteriores do ciclo até a fase package. Uma lista completa das fases do ciclo de vida possíveis pode ser encontrada aqui.

Algumas das fases do ciclo possuem plugins associadas a elas, e esses plugins são executados assim que a fase é chamada para ser executada. Você pode também registrar plugins para rodarem em qualquer fase do ciclo, conseguindo, assim, personalizar o build do seu projeto facilmente. Por exemplo, se você quiser criar um jar com o código fonte do projeto, e que esse jar seja gerado depois que o projeto foi empacotado, é só acrescentar no seu pom:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <executions>
          <execution>
            <id>attach-sources</id>
            <phase>package</phase>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

Assim, o plugin Source vai executar seu goal jar na fase package do ciclo de vida. É como se fosse chamado mvn source:jar quando o build passa pela fase de package. A fase package já possui um plugin associado a ela: o jar:jar (supondo que é um projeto jar), então o plugin source só será executado depois do jar:jar. Em geral se você registrar mais de um plugin pra mesma fase, eles serão executados na ordem em que eles forem declarados. O jeito de configurar o plugin para colocá-lo dentro de uma fase do ciclo geralmente está no site principal do plugin, na seção Usage.

O Maven possui ainda outras funcionalidades interessantes, como geração de relatórios. Alguns plugins também merecem uma atenção especial, como o Eclipse que gera informações de projeto para o eclipse (.classpath e .project), o Antrun que te permite executar código Ant dentro do Maven, o Cobertura que gera um relatório mostrando a cobertura de testes no seu projeto, o Jetty que sobe uma instância do Jetty com sua aplicação deployed, o Selenium que sobe uma instância do servidor do Selenium para poder fazer os testes de aceitação do selenium, enfim, existem vários plugins interessantes e é relativamente fácil achar o plugin que faz o que você precisa. É igualmente fácil, também, fazer um plugin para o Maven, o chamado Mojo.

Aqui na Caelum, além do Maven e JUnit, usamos muito o Selenium, juntamente com o SeleniumDSL, para os testes de integração, e o Cruise Control para o controle da integração contínua. Esperamos colocar tutoriais e vídeos sobre essas ferramentas também.

Caelum Stella - o cinto de utilidades para o desenvolvedor brasileiro

Por Fabio Kung em 21/05/08

stellaDurante o Falando em Java 2008 do último fim de semana (18/05/2008), anunciamos o lançamento do novo Caelum Stella.

O projeto vem para auxiliar os desenvolvedores brasileiros, suprindo algumas das necessidades comumente encontradas em aplicações desenvolvidas aqui no Brasil. Atualmente, o Caelum Stella fornece uma biblioteca de validadores, formatadores e conversores para documentos brasileiros, tais como CPF, CNPJ e PIS/PASEP.

 String cpf = "867.554.707-24";
 CPFValidator vld = new CPFValidator();
 for(ValidationMessage error : vld.invalidMessagesFor(cpf)) {
   System.out.println(error.getMessage());
 }

Há uma alternativa que lança uma exceção caso ocorra algum problema de validação:

 new CPFValidator().assertValid("867.554.707-24");

O Stella também inclui módulos extras, como o de geração de boletos bancários, adaptadores para JSF, VRaptor, JBoss Seam e Hibernate Validator. Veja um exemplo de validação para CPFs usando o Caelum Stella junto ao Hibernate Validator:

  @Entity
  public class Modelo {
    @CPF
    private String cpf;
  
    public String getCpf() {
      return cpf;
    }
  }

O módulo Stella Faces conta com alguns validadores compatíveis com a especificação JSF, que você pode adicionar aos seus componentes:


<h:inputText id="cpf" value="#{usuarioBean.cpf}">
  <stella:validateCPF/>
</h:inputText>

O Stella Boleto procura fornecer um idioma mais fluente para a geração de boletos, através do encadeamento de métodos, gerando PDFs, PNGs e em breve TXT, RTF e HTML:

  Boleto boleto = Boleto.newBoleto()
      .withBanco(banco).withDatas(datas)
      .withDescricoes("descricao 1""descricao 2""descricao 3")
      .withEmissor(emissor).withSacado(sacado)
      .withValorBoleto("200.00").withNoDocumento("1234")
      .withInstrucoes("instrucao 1""instrucao 2""instrucao 3")
      .withLocaisDePagamento("local 1""local 2");

  new BoletoGenerator(boleto).toPNG("teste.png");

Estão ainda previstas no roadmap do projeto funcionalidades como JSP taglibs, rotinas JavaScript para máscaras, validação e suporte a formulários, seleção de cidades dependente da seleção de estados, suporte a mais documentos, geração da nota fiscal eletrônica, webservices para busca de endereços através de CEP, entre muitas outras. A lista vem sendo constantemente atualizada e você pode conferi-la através deste link.

Todas estas funcionalidades estão divididas em diversos módulos dentro do Stella. Atualmente são quatro: Stella Core, Stella Hibernate, Stella Faces e Stella Boleto. Cada um com um propósito diferente, mas todos relacionados aos problemas do dia a dia recorrentes no mercado brasileiro.

Além de facilitar a vida dos desenvolvedores brasileiros, o projeto prima pela alta qualidade (extensa quantidade de testes unitários, cobertura e documentação) e facilidade de uso. Você pode conferir diversas características técnicas do projeto e dos vários módulos na página técnica, gerada pelo maven. Lá você encontra a lista de responsáveis (e respectivos emails), a ótima cobertura dos testes para cada um dos módulos e o código fonte navegável para cada um dos módulos.

Como de costume em qualquer projeto open-source, o código fonte está disponível em um repositório SVN (subversion) no sourceforge.net. Para baixar os fontes basta usar seu cliente preferido:

svn checkout http://caelum-stella.svn.sourceforge.net/svnroot/caelum-stella/trunk

Você também pode navegar pelo repositório neste link.

Para tirar as suas dúvidas, sugerir funcionalidades, apontar bugs e discutir sobre o projeto, não deixe de assinar as listas de discussão, que podem ser encontradas aqui. Se preferir, pode postar também no GUJ.

Visite, use, comente e participe do desenvolvimento do projeto!

stella.caelum.com.br

Testes unitários com JMock 2

Por Lucas Cavalcanti em 17/03/08

Podemos definir teste unitário de uma classe como um teste em que verificamos uma funcionalidade da classe em questão passando o mínimo possível por outras classes do sistema, ou que sejam dependências do sistema.

Por mais desacoplada que seja nossa classe, se ela tiver um mínimo de complexidade, ela vai precisar de funcionalidades de outras classe, ou seja, ela vai ter dependências. E essas dependências sempre nos atrapalham na hora de fazermos os testes unitários da classe.

Por exemplo, se uma das dependências da nossa classe é a interface HttpServletRequest, o que vamos passar pra ela na hora do teste? null, e levar uma NullPointerException? Desenterrar a biblioteca e passar uma implementação de verdade? Claro que não.

Em testes unitários não estamos interessados no comportamento real das dependências da classe, mas em como a classe em questão se comporta diante das possíveis respostas das dependências, ou então se a classe modificou as dependências da maneira esperada.

Então é comum passarmos implementações falsas (mock objects) das dependências da classe, retornando valores pertinentes para conseguirmos testá-la satisfatoriamente. Esses mocks costumam poluir seu código de testes com várias classes que só são usadas em poucos testes. E o problema fica pior quando a interface da dependência possui muitos métodos mas só usamos um ou dois deles (é o caso do HttpServletRequest). É nessa hora que o JMock vem para facilitar a nossa vida.

O JMock é uma biblioteca que auxilia o Test Driven Development através dos mock objects. É uma biblioteca que vai criar implementações de mentira específicas para o seu teste, de uma maneira rápida e simples, sem ter que se preocupar com os métodos que não vamos usar no teste, sem ao menos ficar criando classes “a toa”. Com o JMock podemos definir o comportamento necessário do objeto de mentira, para criarmos a situação pedida pelo teste.

Para termos uma idéia melhor vamos ver um exemplo prático de como usar o JMock. Vamos supor que temos uma lógica de login no nosso sistema e queremos testá-la. As dependências dessa lógica são recebidas no construtor:

public class LoginLogic {
    private final HttpSession session;
    private final Authenticator auth;
    public LoginLogic (HttpSession session, Authenticator auth) {
        this
.session = session;
        this.auth = auth;
    }
    public void login(User user) {
        //…
    }
}

Vamos testar o método login, que tem duas situações possíveis: se o usuário é válido, entra na sessão, senão não entra. Para testar precisamos de um teste que vai passar um usuário válido, e garantir que o usuário entrou na sessão; e um teste que vai passar um usuário inválido e garantir que o usuário não entrou na sessão.

Para criar esses testes precisamos de uma instância da classe LoginLogic, logo precisamos nos preocupar com as dependências da classe: o HttpSession, que é uma interface do java servlet, e o Authenticator, que é uma interface do nosso projeto contendo um método chamado isValid(User) que recebe um usuário e vê se ele é válido.

Começando nosso teste:

LoginLogic logic = new LoginLogic(/*um httpSession*/, /*um authenticator*/);
logic.login(/*um user válido*/);
assertTrue(/*o usuário está na sessão*/);

Precisamos passar para nossa lógica um objeto que implementa HttpSession, e um que implementa Authenticator. Poderíamos criar implementações falsas dessas interfaces, mas não queremos poluir nosso código de testes com classes “inúteis”. Vamos, então, deixar o JMock fazer esse trabalho sujo para nós.

Para começar a criar essas implementações falsas, precisamos de uma fábrica de objetos falsos (mocks) do JMock:

Mockery mockery = new Mockery();

É comum criarmos essa fábrica no começo de cada teste, ou no setUp da sua classe de testes (se você usa o JUnit). Com a fábrica em mãos, podemos começar a mockar as nossas interfaces. É bem simples:

HttpSession session = mockery.mock(HttpSession.class);
Authenticator auth = mockery.mock(Authenticator.class);

E já podemos passar esses mocks para nosso LoginLogic. Nosso teste ficaria então:

final Mockery mockery = new Mockery();
final HttpSession session = mockery.mock(HttpSession.class);
final Authenticator auth = mockery.mock(Authenticator.class);

LoginLogic logic = new LoginLogic(session, auth);
logic.login(/*um user válido*/);
assertTrue(/*o usuário está na sessão*/);

Se fizermos simplesmente isso, o teste não vai fazer nada. Precisamos dizer para nossos mocks como vai ser o comportamento deles quando forem acessados. A maneira de fazer isso no JMock 2.4 é bem interessante, utiliza uma sintaxe um pouco incomum, mas quando você se acostuma com ela fica bastante legível:

mockery.checking(new Expectations() {{
    //comportamento dos mocks aqui
}});

Repare no truque: uma classe anônima, com um pré construtor (note os dois grupos de chaves). Esse bloco vai ser chamado pelo método checking, e vai adicionar o comportamento pedido aos mocks. Fazemos isso de uma maneira fluente bem interessante. Por exemplo, se quisermos garantir que o usuário foi colocado na sessão após o teste, fazemos:

one(session).setAttribute(“user”, with(any(User.class)));

O que essa linha quer dizer é o seguinte: eu quero que o método setAttribute seja chamado uma vez, com os parâmetros “user” e com qualquer objeto do tipo User.

Se esse método nunca for chamado com esses parâmetros ou for chamado mais de uma vez, o teste vai falhar. Na verdade, tudo que é possível acontecer com os mocks tem que estar descrito em um expectations (pode ter mais de um no teste). Se pro teste não é importante o que acontece com um dos objetos mockados, podemos ignorá-los, escrevendo:

ignoring(mockObject);

Fazendo isso, todo método chamado desse método vai retornar valores padrão (0, “”, null ou, se for possível um outro mockObject marcado como ignoring). Mas para que tudo isso aconteça, precisamos colocar no final do nosso teste a seguinte linha:

mockery.assertIsSatisfied();

Bom, ainda não terminamos o nosso teste, precisamos fazer com que o usuário seja válido, ou seja, precisamos garantir que o método isValid() do authenticator retorne true. Fazemos isso da seguinte maneira, dentro do expectations:

one(auth).isValid(with(any(User.class)));
will(returnValue(true));

Fica bastante legível: o método isValid do mock auth será chamado uma vez, com qualquer User como parâmetro, e vai retornar o valor true.

O nosso teste completo ficaria, então:

final Mockery mockery = new Mockery();
final HttpSession session = mockery.mock(HttpSession.class);
final Authenticator auth = mockery.mock(Authenticator.class)

mockery.checking(new Expectations() {{
    one(session).setAttribute(“user”, with(any(User.class)));
    one(auth).isValid(with(any(User.class)));
    will(returnValue(true));
}});

LoginLogic logic = new LoginLogic(session, auth);
logic.login(new User());

mockery.assertIsSatisfied();

Note que poderíamos garantir que o user passado para os métodos foi o mesmo que passamos para a lógica, apenas colocando ele como parâmetro para o método. E para fazer o teste do usuário inválido só precisamos mudar o conteúdo do expectations, substituindo por:

never(session).setAttribute(“user”, with(any(User.class)));
one(auth).isValid(with(any(User.class)));
will(returnValue(false));

Ou seja, o isValid vai retornar false, e o método setAttibute nunca vai ser chamado. Se isso acontecer, o teste passa.

O roteiro do expectations não precisa estar na ordem em que ele vai acontecer no seu método (por exemplo o isValid provavelmente vai ser chamado antes do setAttribute), mas tem um jeito de você garantir a ordem dos comandos.

Por padrão o JMock só é capaz de mockar interfaces, mas você pode configurá-lo para mockar classes.

Você pode, ainda, usar o JMock para fazer testes de integração. Como o roteiro do teste pode ficar bastante complexo, você pode utilizar uma máquina de estados para adicionar um pouco mais de poder ao roteiro (por exemplo garantir que uma chamada de método sempre ocorra depois de uma outra).

Uma chamada genérica num expectations, então, seria:

invocation-count (mock-object).method(argument-constraints);
inSequence(sequence-name);
when(state-machine.is(state-name));
will(action);
then(state-machine.is(new-state-name));

Se você quiser usar todo o poder do JMock, dê uma olhada na sua documentação. E, claro, não abuse de mocks colocando-os em todas as situações, senão seu teste unitário pode perder o valor de testar uma pequena unidade.

Os 7 hábitos dos desenvolvedores de WebServices altamente eficazes

Por Paulo Silveira em 18/02/08

Esta semana tive o prazer de palestrar no Café Com Tapioca, evento do CeJUG realizado em Fortaleza. Estavam presentes dois conhecidos evangelistas da Sun: Reggie Hutcherson e Simon Ritter.


DSC01766 DSC01773
DSC01770 DSC01763

O agradecimento fica ao Rafael Carneiro, do PortalJava e do CeJUG, pela organização geral do evento. Agradeço também à equipe do grupo Fortes pela minha vinda ao Ceará: ao Clavius Tales, Igo Coelho, Antônio Israel, Ronaldo Moreira, Tiago Moraes, Rodrigo Maia e tantos outros. Vale reparar como a comunidade java cearense é ativa nos blogs! Nesse sábado também ministrei umworkshop sobre Arquitetura e Design Java aqui em Fortaleza.

Durante esses dias aqui, muitas pessoas perguntaram bastante sobre webservices: em especial JSON e Rest. Compilei alguns dos pontos que foram recorrentes nas discussões e considerados como boas práticas:

Cuidado com a granularidade - a granularidade do seu serviço não pode ser muito fina, caso contrário seus seviços sofrerão dos mesmos problemas que o EntityBean do EJB2 exposto remotamente: uma enorme quantidade de requisições serão disparadas para executar pequenas tarefas, como getters! Seus serviços devem realizar uma quantidade significativa de tarefas, para evitar um número alto de roundtrips!

Exponha serviços, não dados - é comum ouvir a frase “Vou criar webservices para expor os meus dados“. O grande perigo aqui é deixar toda a lógica de negócio na mão do cliente, o que descentralizará seu serviço e forçará o cliente a realizar muitas requisições ao servidor. Devemos expor serviços, e não dados em sua forma mais crua.

Nunca parsear WSDL/SOAP manualmente - antes das ferramentas que trabalham com webservices terem atingido sua maturidade, era muito comum ver por aí as pessoas gerando o SOAP manualmente, através de bibliotecas XML ou às vezes até mesmo concatenando String com o uso de StringBuffer! O SOAP é o protocolo de comunicação, e assim como quando você usa RMI/CORBA e você não enxerga absolutamente nada do JRMP/IIOP, você nunca deveria ter contato direto com o protocolo de comunicação! O SOAP e o WSDL devem ser utilizados por ferramentas, e não pela sua própria aplicação. Hoje em dia qualquer ferramenta como o Apache Axis, Apache CXF, ou mesmo o wsimport, que já vem no JDK 6, auxilia nossa tarefa de gerar Stubs que sabem trabalhar com o SOAP, sem que você nem mesmo precise ve-lo um dia. Se você está usando um servidor de aplicação, esta tarefa é ainda mais fácil, até mesmo para fazer o deploy do serviço.

Não enviar XML dentro de XML - outra prática comumente encontrada em aplicações antigas que usam Webservices: dentro do SOAP é enviado um outro XML como um dos parâmetros, então mesmo usando ferramentas para gerar os stubs você fica com uma String que dentro dela há um XML e este precisa ser parseado por sua própria aplicação. Isto é uma má interpretação do conceito que “Webservices é comunicação via XML“. Concordo que em alguns raros casos isso possa fazer sentido, como por exemplo se o valor do que você quer realmente é um outro XML, mas no geral isto é feito sem necessidade nenhuma, como é o famoso caso do WebService MS Office dos Correios do Brasil.

Considerar protocolo binário - são muitas as reclamações de que o SOAP acaba sendo um XML grande e pesado para ser transportado. Hoje em dia há muitas formas de contornar isso, como o uso do padrão Fast InfoSet para compressão do XML. Uma outra forma seria o uso de protocolos proprietários, como o AMF da Adobe, que é uma opção comum no uso do Flex.

Considerar não usar WSDL/SOAP - pelas diversas críticas a burocracia exagerada do WSDL/SOAP, muitas pessoas estão optando por usar algo mais simples, como o bom e velho XML (POX), JSON ou até mesmo uma forma qualquer de estruturar dados. Caso você precise de simplicidade e velocidade no desenvolvimento com outras plataformas, essa é uma boa opção. Cuidado: muita gente está considerando qualquer webservice que não use WSDL/SOAP como sendo REST. Os webservices do flickr e da Amazon são um bom exemplo: tudo é via GET (as vezes POST) e não há recurso identificado pela URI. Na verdade ele usa apenas um esquema HTTP+XML (POX), considerado apenas em parte como RESTful. Você pode ver que esses webservices são muito diferentes do modelo RESTful do Atom Publishing Protocol. Criar um protocolo realmente REST como este não é fácil: porém teremos esse trabalho bastante simplificado com a JAX-RS (JSR 311), e já podemos ver isso através do Glassfish Jersey.

Considerar usar JSON - JSON é um formato que tem ganho muita popularidade. Não é a toa: além de ser um simples para debugar, parsear e gerar, com javascript basta fazer um eval em um resultado JSON que ele já estará pronto para usar. É uma excelente opção para o consumo via AJAX e criação de mashups. JSON tem ganho bastante força na comunidade Flex, tornando-o uma ótima opção como ponte entre Flex e Java. A escolha por JSON abre portas para muitos tipos diferentes de clientes, e em especial o browser, que é nossa plataforma global.

Guia rápido de migração EJB2 para EJB3

Por Paulo Silveira em 19/03/07

Migrar um sistema de EJB2 para EJB3 poderá ser um trabalho muito requisitado no futuro. A especificação do EJB3 facilita muito essa tarefa.

Considere um session bean 2.x chamado ServicoBean, com sua interface remota Servico e a interface home remota ServicoHome:

public interface ServicoHome extends EJBHome {
  Servico create() throws RemoteException, CreateException;
}

public interface Servico extends EJBObject {
  void executa() throws RemoteException;
}

public class ServicoBean implements SessionBean {

  public void executa() {
    // executa a logica de negocios
  }

  public void ejbActivate() {
  }

  public void ejbPassivate() {
  }

  public void ejbRemove() {
  }

  public void setSessionContext(SessionContext sc) {
  }
}

Repare que só poderemos alterar a nossa classe ServicoBean! A alteração das duas interfaces é inadmissível, para que não tenhamos de mudar os possíveis vários clientes desse EJB. Vamos mudar então o ServicoBean, começando por anota-lo como um @Stateless session bean e informando quais são suas interfaces (em vez de anotar as interfaces, o que é a opção mais interessante no EJB3). Além disso não implementamos mais a interface SessionBean, já que qualquer método de callback pode ser definido através de anotações:

@Stateless
@Remote(Servico.class)
@RemoteHome(ServicoHome.class)
public class ServicoBean {
}

Infelizmente aqui não podemos implementar a interface Servico, como ocorreria em um EJB3 comum, por causa de um dos mesmos motivos que um session bean 2.x não deve implementar sua interface remota: teríamos de escrever os métodos encontrados em EJBObject (além de não ter sentido semântico, mas isso foi quebrado no EJB3).

Precisamos então adicionar nossos métodos de negócio com cuidado, para não ocorrer “spec violations” durante o deploy:

@Stateless
@Remote(Servico.class)
@RemoteHome(ServicoHome.class)
public class ServicoBean  {}
  public void executa() {
    // executa a logica de negocios
  }
}

No caso de um Session Bean Stateful, teríamos ainda o ejbCreate. Para isso basta anotarmos um ou mais métodos com @Init. Esse método pode ter qualquer nome, é pela assinatura do create na Home e pela anotação que o container descobrirá qual invocar.
Pronto! Você pode usar seus clientes antigos normalmente, e agora você não precisa mais do ejb-jar.xml no jar desse seu ejb e pode contar com todos os recursos da nova especificação.

Os entity beans também podem ser facilmente migrados através dos VOs, desde que você tenha seguido as boas práticas e não os esteja acessando via interface remota.