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.

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.

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.

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.

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).

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.]

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.

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:

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”.

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/.

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.

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.

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.

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:

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.