Descrição: Veja neste artigo como desenvolver Session Beans, Stateless e Stateful, para acesso remoto com servidor de aplicações JBoss AS7. A seguir será implementado uma aplicação java para invocar os Sessions Beans criados.

Neste artigo serão desenvolvidos dois Sessions Beans, um do tipo Stateless e outro Stateful. O primeiro disponibilizará uma funcionalidade de conversão de câmbio, de dólar para real. O segundo possuirá serviços para realizar a compra da moeda americana.

Esses componentes de negócios serão utilizados por outro sistema, através de acesso remoto.

O IDE utilizado será o Eclipse em sua versão 4.2.2 Juno, com o plugin JBoss AS, para a configuração do servidor.

O primeiro passo é criar um EJB Project, para isso clique com o botão direito do mouse na área de Project Explorer, New -> EJB Project.

Criação do EJB Project no Eclipse

Figura 1: Criação do EJB Project no Eclipse

Após isso, um wizard será aberto, assim devemos configurar o nome do projeto e o target runtime que é o servidor de aplicações que iremos utilizar na nosso projeto. Para facilitar a configuração do servidor no Eclipse, foi instalado o plugin do JBoss AS.

Wizard Eclipse para criação do EJB project

Figura 2: Wizard Eclipse para criação do EJB project

Para a configuração do servidor no Eclipse utilizando o plugin, clique no botão “New Runtime...” e escolha dentro da pasta JBoss Comunity a opção JBoss 7.1 Runtime, após isso, clique em next. Na próxima etapa é preciso apontar para a pasta onde o JBoss está instalado.

Configuração do JBoss 7.1 no EJB project

Figura 3: Configuração do JBoss 7.1 no EJB project

O resultado final é apresentado na Figura 4, onde é possível ver o nome do projeto e a configuração final do servidor. Em seguida basta finalizar a criação.

Configuração final do EJB Project

Figura 4: Configuração final do EJB Project

Após a configuração do servidor, iremos criar os Sessions Beans. Criaremos primeiramente o componente que permite a conversão de câmbio, de dólar para real. Esse é um exemplo extremamente simples, pois o foco do artigo é explorar os conceitos dos tipos de EJBs (Stateless e Stateful) e JNDI, para acesso remoto entre aplicações Java. Conceitos importantes em aplicações distribuídas.

Para criar um Session Bean no Eclipse podemos utilizar um wizard. Ele facilita o processo de criação. Para acessá-lo clique com o botão direito do mouse na pasta ejb-module, que está dentro do projeto EJB e escolha a opção new -> Session Bean (EJB 3.x).

Criação de Stateless Session Beans

Figura 5: Criação de Stateless Session Beans

Neste wizard é possível configurar Stateless, Stateful e Singleton session beans para acesso Local e/ou Remoto.]

Wizard para criação de Session Bean

Figura 6: Wizard para criação de Session Bean

Criaremos agora um Stateless Session Bean com acesso Remoto. Para isso, é preciso configurar o pacote e no nome do EJB, na opção “State type”, escolha Stateless e marque o checkbox Remote, para a criação da interface do EJB com a anotação Remote.

Resultado da criação do Session Bean

Figura 7: Resultado da criação do Session Bean

O resultado da criação é apresentado na Figura 7, a classe CotacaoServices e a interface CotacaoServicesRemote foram criados corretamente.

Na interface CotacaoServicesRemote foi definido um único serviço, o calcular cotação da moeda real, passado um valor em dólar:

Listagem 1: Interface CotacaoServicesRemote


package devmedia.ejb;

import javax.ejb.Remote;

@Remote
public interface CotacaoServicesRemote {

	public double calcularCotacaoReal(double valor);
	
}

A interface está anotada com @Remote para possibilitar o acesso remoto ao EJB. Dessa forma, aplicações executadas em diferentes servidores de aplicações e/ou diferentes máquinas podem utilizar o nosso componente de negócios.

A classe deve implementar a interface e receber a anotação @Stateless, pois essa funcionalidade não precisa manter o estado. O serviço é atômico, ou seja, a conversão é executada para um cliente e logo após finalizada. Desta forma, ficará disponível para que outros clientes possam utilizar o serviço de conversão.

