Wednesday, February 2, 2011

Lightning Talk - gtugbh - Feb, 03 2011

What is GWT?
It's a web toolkit provided by Google. It allows developers to implement rich web applications using the Java language. A compiler converts the Java code into pure Javascript+HTML code. The generated code is browser agnostic and highly optimized.
As most web apps, GWT apps usually needs to make calls to some HTTP server to retrieve or post some data. GWT uses Ajax to do this task. There are three types of client server communication in GWT:
  • Pure HTTP calls: usually transfer data using the JSON format. The problem with JSON is that GWT client code is Java. Java is statically typed so convert JSON data into Java objects is not easy and results in ugly code.
  • GWT-RPC: uses a proprietary format to invoke server side GWT specific servlets using Java code in both sides. This looks like a better solution but the developer must be very careful about the objects transfered from/to this services because they have to be serializable according to the proprietary GWT format.
  • RequestFactory: starting at GWT 2.1 this feature improves the GWT-RPC for service oriented apps. But I think it requires lots of redundant code to expose server side objects to the client code.
What is Roo?
Spring Roo is a command line tool that uses round trip code generation and Aspect Oriented programming to create Java Enterprise Applications in a very productive way. It's hard to explain what Roo really is so I suggest you guys to take a look at this tech talk.
I think Roo can help to solve the client server communication issues related to entity types serialization and integration with server side business logic. Roo uses the new GWT 2.1 RequestFactory for client server communication. All redundant code is generated by Roo.
The GWT+Roo integration is a very new feature and I haven't tried in real World apps yet but it looks promising. Compared to other Roo stuff the GWT integration is not really aspect oriented so is hard to customize the scaffolded UI. There is no documentation on this topic and for now I think Roo team is using GWT to give some attention to the project.
I am looking forward for improvements on this Roo feature because right now looks like it only works for very simple CRUD UI's.
For more info

Monday, September 7, 2009

Using Groovy to externalize SQL queries

A very common use case for enterprise applications is to run complex SQL queries. Usually these queries are large and hard to write inside Java code. SQL queries are big Strings that should span into multiple lines so we can understand what they do. The following example shows a SQL statement inside Java code:
String sqlText =
"  select "
"    user.name "+
"    ,user.age " +
"    ,address.city "+
"  from " +
"    user, address "+
"  where " +
"    user.address_id = address.id " +
"    and user.name like ? " +
"    and address.city like ? ";
This SQL statement is far from the real world queries but it is large enough to illustrate how hard is to write these queries inside Java code.
To make things a little more complicated, imagine the query parameters are optional. If the user doesn't provide the user name or the address city the query must adapt dynamically removing the parameter from the SQL where clause. In pure Java we have one option: split the query String creation so we can test for the presence of the parameters. Something like this:
StringBuffer sqlBuffer = new StringBuffer("  select " +
"    user.name "+
"    ,user.age " +
"    ,address.city "+
"  from " +
"    user, address "+
"  where " +
"    user.address_id = address.id ");

if(paramName != null){
  sqlBuffer.append("    and user.name like ? ");
}

if(paramCity != null){
  sqlBuffer.append("    and address.city like ? ");
}

String sqlText = sqlBuffer.toString();
Now imagine large queries with lots of optional parameters: a real pain. The main problems here are:
  • Java has no support to multi line strings so we need to use ugly and error prone string concatenation.
  • When the SQL has dynamic parameters the code becomes bigger and confuse.
  • It is hard to copy and paste the SQL to test it in query tools like Oracle SQL plus.
There are some good options to solve this problem. Template engines like Velocity and Freemarker or the excellent iBatis could help. In this post I will show how Groovy can be used to create smart query strings.
Groovy provides multi line strings support so we can write queries this way:
def sqlText =
"""
select
  user.name
  ,user.age
  ,address.city
from
  user, address
where
  user.address_id = address.id
  and user.name like ?
  and address.city like ?
"""
This solves the string concatenation problem but we still have to deal with optional parameters. Thanks to groovy embedded template engine. Assuming the query parameters are provided as a java.util.Map, we can rewrite the query like this:
def sqlText =
"""
select
  user.name
  ,user.age
  ,address.city
from
  user, address
where
  user.address_id = address.id
  <% if(userName){ %>
    and user.name like ?
  <% } %>
  <% if(city){ %>
    and address.city like ?
  <% } %>
"""
As Groovy is a JVM language, we can compile Groovy classes to binary Java byte code and use them like regular Java classes. The query templates can be implemented as Groovy classes that inherits from a common base class. The base groovy class provides common template processing logic. The complete Groovy code is provided below:
import groovy.text.*

