Uma situação comum é a necessidade de conectarmos uma aplicação cliente, seja ela um componente EJB, um componente Web, uma aplicação desktop ou uma aplicação de linha de comando, a um componente EJB hospedado em outro servidor através de sua interface remota.
Nesse post, iremos ver como fazer com que uma aplicação de linha de comando invoque um método de negócio em um componente EJB hospedado em um ambiente WebSphere 7.



Os artefatos que vamos gerar nesse tutorial serão os seguintes:
  1 arquivo EAR (Java EE 5) chamado ExemploEAR.ear
  1 arquivo EJB JAR (EJB 3.0) chamado ExemploEJB.jar
  1 arquivo JAR chamado ExemploEJBClient.jar (Que irá conter a interface de negócio do EJB)
  1 arquivo JAR chamado ExemploClient.jar (Aplicação cliente)

O que você vai precisar:

  1. Uma IDE de sua preferência (pode ser um simples editor de texto também). Para o código desse post estou usando o Eclipse Indigo (3.7.2) for Java EE Developers
  2. Uma instalação do WebSphere Application Server v7 (Remota ou local)
  3. Um JDK 6 ou JDK 7 IBM ou SUN-Oracle
Vamos iniciar criando a interface de negócio do nosso EJB, que será empacotada no arquivo ExemploEJBClient.jar
1
2
3
4
5
6
7
8
package com.ladoservidor.exemplo.ejb;
import javax.ejb.Remote;

@Remote
public interface ExemploSLSBRemote {

 public String metodoRemoto();
}

Com a interface criada, podemos criar agora o componente EJB
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.ladoservidor.exemplo.ejb;

import java.util.logging.Logger;

import javax.ejb.Stateless;

@Stateless
public class ExemploSLSB implements ExemploSLSBRemote {

 Logger log = Logger.getLogger(ExemploSLSB.class.getName());
    public String metodoRemoto() {
     log.info("Metodo remoto executado no EJB!");
     
     return "Metodo executado com sucesso, verifique o log do servidor";
    }
}

Essa classe deve ser colocada dentro do arquivo ExemploEJB.jar e seu arquivo MANIFEST.MF deve incluir no atributo Class-Path a referência para o arquivo ExemploEJBClient.jar, abaixo um exemplo do conteúdo desse arquivo
1
2
Manifest-Version: 1.0
Class-Path: ExemploEJBClient.jar

Com os dois arquivos criados, basta empacotá-los dentro do arquivo ExemploEAR.ear e fazer o deploy dele no WebSphere, esse processo pode ser feito com as opções padrão apresentadas pela console administrativa. Após o deploy, inicie aplicação usando a console administrativa.

Ao iniciar a aplicação, verifique no log do servidor a inicialização do componente EJB e o nome JNDI pelo qual ele foi registrado. Esse registro do nome JNDI é importante para fazer o lookup do componente e caso não tenha sido informado em nenhum local (anotações, arquivo de configuração do componente, console administrativa) ele resolverá para o nome qualificado da interface remota, nesse caso com.ladoservidor.exemplo.ejb.ExemploSLSBRemote
1
2
3
4
EJBContainerI I   WSVR0037I: Starting EJB jar: ExemploEJB.jar
EJBContainerI I   CNTR0167I: The server is binding the com.ladoservidor.exemplo.ejb.ExemploSLSBRemote interface of the ExemploSLSB enterprise bean in the ExemploEJB.jar module of the ExemploEAR application.  The binding location is: ejb/ExemploEAR/ExemploEJB.jar/ExemploSLSB#com.ladoservidor.exemplo.ejb.ExemploSLSBRemote
EJBContainerI I   CNTR0167I: The server is binding the com.ladoservidor.exemplo.ejb.ExemploSLSBRemote interface of the ExemploSLSB enterprise bean in the ExemploEJB.jar module of the ExemploEAR application.  The binding location is: com.ladoservidor.exemplo.ejb.ExemploSLSBRemote
EJBContainerI I   WSVR0057I: EJB jar started: ExemploEJB.jar

Se durante a inicialização da aplicação for apresentada a seguinte mensagem de erro
1
2
3
4
EJBMDOrchestr E   NO_BEANS_IN_MODULE_CNTR9269E
EJBContainerI E   WSVR0039E: Unable to start EJB jar, ExemploEJB.jar: The ExemploEJB.jar Enterprise JavaBeans (EJB) module does not have any enterprise beans configured.
DeployedAppli W   WSVR0206E: Module, ExemploEJB.jar, of application, ExemploEAR.ear/deployments/ExemploEAR, failed to start
ApplicationMg W   WSVR0101W: An error occurred starting, ExemploEAR

Será preciso marcar a opção "Generate Default Bindings" no inicio do processo de deploy para resolver esse erro
Opção Generate Default Bindings marcada durante o deploy da aplicação no WebSphere

Com o componente EJB em execução, precisamos agora criar o código no cliente que irá invocar o metodoRemoto() do componente EJB
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.ladoservidor.exemplo.client;

import java.util.Properties;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import com.ladoservidor.exemplo.ejb.ExemploSLSBRemote;