Listagem 2: Classe CotacaoServices


package devmedia.ejb;

import javax.ejb.Stateless;

/**
 * Session Bean implementation class CotacaoServices
 */
@Stateless
public class CotacaoServices implements CotacaoServicesRemote {

    private static final double COTACAO_DOLAR = 2.05;
    
	public CotacaoServices() {

    }

	@Override
	public double calcularCotacaoReal(double valor) {
		return valor*COTACAO_DOLAR;
	}

}

O próximo componente que iremos desenvolver neste artigo é um Session Bean Stateful para a compra de dólares. Ele armazena a quantidade de dólares que o usuário irá adquirir e calcular o valor total que o cliente deverá pagar em reais, para isso iremos utilizar o componente de conversão que desenvolvemos anteriormente.

O processo para a construção do EJB Stateful é o mesmo utilizado para o EJB Stateless, a diferença é que no momento da configuração, devemos escolher a opção Stateful ao invés de Stateless:

Criação de EJB Stateful

Figura 8: Criação de EJB Stateful

O Session Bean possui três funcionalidades: adiconar e remover dólares, e calcular o valor total em reais:

Listagem 3: Interface CompraDolarServiceRemote


package devmedia.ejb;

import javax.ejb.Remote;

@Remote
public interface CompraDolarServicesRemote {

	public void adicionarDolar(double valor);
	
	public void removerDolar(double valor);
	
	public double calcularValorTotal();
	
} 

A implementação da interface é uma classe EJB anotada com @Stateful, pois para esse serviço, a aplicação deve manter a quantidade de dólares que o usuário deseja comprar, pois cada instância do Session Bean deve atender a um cliente, mantendo a integridade das informações.

Listagem 4: Classe CompraDolarServices


package devmedia.ejb;

import javax.ejb.EJB;
import javax.ejb.Stateful;

/**
 * Session Bean implementation class CompraDolarServices
 */
@Stateful
public class CompraDolarServices implements CompraDolarServicesRemote {

    private double valor;
    
    @EJB
    private CotacaoServicesRemote cotacaoService;
	
    public CompraDolarServices() {
    }

	@Override
	public void adicionarDolar(double pValor) {
		valor += pValor;
	}

	@Override
	public void removerDolar(double pValor) {
		valor -= pValor;
	}

	@Override
	public double calcularValorTotal() {
		return cotacaoService.calcularCotacaoReal(valor);
	}

}
 

Esse EJB depende da funcionalidade do EJB de conversão, assim, foi criado um atributo que referencia a interface remota do EJB de conversão (CotacaoServicesRemote) e ela foi anotada com @EJB para utilizar a injeção de dependência, ou seja, para que o servidor de aplicações coloque uma instância do Session Bean de ConversaoServices neste atributo, depois que o componente de CompraDolarServices for criado.

Esta forma demonstra que a interface de Session Bean anotada com @Remote pode ser utilizada também de forma local, pois, os dois componentes (Conversão e Compra) estão sendo executados no mesmo servidor de aplicações. O contrário não pode ser realizado, pois a interface anotada como @Local não pode ser utilizada para chamadas remotas.

Os dois componentes estão prontos. Agora é necessário executar a aplicação no servidor JBoss AS 7.1. Para isso, iremos executar dentro do próprio Eclipse (é possível também gerar o arquivo .jar e realizar o deploy dentro do servidor). Para isso, clique com o botão direito do mouse no projeto e escolha “Run as” -> “Run on Server”.

Opção para executar o EJB project de dentro do eclipse no JBoss AS 7.1

Figura 9: Opção para executar o EJB project de dentro do eclipse no JBoss AS 7.1

Se a outra aplicação (Cliente) for acessar o servidor utilizando endereço de IP, é necessário realizar uma configuração no servidor de aplicações. Isso é necessário pois a configuração padrão permite o acesso somente com “localhost”.

Essa configuração é feita no arquivo standalone.xml e pode ser acessado de dentro do Eclipse na aba server -> Filesets -> Configuration File ->>standalone.xml. Este arquivo está presente dentro do jboss, no path JBOSS_HOME/standalone/configuration/.