/**
* Base class for query templates
*/
public abstract class AbstractQueryTemplate{

  /**
  * This method must be implemented by subclasses to provide the SQL template text
  */
  protected abstract String getSqlTemplate();

  public String getSqlText(Map parameters){
    return translateSqlText(parameters)
  }

  private String translateSqlText(Map parameters){
    def engine = new GStringTemplateEngine()
    def template = engine.createTemplate(getSqlTemplate()).make(parameters)

    return template.toString()
  }

}
The query templates implementations inherits from the base class:
public class UserQuery extends AbstractQueryTemplate{

  def static SQL_TEXT =
  """
  select
    user.name
    ,user.age
    ,address.city
  from
    user, address
  where
    user.address_id = address.id
    <% if(userName){ %>
      and user.name like ?
    <% } %>
    <% if(city){ %>
      and address.city like ?
    <% } %>
  """

  protected String getSqlTemplate(){
    return SQL_TEXT
  }
}
After compiling the Groovy classes and putting them into our app classpath, we can use them like in the following Java snippet:
  UserQuery query = new UserQuery();
  Map parameters = new HashMap();
  parameters.put("userName", "John Doe");
  parameters.put("city", "Taipei");
  String sqlText = query.getSqlText(parameters);
As you can see, Groovy is a nice option to simplify the way we write complex SQL queries into enterprise applications.

Wednesday, May 20, 2009

Mapas em Java - Reduzindo o sofrimento

O aparecimento de linguagens modernas como o Groovy e o Ruby me levou a uma triste constatação: Java é uma linguagem completamente defasada que pouco colabora para a produtividade do programador. Antes que alguém comece a jogar pedras, quero deixar claro que estou falando da linguagem, não da JVM, mesmo porque outras linguagens como Groovy e JRuby rodam na JVM. Quando comecei a estudar Groovy, o recurso que mais me impressionou foi o excelente suporte a coleções e mapas. Estas estruturas são de uso muito frequente, mas o Java simplesmente não tem suporte nativo para elas, já que a manipulação de listas e mapas é feita via API do JDK. Se você nunca usou listas e mapas numa linguagem como Groovy ou Ruby, você provavelmente está relativamente satisfeito com a API de collections do Java. É mais ou menos como alguém que sempre teve um carro 1.0 e não sabe o quanto ele é fraco até o dia que dirige um carro 1.8. Nada melhor que um exemplo para ilustrar:
Em Groovy:
// Definindo e inicializando um mapa em Groovy
def errorMap = [
 1:"Database is down"
 ,2:"Network is down"
 ,3:"Something else"
]
Em Java:
//Mesmo mapa em Java
Map errorMap = new HashMap();

errorMap.put(1, "Database is down");
errorMap.put(2, "Network is down");
errorMap.put(3, "Something else");
Nos dois exemplos anteriores temos quatro linhas de código Java contra apenas uma do Groovy. Se o mapa for atribuído a uma variável estática, que aliás é muito comum no caso de mapas "constantes", a vantagem do Groovy fica ainda maior.
Em Groovy:
// Definindo e inicializando um mapa estático em Groovy
def static errorMap = [
 1:"Database is down"
 ,2:"Network is down"
 ,3:"Something else"
]
Em Java:
//Mesmo mapa estático em Java
static Map errorMap = new HashMap();