public class ExemploClient {
 private Context ctx;
 static Logger log = Logger.getLogger(ExemploClient.class.getName());

 public ExemploClient() {
  try {
   Properties props = new Properties();
   props.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
   props.put(Context.PROVIDER_URL, "iiop://was.ladoservidor.com:2809");
   ctx = new InitialContext(props);
  } catch (NamingException ex) {
   log.severe("Problemas ao inicializar o Context " + ex.getMessage());
   ex.printStackTrace();
  }
 }

 public static void main(String[] args) {
  ExemploClient cliente = new ExemploClient();

  log.info("Método sendo executado: " + cliente.metodoClient());
 }

 public String metodoClient() {
  String retorno = null;
  ExemploSLSBRemote slsb = null;

  try {
   log.info("Executando o lookup no componente EJB");
   Object obj = ctx.lookup("com.ladoservidor.exemplo.ejb.ExemploSLSBRemote");
   slsb = (ExemploSLSBRemote) PortableRemoteObject.narrow(obj, ExemploSLSBRemote.class);
   log.info("Invocando metodo remoto");
   retorno = slsb.metodoRemoto();
  } catch (NamingException ex) {
   log.info("Problemas ao localizar o EJB: " + ex.getMessage());
  }

  return retorno;
 }
}

Atenção as informações de conexão ao WebSphere, a inicialização da classe Context deve conter as informações corretas de conexão JNDI, i.e., você tem que informar a classe
com.ibm.websphere.naming.WsnInitialContextFactory como Factory e a porta para conexão deve ser a porta BOOTSTRAP_ADDRESS ou ORB_LISTENER_ADDRESS do servidor WebSphere que hospeda o componente EJB.
Você pode obter a informação sobre o número correto da porta através da console administrativa, nas configurações do servidor
Portas do servidor WebSphere apresentadas pela console administrativa

Com o código do cliente preparado, você pode empacotar a classe cliente no arquivo ExemploClient.jar e para executar a aplicação você precisará colocar no classpath dela o arquivo ExemploEJBClient.jar

Para executar o cliente usando um JDK da IBM, também é preciso colocar no classpath da aplicação os seguintes arquivos:
[WAS_INSTALL_DIR]/AppServer/lib/activation-impl.jar
[WAS_INSTALL_DIR]/AppServer/lib/mail-impl.jar
[WAS_INSTALL_DIR]/AppServer/lib/j2ee.jar
[WAS_INSTALL_DIR]/AppServer/runtimes/com.ibm.ws.ejb.thinclient_7.0.0.jar

Caso você resolva utilizar uma JDK SUN-Oracle, também deverá incluir o seguinte arquivo no classpath
[WAS_INSTALL_DIR]/AppServer/runtimes/com.ibm.ws.orb_7.0.0.jar

Ao executar o cliente na forma de uma aplicação Java de linha de comando, provavelment será apresentada a seguinte mensagem de erro na execução
1
Exception in thread "P=73734:O=0:CT" java.lang.ClassCastException: Unable to load class: com.ladoservidor.exemplo.ejb._ExemploSLSBRemote_Stub

Esse erro ocorre porque o cliente não possui o stub (Representação local da interface remota) necessário para a invocação remota do EJB, esse stub é gerado dinamicamente pelo WebSphere Application Server Just-In-Time (JIT) dentro de um container Java EE (web container, EJB container ou application client container) rodando em ambiente WebSphere v7. Para clientes Java SE (Nosso caso nesse exemplo) ou ambientes Java EE que não sejam WebSphere v7, é necessário criar o stub manualmente e colocá-lo no classpath da aplicação cliente.

Para gerar a classe stub, é preciso executar o script createEJBStubs que se encontra dentro do diretório [WAS_INSTALL_DIR]/AppServer/bin, passando como parâmetro a classe que representa a interface remota do EJB - ExemploSLSBRemote
1
createEJBStubs.sh com.ladoservidor.exemplo.ejb.ExemploSLSBRemote -cp .

Lembre de colocar como argumento de classpath (-cp) o local onde a classe ExemploSLSBRemote pode ser encontrada

O comando createEJBStubs aceita como argumentos para gerar o stub uma classe, um arquivo JAR ou um arquivo EAR, você pode executar o script sem passar nenhum parâmetro para ver as suas outras opções de invocação.

Com o stub criado, basta incluir ele no classpath da aplicação cliente e tentar novamente a execução. A saída do comando deve ser parecida com o seguinte
1
2
3
4
5
6
com.ladoservidor.exemplo.client.ExemploClient metodoClient
INFO: Executando o lookup no componente EJB
com.ladoservidor.exemplo.client.ExemploClient metodoClient
INFO: Invocando metodo remoto
com.ladoservidor.exemplo.client.ExemploClient main
INFO: Método sendo executado: Metodo executado com sucesso, verifique o log do servidor

E no log do WebSphere, será apresentada a seguinte mensagem
1
ExemploSLSB   I   Metodo remoto executado no EJB!

Um abraço,
Marcelo Sousa Ancelmo