Arquivo de configuração do JBoss AS7

Figura 10: Arquivo de configuração do JBoss AS7

Neste arquivo, altere a interface public de para :

Listagem 5: Trecho do arquivo standalone.xml

 
 <interfaces>
        <interface name="management">
            <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
        </interface>
        <interface name="public">
            <any-address/>
        </interface>
        <interface name="unsecure">
            <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/>
        </interface>
</interfaces>

Vamos desenvolver uma outra aplicação para utilizar os EJBs de forma remota. Existem duas formas de fazer o acesso remoto, utilizando EJB Client API ou JNDI. Neste artigo vamos utilizar JNDI – Java Naming and Directory Interface para fazer o lookup do nossos Sessions Beans.

No Eclipse, crie um Java Project através do menu file -> new -> Project -> Java Project. Após isso, um wizard de configuração será aberto. Nele é necessário configurar o nome do projeto. O nosso será DevMedia_AcessoRemoto, as outras configurações podemos deixar o padrão.

Criação de um Java Project no Eclipse

Figura 11: Criação de um Java Project no Eclipse

O acesso remoto é feito sempre utilizando os contratos dos serviços, ou seja, através das interfaces dos EJBs. Dessa forma, é necessário copiar as interfaces “CompraDolarServicesRemote.java” e “CotacaoServicesRemote.java” para dentro do Java Project criado. É através das interfaces que podemos utilizar RMI (Remote Method Invocation) e invocar os métodos que estão “rodando” em outro servidor, em diferentes máquinas.

Copiando as interfaces dos EJBs

Figura 12: Copiando as interfaces dos EJBs

A Figura 12 mostra as interfaces copiadas do projeto EJB, elas apresentam erros, pois utilizam a annotation @Remote do javax.ejb.Remote e não estão presentes no projeto. Para os projetos clientes, podemos adicionar o “jboss-client.jar” no classpath da aplicação. Esse jar está no JBOSS_HOME/bin/client/ e contém todos os elementos que vamos precisar no desenvolvimento. Dessa forma os erros não haverá mais erros.

Para adicionar esse jar no buildpath do projeto, clique com o botão direito do mouse em cima do projeto e acesse o menu Build Path -> Configure Build Path, aa aba “Libraries”, clique em “add External JARs..” e procure pelo jboss-client.jar.

Adicionando o jboss-client.jar no buildpath do projeto

Figura 13: Adicionando o jboss-client.jar no buildpath do projeto

Vamos escrever a classe de teste. O primeiro passo é configurar as propriedades do contexto para fazer o lookup do EJB utilizando JNDI.

JNDI é a API padrão na plataforma Java EE para acesso uniforme a serviços de nomes e diretórios, desta forma é possível acessar diversos sistemas de catálogos, e no nosso caso, será utilizado para localizar os EJBs.

A aplicação cliente deve possuir um arquivo de configuração em seu classpath, chamado jboss-ejb-client.properties. Segue o conteúdo do arquivo para a nossa aplicação:

Listagem 6: Arquivo de configuração da aplicação


endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
 
remote.connections=default
 
remote.connection.default.host=10.2.22.10
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false

remote.connection.default.username=thiagoyama
remote.connection.default.password=123456

A serguir vamos descrever algumas das propriedades:

  • endpoint.name: propriedade opcional, caso não seja definida, será utilizado o valor padrão: “config-based-ejb-client-endpoint”. Ela representa o nome do endpoint do lado da aplicação cliente;
  • remote.connections: nome da conexão, é utilizado para configurar as conexões. É possível definir várias conexões utilizando nomes diferentes para cada conexão. Ex. remote.connections= conexao1, conexao2;
  • remote.connection..host: host do servidor onde a aplicação EJB está executando;
  • remote.connection..port: porta para a conexão, por padrão o JBoss AS7 utiliza a 4447;
  • remote.connection..username, remote.connection..password: usuário e senha. Elas podem ser criadas através do comando JBOSS_HOME/bin/add-user.sh (ou .bat). Isso é necessário pois a configuração padrão de segurança sercurity-realm está ativada.
  • é o nome da conexão definido na propriedade remote.connection.