static{
 errorMap.put(1, "Database is down");
 errorMap.put(2, "Network is down");
 errorMap.put(3, "Something else");
}
Resumindo, o Java não permite criar e inicializar um mapa ao mesmo tempo, o que acaba levando o programador a escrever muito código simplesmente para criar e preencher um mapa. Esta deficiência em particular poderia não existir se o método Map.put() retornasse a instância do próprio Map, como faz o método StringBuffer.append(), mas infelizmente o retorno do put() é void :(
Para melhorar esta triste realidade, podemos utilizar um MapBuilder, que usa o mesmo pattern do StringBuffer. O método put() do MapBuilder retorna a própria instância do builder, o que permite encadear chamadas ao put numa única linha de código. O HashMap é criado internamente e pode ser obtido através do método toMap().
public class MapBuilder{
  private Map workMap;

  public MapBuilder(){
     workMap = new HashMap();
  }

  public MapBuilder put(Object key, Object value){
     assert key != null;

     workMap.put(key, value);

     return this;
  }

  public toMap(){
     return workMap;
  }
}
A implementação acima é muito simples e pode ser melhorada, mas o objetivo é mostrar o conceito de um map builder. O trecho de código a seguir mostra um exemplo de uso:
static Map errorMap = new MapBuilder()
 .put(1, "Database is down")
 .put(2, "Network is down")
 .put(3, "Something else")
.toMap();
O MapBuilder é uma idéia simples que pode ajudar a reduzir volume e melhorar a legibilidade do código.

Saturday, November 29, 2008

Inspecting log files with Log Factor 5

Almost every serious Java developer uses Log4j as part of their everyday tool set. Log4j is a very powerful, simple and flexible logging framework.

Most developers, myself included, uses a very small subset of Log4j features. Some nice framework capabilities remains hidden from almost everyone. For me the most useful of these hidden features is the Log Factor 5 AKA LF5. LF5 provides a special message formatter and a SWING based user interface for log files inspection. The user interface allows hierarchical category filtering and full text search. The LF5 formatter prepares log messages so they can be opened by LF5 GUI.

LF5 formatter configuration is quite simple. All you have to do is to add the following configuration to your Log4j configuration file:

log4j.rootCategory= INFO,A1

#Configure rolling file appender
log4j.appender.A1=org.apache.log4j.RollingFileAppender
log4j.appender.A1.File=log4j_lf5.log

#Configure pattern for LF5
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[slf5s.start]%d{DATE}[slf5s.DATE]%n\
%p[slf5s.PRIORITY]%n%x[slf5s.NDC]%n%t[slf5s.THREAD]%n\
%c[slf5s.CATEGORY]%n%l[slf5s.LOCATION]%n%m[slf5s.MESSAGE]%n%n

The previous configuration creates a file appender. The message format is compatible with LF5. After running any Java application (that sends messages to Log4j) using this logging configuration, the file named log4j_lf5.log will be created on the application root directory.

To inspect the log file, open the LF5 GUI using the following command (make sure the log4j jar is available in the classpath):

$ java org.apache.log4j.lf5.StartLogFactor5

You can easily run the LF5 GUI from Eclipse using a run configuration pointing to the org.apache.log4j.lf5.StartLogFactor5 class:

Assuming everything works fine the LF5 GUI will open. Now open the log file using the Menu File|Open... and point to the file log4j_lf5.log. You will see something like the following figure:

Using the tree on left side is possible to filter the categories. It is also possible to choose the visible columns and apply filters by log level.

Using LF5 you can easily change the log scope (level and categories) using the same file as input and this is a great bonus. I hope you enjoy LF5 as much as I do.

Saturday, October 18, 2008

Modelando código testável

No mundo do desenvolvimento de software, testes unitários são aceitos por quase 100% dos profissionais, empresas e clientes como uma excelente ferramenta para o incremento da qualidade, manutenibilidade, confiabilidade e mais vários *dade que a literatura tanto gosta de enumerar. Paradoxalmente, quase ninguém aceita arcar com os custos associados à utilização deste tipo de teste quando percebe que eles não são tão triviais como são pintados pela maioria dos TDD Evangelists de plantão.
Em muitos casos, o código de um determinado teste unitário é mais extenso e/ou complexo que o código que está sendo testado. Infelizmente só nos damos conta disso quando começamos a bater cabeça. Como os projetos do mundo real nunca são tão simplórios quanto os exemplos Hello World like dos livros, e os prazos sempre são apertados, a boa intensão de criar aquela suite de testes mágica que vai verificar o código automaticamente de hora em hora, vai por água abaixo.
Outra importante causa de fracasso neste campo é que muita gente não modela o código de forma que o mesmo seja testável. Não é óbvio à primeira vista, mas nem todo código é testável. Usualmente, código bem modelado é testável, mas o desenvolvedor tem que pensar, o tempo todo, que aquela classe tem que ser testada isoladamente, livre de todas as dependências. Assim, o requisito mais importante para utilizar testes unitários como uma ferramenta efetiva, é a modelagem do código de forma que ele seja facilmente testável via frameworks como JUnit ou TestNG. Se o código não atende a este requisito, refatore-o ou esqueça os testes unitários.
Antes de dizer como modelar código testável, prefiro dizer o que torna uma classe difícil de testar:
  • Classes faz-tudo com milhares de linhas contendo métodos faz-tudo com centenas de linhas são muito difíceis de testar. Quando o código a ser testado possui uma grande quantidade de fluxos possíveis, é muito complicado configurar um código de teste que exercite todos estes fluxos. É importante que as classes tenham responsabilidades bem definidas e que elas deleguem outras responsabilidades para outras classes. Isso é modelagem OO tradicional mesmo, nada de novo.

  • Classes que referenciam outras classes (colaborators) diretamente, usando o operador new. É praticamente impossível substituir os colaborators por mocks.

  • Classes que dependem diretamente de recursos externos, tais como: conexões de rede, sistemas de arquivos, datasources, etc são uma pedra no sapato do autor de um teste unitário.

O exemplo a seguir mostra uma classe que depende que um arquivo com um certo nome exista no sistema de arquivos, o que força o pobre desenvolvedor do teste unitário a garantir que o arquivo em questão exista no mesmo diretório, exatamente com o mesmo nome usado pelo código alvo do teste:
public class ImportadorDadosVendas{
  public void importar(){
    File arquivoVendas = new File(obterNomeArquivoVendas());
    //--- Processar arquivo linha a linha, bla, bla
  }
  private String obterNomeArquivoVendas(){
    //
  }  
}
É muito simples refatorar o código acima de modo a torná-lo mais amigável a um teste unitário. Como o sistema de arquivos é um recurso externo, temos que garantir que qualquer acesso a ele seja feito indiretamente, através de uma classe auxiliar. Neste exemplo vou usar o Spring para ilustrar a aplicação da inversão de controle (IoC) neste tipo de situação. O código a seguir ilustra esta refatoração:
/**
 * Interface que define um servico de acesso indireto ao filesysten
 **/
public interface ProvedorArquivos{
  File getReferenciaArquivo(String nomeArquivo);
}

/**
 * Implementação padrão da interface ProvedorArquivos
 **/
public class ProvedorArquivosImpl implements ProvedorArquivos{
  public File getReferenciaArquivo(String nomeArquivo){
    return new File(nomeArquivo);
  }  
}

/**
 * Serviço original refatorado
 **/
public class ImportadorDadosVendas{
  private ProvedorArquivos provedorArquivos;
  public void importar(){
    //--- Referência ao arquivo passa a ser fornecida pelo objeto provedorArquivos
    File arquivoVendas = provedorArquivos.getReferenciaArquivo(obterNomeArquivoVendas());

    //--- Processar arquivo linha a linha, bla, bla
  }
  private String obterNomeArquivoVendas(){
    //
  }

  /**
   * IMPORTANTE: Este método é chamado pelo Spring, que injeta o colaborator
   * O teste unitário pode chamar este método para fornecer um mock
   **/
  public setProvedorArquivos(ProvedorArquivos provedorArquivos){
    this.provedorArquivos = provedorArquivos;
  }
}
Para o Spring, temos a seguinte configuração:

  
  
  

  
  
    
    
    
  

A classe ImportadorDadosVendas agora usa uma implementação da interface ProvedorArquivos para recuperar uma referência ao arquivo que será importado. Note também que a instância do provedor de arquivos não é criada diretamente a partir do código da classe ImportadorDadosVendas, mas é fornecida externamente pelo Spring via um setter. Esta é uma mudança crucial: as dependências não são criadas localmente, mas fornecidas externamente.
Quando a classe ImportadorDadosVendas for executada normalmente, será fornecida uma instância de ProvedorArquivosImpl via setter. Porém, quando for executada por um teste unitário, será fornecida uma implementação alternativa da interface ProvedorArquivos, com comportamento previsível dentro do contexto do teste:
public class ImportadorDadosVendas extends Testcase{
  public void testImportar(){
    ImportadorDadosVendas importador = new ImportadorDadosVenda();
    ProvedorArquivos provedorArquivos = new ProvedorArquivosMock();
    importador.setProvedorArquivos(provedorArquivos);
    importador.importar();
    //--- Asserts a partir daqui
  }

  public static class ProvedorArquivosMock implements ProvedorArquivos{
    public File getReferenciaArquivo(String novoArquivo){
      File arquivoParaTeste = File.createTempFile("testeImportacao.dat");
      //--- Popular arquivo temporario com dados para o teste
    }
  }
}
Esta abordagem pode ser aplicada a qualquer dependência da classe a ser testada. O uso de objetos “falsos”, conhecidos como mocks e stubs é uma das técnicas mais importantes para a implementação de testes unitários. Note que a referência à instância do provedor de arquivos é feita através de uma interface. Isso esconde a verdadeira implementação e facilita a substituição da classe real por um mock. Existem diversas libs de suporte à criação de mocks e stubs. Entre elas, as mais usadas são EasyMock e JMock.
O uso de frameworks de inversão de controle (AKA IoC) como Spring ou Google Guice traz uma grande contribuição para a testabilidade do código. Estes frameworks funcionam como super factories de objetos, sendo responsáveis tanto pela criação dos objetos, quanto dos relacionamentos entre eles. A chave está justamente no gerenciamento das dependências. Uma vez que as dependências são injetadas de forma transparente, via setters, o código do teste unitário pode substituir estas dependências por mocks usando os mesmos setters que normalmente seriam invocados pelo container de IoC.
Está fora do escopo deste post abordar o funcionamento dos containers IoC, mas quero enfatizar que eles são a principal ferramenta no caminho para a criação de código bem estruturado, pouco acoplado e portanto testável.
Criar suites de testes efetivas não é uma tarefa simples, mas pode ser facilitada:
  • Procure dividir a complexidade em classes menores com responsabilidade bem definida.

  • Sempre acesse colaborators através de interfaces.

  • Use frameworks de IoC como o Spring para tornar o mecanismo de injeção de dependências mais fácil de ser substituído pelo setup do código de teste.

  • Isole o acesso a recursos externos como bancos de dados e sistemas de arquivos. Referências a estes recursos devem ser fornecidas por interfaces injetadas via IoC.

Friday, August 29, 2008

Integrando Aplicações com Apache Camel

Foi-se o tempo em que a informatização de processos das organizações era feita em ilhas departamentais, quando sistemas isolados cuidavam dos diferentes aspectos do negócio. Hoje as empresas esperam que o software automatize todos os processos de negócio de forma integrada e ortogonal, esperam que a mesma informação não seja duplicada em bancos de dados diferentes, esperam que as regras de negócio não sejam duplicadas em diferentes aplicações. Infelizmente elas continuam esperando, pois ainda não é uma tarefa simples atender a todos estes requisitos.
Um passo importante nesta direção foi o advento dos Web Services, que permitem que aplicações diferentes, escritas usando os mais diversos ambientes de desenvolvimento, possam interoperar através da publicação de regras de negócio na forma de interfaces baseadas em padrões bem definidos como WSDL e SOAP. A utilização de web services permite que a salada de sistemas que normalmente povoam os data centers das empresas conversem de forma menos traumática que os famigerados métodos tão conhecidos como transferências de dados via arquivos texto ou escritas em bancos de dados alheios. Porém, implementar fluxos de negócio complexos usando web services é bem mais difícil que parece à primeira vista. As principais dificuldades relacionadas ao uso de web services são:
  • As diversas implementações de WSDL e SOAP não seguem as especificações à risca, o que muitas vezes faz com que certos web services não sejam acessíveis a todas as aplicações.
  • Em alguns casos, um web service deve executar de forma assíncrona do ponto de vista do cliente que o invocou, mas mesmo assim deve existir uma garantia de que o mesmo será executado.
  • Se uma aplicação que publica um conjunto de web services sai do ar, isso afetará todas as aplicações que dependem destes web services. Seria interessante que web services pudessem ser invocados mesmo quando as aplicações que os publicam estivessem indisponíveis.
  • Web services são semanticamente próximos a chamadas de métodos, mas as trocas de informações entre sistemas, muitas vezes, são mais parecidas com eventos ou trocas de mensagens, que podem ser implementadas através de um gerenciador de filas baseado em JMS, por exemplo.
  • Os dados que trafegam entre aplicações diferentes podem necessitar de algum tipo de tradução. Em alguns casos precisam ser mesclados a partir de fontes diferentes, ou uma mensagem pode ser dividida em várias mensagens, que podem ser enviadas para destinatários.
A atual abordagem para solucionar parte destes problemas é a utilização de um Enterprise Service Bus, ou ESB. Um ESB é um componente de software capaz de atuar como um ponto central para a publicação de serviços baseados em diversos protocolos, além de auxiliar no roteamento e tradução de mensagens. Em arquiteturas orientadas a serviços, conhecidas como SOA, o ESB é o barramento onde os serviços corporativos são pendurados, permitindo a troca de mensagens entre as aplicações, sejam elas internas ou externas à organização. A implantação de um ESB é um dos requisitos para a implantação de uma arquitetura SOA.
Existem várias implementações de ESB, sendo a maioria de alto custo e fornecidas por grandes empresas como IBM e SAP. Porém algumas implementações open source vêm sendo desenvolvidas e ganhando maturidade à medida que sua adoção aumenta. Entre estas implementações, destacam-se: Apache Servicemix, Mule ESB, JBoss ESB e Apache Camel. Neste post vou me concentrar no Camel em função da sua simplicidade quando comparado aos demais.
Uma visão geral do Camel
Os próprios desenvolvedores admitem que o Camel não é um ESB completo, como mostra a resposta do FAQ à pergunta "Is Camel an ESB?":
Typically vendors claim practically everything is an ESB these days, so the answer is probably yes
However our view is that an ESB is more of a container of integration components, so we view Apache ServiceMix to be a true ESB based around JBI to provide a standards based integration platform of components.
We view Camel as being a rule based routing & mediation engine which can be used inside a full blown ESB, a message broker or a web services smart client. Though if you want to, you could consider that Camel is a small, lightweight embeddable ESB since it can provide many of the common ESB services like smart routing, transformation, mediation, monitoring, orchestration etc.
Independente de ser um ESB verdadeiro ou não, o importante é que o Camel é extremamente útil na implementação dos patterns típicos de integração de aplicações. O melhor é que isso pode ser feito com grande simplicidade, que é o maior diferencial em relação às demais soluções open source.
O Camel é construído tendo como base o Spring Framework, que fornece um ambiente de execução leve e de fácil configuração. Todos os componentes que formam o Camel são criados e gerenciados via Spring, o que traz todos os benefícios da inversão de controle, como baixo acoplamento, extensibilidade e facilidade de automação de testes. O Camel é composto pelos seguintes elementos:
CamelContext
Representa uma instância do Camel e é uma extensão do ApplicationContext do Spring. Os demais elementos da arquitetura são pendurados embaixo deste objeto.
Component
São plugins que enriquecem o Camel em termos de funcionalidades. Components são factories de endpoints (ver próximo tópico) que são os blocos elementares das rotas que podem ser criadas pelo desenvolvedor. O Camel fornece um rico conjunto de componentes, que permitem integração com diversor protocolos e ambientes diferentes, tais como JMS, JDBC, HTTP, File Systems, etc.
Route
O Camel pode ser definido como um roteador de mensagens. Routes são usados para definir a estrutura das rotas pelas quais irão fluir as mensagens.
Endpoint
Quando trafegam pelas rotas, as mensagens passam por pontos intermediários chamados de Endpoints. Os Endpoints são criados por componentes e são responsáveis pelo processamento das mensagens. O conceito de Endpoint implementado pelo Camel pode ser visto aqui. Os endpoints são declarados através de URL's. Uma URL serve como um identificador único de um endpoint dentro de uma rota, além disso ela informa ao CamelContext qual componente será responsável pela criação do endpoint.
Exchange
As mensagens são empacotadas em Exchanges. Estes objetos são usados pelos endpoints para a transferência das mensagens ao longo da rota.
Resolvendo problemas comuns de Integração
O Camel pode ser usado para criar proxies de web services. A vantagem desta abordagem é a centralização os endereços dos web services da organização em um único ponto. Além disso, é possível usar os recursos de roteamento para aplicar filtros ou enviar as mensagens para mais de um destinatário, entre outras milhares de coisas que podem ser feitas dentro do Camel. O caso mais simples é mostrado na figura a seguir:

As requisições são recebidas por um web service publicado pelo Camel. Estas mensagens são redirecionadas para a implementação real do web service. O código Java para implementar esta rota no Camel é o seguinte:
from("jetty:http://my.company/service").to("http://some.company/service");
Ok, ok. Concordo que isso não é tão incrível, mas é só o começo. Usando os recursos de roteamento e a rica coleção de componentes do Camel, é possível adicionar recursos bem interessantes a esta rota. Como por exemplo:
- Logar todas as mensagens em um arquivo texto:
from("jetty:http://my.company/service")
.to(
  "file:/logs/service.log",
  "http://some.company/service"
);

- Rotear a mensagem para diferentes endereços. Neste caso o Camel funcionaria como um balanceador de carga:
from("jetty:http://my.company/service")
  loadBalance().roundRobin().to(
    "http://some.company/service",
    "http://other.company/service"
);

- Rotear a mensagem para diferentes endereços de acordo com o conteúdo da mensagem. Como o formato de requisições SOAP é XML, é possível usar o filtro XPath do Camel usando o conteúdo da própria mensagem como entrada:
from("jetty:http://my.company/service").choice()
  .when().xpath("//cidade = 'Belo Horizonte' ")
    .to("http://sudeste/service")
  .when().xpath("//cidade = 'Salvador' ")
    .to("http://nordeste/service")
);

