Esse artigo faz parte da revista Java Magazine edição 52. Clique aqui para ler todos os artigos desta edição 

re proveito do desempenho do New I/O sem escovar bits

 

Neste artigo veremos como desenvolver aplicações de rede em Java com alto nível de abstração, fazendo uso do Apache MINA (Multipurpose Infrastructure for Network Applications). O MINA é um framework que facilita a codificação de aplicações que requerem conectividade, com foco na flexibilidade, escalabilidade e facilidade de manutenção. Ele faz pelo desenvolvimento de aplicações de rede o que, por exemplo, o framework de coleções do Java SE faz pela programação com conjuntos de objetos. O  MINA é baseado nas APIs New I/O (NIO), que foram lançadas com o J2SE 1.4.

O MINA é muito usado para criar servidores e clientes suportando protocolos arbitrários. Neste ponto, o leitor pode perguntar por que desenvolver um servidor desde o início, ao invés de utilizar soluções prontas como o JBoss ou o Tomcat? Temos que ter em mente que esses servidores utio lizam principalmente o protocolo HTTP/ HTTPS para a troca de mensagens, mas que, às vezes, temos que utilizar aplicativos que trabalham com protocolos customizados, que não são suportados por padrão em servidores ou em soluções de integração. Como exemplo, podemos citar um servidor que os autores desenvolveram recentemente para realizar a troca de mensagens entre companhias aéreas, no qual um dos requisitos básicos era a utilização de protocolos como o GIOP (General Inter-ORB Protocol1), MATIP (Mapping of Airline Traffic over Internet Protocol), além de protocolos customizados. Tudo isso foi simples de ser implementado por utilizarmos o MINA. O sistema também exigia alta performance e escalabilidade, O que é garantido com o correto uso desse framework.

 

Nota 1: O GIOP é o protocolo abstrato, independente de transporte, do middleware CORBA. Sendo que o mais conhecido IIOP é um mapeamento deste protocolo para redes TCP/IP, usado por ORBs CORBA e pelo EJB.

 

Arquitetura e conceitos

O MINA suporta a manipulação de bytes e de outros recursos de baixo nível, e também permite adicionar um nível de abstração no recebimento e envio dos dados pela rede. Veremos a seguir como trabalhar nesses dois níveis de abstração.

 

Camada de I/O: desenvolvendo em baixo nível

Quem necessita realizar operações de entrada e saída (I/O) de baixo nível para serviços simples pode se beneficiar dos recursos do MINA. A arquitetura do framework é dividida em camadas. A primeira é a de I/O de baixo nível, que permite realizar a manipulação dos dados em forma de bytes, mas sem deixar de aproveitar os recursos providos pelo framework. A Figura 1 ilustra a arquitetura do MINA para a camada de I/O.

 

Figura 1. Arquitetura do MINA para I/O de baixo nível

 

IoSession

Um elemento essencial do MINA (não mostrado na figura) é a interface org.apache. mina.common.IoSession, que representa umaconexão entre o cliente e o servidor. Através de implementações desta interface, é possível obter informações sobre a conexão e acerca da comunicação entre o cliente e o servidor. A Figura 2 destaca essa interface com todos os métodos disponíveis.

 

Figura 2. Interface IoSession

 

IoHandler: I/O através de eventos

A interface org.apache.mina.common.IoHandler recebe notificações do MINA sobre os eventos ocorridos e permite que a aplicação responda a esses eventos através de uma instância de IoSession, que encapsula  a conexão de rede.

Para facilitar, o MINA provê um classe adaptadora (org.apache.mina.common. IoHandlerAdapter), que já implementa todos os métodos da interface IoHandler. Com isso, é possível redefinir (override) apenas os métodos que serão realmente utilizados. Os métodos da interface IoHandler são os seguintes:

  • sessionCreated(): Invocado quando uma conexão é criada, permite configurar parâmetros do socket utilizado para a comunicação, através da propriedade IoSession.config. Nesse método não devem ser realizadas operações de I/O, mas sim apenas configurações.
  • sessionOpened(): Chamado após o sessionCreated(), permite a realização de I/O.
  • sessionClosed(): Invocado quando a conexão é fechada.
  • sessionIdle(): Invocado quando não ocorre comunicação alguma durante um determinado período de tempo.
  • exceptionCaught(): Chamado quando são lançadas exceções pelo IoAcceptor ou IoHandler.
  • messageReceived(): Método chamado quando uma mensagem do protocolo é recebida. Quando não é utilizado um protocolo, mas sim diretamente a camada de I/O, o objeto representando a mensagem  é uma instância de ByteBuffer.
  • messageSent: Invocado quando dados são escritos na conexão através do método IoSession.write(Object).

 

Exemplo com I/O de baixo nível

Como primeiro exemplo, vamos implementar um servidor que, ao receber uma mensagem, envia esta mensagem de volta para o cliente. Trata-se portanto de um servidor de “Eco”. A Listagem 1 apresenta a classe ServidorEco, que inicia o servidor eregistra o IoHandler, responsável por responder aos eventos de I/O.

Analisando essa classe, temos como o primeiro objeto criado um SocketAcceptor, que será responsável por iniciar o processo do servidor e aceitar conexões em um determinado endereço e porta. O segundo objeto criado – uma instância de SocketAcceptorConfig – permite a configuração do comportamento do SocketAcceptor, fornecendoacesso ao socket criado para a conexão. Por fim, invocamos o método bind() do SocketAcceptor, que associa um IoHandler a um endereço TCP/IP. Isso faz com que os eventos de conexões recebidas nesse endereço sejam tratados pelo IoHandler, que no exemplo será uma instância de ServidorEcoIoHandler.

A Listagem 2 apresenta a classe Servidor EcoIoHandler. No método sessionCreated(), efetuamos a configuração do buffer de leitura do socket utilizado. Exceções lançadas são tratadas pelo método exceptionCaught(). No exemplo, as exceções causam o fechamento da conexão. Por fim, o método messageReceived() trata a recepção de informações. Após lidos, os dados são escritos no canal de comunicação através do objeto que representa a sessão (instância de IoSession). Caso implementássemos também o método messageSent(), ao escrever os dados na conexão esse método seria invocado para informar que uma mensagem foi enviada.

A Listagem 3 ilustra as classes do cliente, que estabelecem a conexão, enviam um texto lido a partir do teclado e fazem a leitura do que o servidor retornou, através da recepção de eventos no lado do cliente (os dados retornados devem ser os mesmos enviados). Repare que no cliente também usamos o MINA.

 

Listagem 1. Classe que inicia o servidor de Eco

package br.com.jm.mina.eco.servidor;

 

//imports...

 

public class ServidorEco {

   private static final int PORTA = 8080;

 

public static void main(String[] args) throws Exception {

...
Quer ler esse conteúdo completo? Tenha acesso completo