Vamos criar um usuário para a autenticação do acesso remoto. Acesse o add-user.sh (ou .bat), escolha a opção a, para criar um Management User, deixe em branco o Realm (ManagementRealm). Digite um username e password e finalize:

Criação de um usuário no JBoss AS7

Figura 14: Criação de um usuário no JBoss AS7

Com a configuração de contexto realizada, poderemos criar o contexto e utilizá-lo para fazer o lookup dos EJBs através do nome JNDI que eles foram publicados no servidor.

O nome de publicação para Stateless Session bean é composto por:

ejb:/// !

O Stateful Session bean segue o mesmo padrão, a diferença é a adição de ?Stateful no final do nome:

ejb:/// !?Stateful

Isso indica que estamos fazendo o lookup de um Stateful Session Bean, possibilitando a criação do bean correto, que deve manter sessão.

Vamos analizar cada parte do nome:

  • ejb: é uma constante (não muda), define que é um lookup de EJB;
  • app-name: o nome do .ear (sem o sufixo .ear) que contém os EJBs. Se a aplicação não for empacotada em um ear, então basta deixar o app-name vazio;
  • module-name: é o nome do .jar ou .war (sem o sufixo .jar ou .war) que contém os EJBs.
  • distinct-name: é um nome alternativo que podemos definir. Ela é opcional;
  • bean-name: o nome do session bean que queremos fazer o lookup;
  • fully-qualified-classname-of-the-remote-interface: o nome e pacote da interface remota que queremos fazer o lookup;

Abaixo está a classe de teste, ela cria o contexto, faz o lookup dos EJBs e invoca os métodos:

Listagm 7: Classe de teste


package devmedia.main;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;

import devmedia.ejb.CompraDolarServicesRemote;
import devmedia.ejb.CotacaoServicesRemote;

public class ConsoleViewCotacao {

	public static void main(String[] args) throws Exception{
		
		Properties prop = new Properties();
		prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
		final Context context = new InitialContext(prop);
		
		//Lookup no serviço de cotação remota
		CotacaoServicesRemote cotacaoService = CotacaoServicesRemote) context.lookup(
"ejb:/DevMedia_EJB/CotacaoServices!devmedia.ejb.CotacaoServicesRemote");
		
		//Invocando a cotação remota
		System.out.println("Cotação: 20 dolares são " + cotacaoService.calcularCotacaoDolar(20) + " reais");
		
		//Lookup no serviço de compra de dolar remota
		CompraDolarServicesRemote dolarService =  (CompraDolarServicesRemote) context.lookup(
"ejb:/DevMedia_EJB/CompraDolarServices!devmedia.ejb.CompraDolarServicesRemote?stateful");
		
		//Adiciona 40 dolares
		dolarService.adicionarDolar(40);
		
		//Calcula o valor total a pagar em reais
		System.out.println("Total a pagar: " + dolarService.calcularValorTotal() + " reais");
		
		//Remove 20 dolares
		dolarService.removerDolar(20);
		
		//Calcula o valor total a pagar em reais
		System.out.println("Total a pagar: " + dolarService.calcularValorTotal() + " reais");
		
	}
}

A classe possui alguns comentários para facilitar o entendimento. Após o lookup, acionamos o método calculaCotacaoDolar() do CotacaoServicesRemote para converter 20 dólares em reais. Depois, utilizamos bean DolarServicesRemote para adicionar 40 dólares ao nosso “carrinho de compras” e calculamos o valor a pagar. Note que após isso, foi invocado o método para removerDolar, onde foram removidos 20 dólares dos 40 já armazenados. E por fim, calculamos novamente o valor total a pagar. Isso demonstra a diferença entre o Stateless e Stateful Session Bean, o primeiro pode ser acessado por vários clientes enquanto que no segundo, cada instância do Session Bean atende a um cliente e pode armazenar informações relativas a ele.

Neste artigo foram discutidos as diferenças entre Sessions Beans do tipo Stateless e Stateful, e o acesso remoto desses Sessions Beans utilizando JNDI. Estes conceitos podem ser bastante úteis no desenvolvimento de aplicações distribuídas, um cenário comum em grandes aplicações corporativas.