Outro cenário comum, mas difícil de ser implementado sem um software de mediação como o Camel, é a entrega garantida de mensagens. Imagine um sistema com alto requisito de disponibilidade que precisa enviar dados para um sistema externo que é parado diariamente , durante uma hora, para manutenção. Como nosso sistema 24x7 pode depender de outro sistema 23x7? O Camel fornece uma solução simples para este problema através do componente JMS. Claro que é possível usar JMS sem o Camel, mas se pensarmos em uma organização com um enorme parque de sistemas, nem todos eles são escritos em Java e conseguem usar JMS, ou só conseguem fazer isso usando conectores complexos. Usando a solução proposta a seguir, qualquer sistema que tenha integração com o sistema 23x7, só precisa invocar um web service publicado pelo Camel:
from("jetty:http://my.company/service").to("jms:filaEntrega");
from("jms:filaEntrega").to("http://some.company/service");

Caso o serviço saia do ar, o Camel intercepta o erro de conexão e re-submete a requisição automaticamente. Tanto a quantidade de tentativas quanto o intervalo entre elas pode ser configurado. Mesmo que o total de tentativas seja esgotado, é possível direcionar a mensagem para um dead letter channel, que é um endpoint especial que recebe mensagens não entregues, que podem ser tratadas mais tarde. A exemplo abaixo acrescenta tratamento de erro à rota anterior. Mensagens não entregues após 5 tentativas serão encaminhadas à fila "errosServico":
errorHandler(deadLetterChannel("jms:errosServico")
  .maximumRedeliveries(5).useExponentialBackOff());
from("jetty:http://my.company/service").to("jms:filaEntrega");
from("jms:filaEntrega").to("http://some.company/service");

Conclusão
Os exemplos que mostrei aqui são simples, mas acredito que mostram o enorme potencial do Apache Camel como mediador de mensagens.
Existem muitos casos de integração que podem ser tratados através do Camel. Uma lista bem completa baseada no best seller Enterprise Integration Patterns de Gregor Hohpe e Bobby Woolf pode ser encontrada aqui. Espero voltar a falar desse assunto em breve.

Saturday, July 12, 2008

Automação de testes com Canoo WebTest - Parte I

A maioria dos profissionais envolvidos em desenvolvimento de aplicações reconhece a importância da automação de testes, alguns tentam implantá-los, mas muitas vezes este esforço é malsucedido. As causas para o fracasso são várias:
  • Inexistência de roteiros de testes baseados nos requisitos.
  • Esforço para confecção e manutenção dos testes não previsto no cronograma do projeto.
  • Boa parte dos processos de desenvolvimento utilizados não contemplam automação de testes.
  • Dificuldade em convencer aos patrocinadores do projeto da importância dos testes automatizados.
  • Alto custo e limitações das ferramentas de teste.
Neste artigo, vou assumir que o leitor está convencido da utilidade da implantação de testes funcionais automatizados. A seguir vou mostrar que custo e limitação não é mais uma desculpa para não implementar e manter artefatos de automação para automação de testes funcionais para aplicações baseados em Web.

Porque Canoo WebTest?

Ao buscar uma ferramenta para automação de testes, elaborei a seguinte lista de requisitos:
  • Ser de baixo custo, de preferência gratuita.
  • Fácil de instalar e usar.
  • Facilmente "plugável" em scripts de build, de modo a permitir a execução automática dos testes em servidores de integração contínua.
  • Os scripts devem ser escritos em uma linguagem de programação rica, que facilite o uso de massas de dados externas e acesso ao banco de dados para preparação e validação dos resultados dos testes.
Entre as ferramentas avaliadas, o Canoo WebTest atendeu a boa parte destes requisitos:
  • Custo de aquisição zero.
  • Muito simples quando comparada a outras ferramentas.
  • É executada a partir de uma task do ANT, que é a ferramenta de build padrão para aplicações escritas em Java.
  • A linguagem padrão para os scripts é XML, que é muito limitada. Porém é possível criar scripts em Groovy, que é uma linguagem com recursos muito interessantes para esta finalidade. Por não ser a linguagem padrão, é necessário um esforço razoável para configurar o Canoo para utilizar o Groovy.

Instalando o Canoo WebTest

  1. Baixar a distribuição do Canoo WebTest aqui.
  2. Descompactar o arquivo em um diretório de sua escolha. Vou assumir o diretório c:\softs\webtest.
  3. Criar as variáveis de ambiente:
    • WEBTEST_HOME = c:\softs\webtest
    • JAVA_HOME = [diretório onde está instalado o JDK]
  4. Garantir que os diretórios %WEBTEST_HOME%\bin e %JAVA_HOME%\bin estão na lista do PATH do sistema.
  5. Para verificar se a instalação foi bem sucedida, crie um diretório qualquer (vou assumir c:\mytests) e entre com seguinte comando no command prompt:
    $ webtest -f %WEBTEST_HOME%\webtest.xml wt.createProject testeCanoo Buildfile: WebTest\webtest.xml wt.init: wt.createProject: [echo] [echo] Your WebTest project [testeCanoo] has been successfully created with some demo [echo] tests to get started. [echo] You can now just go to [c:\mytests\testCanoo] and run ant. [echo] BUILD SUCCESSFUL
  6. Se a execução do comando anterior gerar uma saída semelhante a esta acima, execute os comandos a seguir: $ cd testeCanoo $ ant
  7. Se após a execução do comando anterior, o arquivo c:\mytests\testeCanoo\results\webTestResults.html existir, o Canoo WebTest está funcionando corretamente em seu sistema. O conteúdo deste arquivo deve ser semelhante à figura abaixo:

Próximo post

No próximo post, vou mostrar como usar um plugin para Firefox como ferramenta de geração de scripts de teste. Também vou explorar os steps básicos que podem ser usadas na composição destes scripts através de exemplos.