[EJB 3.1] Novidades na especificação - Parte 2

sexta-feira, 2 de maio de 2008

Continuando o artigo sobre as novidades na Especificação EJB 3.1, nesta segunda parte vamos abordar o invoções assíncronas e EJB sem interface.

Na nova especificação um cliente pode invocar um session bean de maneira síncrona ou assíncrona. Invocações assíncronas que é a novidade nos permite efetuar uma chamada a um método remoto (assíncrono) e não precisamos esperar o seu retorno, neste caso a assinatura do método deve ser void, ou o método pode retornar um objeto Future que retorna o resultado para o cliente após o processamento, o que permite verificar as exceções ou até mesmo cancelar a invocação ao método no meio da execução.


Exemplo:

@Asynchronous
public Future executarMetodo() {
String resultado = "Executando Método";
return new AsyncResult(resultado);
}

Outra novidade é o que todos já esperavam "O Fim das Interfaces", ou pelo menos agora elas são opcionais. Antes que alguém se empolgue é bom deixar claro que isso só funciona para objetos LOCAIS, ou seja, o cliente que irá chamar o método deve estar na mesma JVM. :-(Na verdade um session bean sem interface é uma variação do EJB local, mas expõe os métodos públicos da classe bean sem utilizar uma interface de negócio separada.
A referência para o Bean sem interface pode ser passada como um parâmetro, como valor de retorno de qualquer interface de negócio local ou como método de uma classe ejb sem interface.
A implementação do contêiner para Beans sem interface é o mesmo que nós fazemos quando acessamos um método qualquer de uma classe qualquer pela referência, o método de negócio da instancia do session bean e seus interceptors são invocados conforme a necessidade.
Somente os métodos públicos da classe bean (e qualquer super-classe) pode ser invocado através do Bean sem interface. Qualquer tentativa de invocação a métodos com modificadores de acessos diferente de público, irá lançar um javax.ejb.EJBException.
Vejamos a evolução da especificação EJB, dando o mesmo exemplo nas versões 2.1, 3.0 e finalmente 3.1.

Versão 2.*

Até a versão 2.* a comunidade reclamava muito da complexidade dos EJBs, para a criação de um método simples era preciso criar as interfaces (remota e local), a interface home e finalmente a classe de negócio...

Interface Local

import javax.ejb.EJBLocalObject;

public interface SayHelloRemote extends EJBLocalObject{
public void hello();
}

Interface Home

import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;

public interface SayHelloHome extends EJBLocalHome {
SayHello create() throws CreateException;
}

Session Bean

import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

public class SayHelloBean implements SessionBean {
public void sayHello() {
System.out.println("Hello");
}

public SayHelloBean() {}
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}


Cliente Session Bean 2.1

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

public class SayHelloClient {

public static void main(String[] args) {
try {
Context initial = new InitialContext();
Context myEnv = (Context)initial.lookup("java:comp/env");
Object objref = myEnv.lookup("ejb/SayHello");
SayHelloHome home = (SayHelloHome) PortableRemoteObject.narrow(objref,
SayHelloHome.class);
SayHello papagaio = home.create();
papagaio.sayHello();
papagaio.remove();
System.exit(0);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Versão 3.0

Vemos que até a versão 2.1 era muito trabalho criar um simples Hello World com EJB, a Sun vendo que o EJB estava perdendo espaço para frameworks comunitários, como Hibernate para persistência e outras alternativas para Session Beans, decidiu fazer uma mudança radical na especificação, descontinuando os Entity Beans e apresentando o JPA, e tirando a obrigatoriedade de criar a classe Home, eliminando a criação de deployment descriptors, usando e abusando da IoC com o uso extensivo de annotations. A especificação facilitou muito o desenvolvimento e a especificação EJB voltou a ser o supra sumo do Java, vejamos um exemplo

Interface de Negócio Local

import javax.ejb.Local;

@Local
public interface SayHelloLocal {
void hello();
}

Session Bean

import javax.ejb.Stateless;

@Stateless
public class SayHelloBean implements SayHelloRemote, SayHelloLocal{
public void hello() {
System.out.println("Hello");
}
}

Cliente que executa o EJB

public class SayHelloClient {
private static Properties prop = new Properties();
private static Context c;
public SayHelloClient(){
prop.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
prop.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
prop.put("java.naming.provider.url","localhost");
}
public static void main(String[] args) {
SayHelloClient main = new SayHelloClient();
SayHelloRemote remote = main.lookupSayHelloBean();
remote.sayHello();
}
private SayHelloRemote lookupSayHelloBean() {
try {
c = new InitialContext(prop);
return (SayHelloRemote) c.lookup("SayHelloBean/remote");
} catch (NamingException ne) {
throw new RuntimeException(ne);
}
}
}

Versão 3.1 Draft

Com a eliminação da classe home não precisamos mais fazer o narrow no código cliente. Agora basta efetuar um lookup na classe Bean.

Classe de implementação de negócio
// Sem Interfaces Remota ou Local
import javax.ejb.Stateless;

@Stateless
public class SayHelloBean implements SayHelloRemote, SayHelloLocal{
public void hello() {
System.out.println("Hello");
}
}

Cliente que executa o EJB

public class SayHelloClient {
// Se o cliente estiver no conteiner, Injeção nele !!!
// Veja que estamos referenciando ao session bean
// SayHelloBean e não SayHelloRemote.

@EJB SayHelloBean falaAi;
@Resource SessionContext c;
public SayHelloClient(){}
public static void main(String[] args) {
falaAi.hello();
SayHelloClient main = new SayHelloClient();
SayHelloBean sayAgain = main.lookupSayHelloBean();
sayAgain.hello();
}

// Ou podemos fazer via JNDI
private SayHelloRemote lookupSayHelloBean() {
try {
return (SayHelloBean) c.lookup("sayHello");
} catch (NamingException ne) {
throw new RuntimeException(ne);
}
}
}

Podemos observar que esta muito mais simples criar EJBs, sem as interfaces o código fica muito mais limpo e temos uma programação muito próximo de objetos POJO, entretanto, por motivos já conhecidos não é possível instanciar um objeto com o operador new, somente via injeção ou jndi. Para a especificação 2.1 por exemplo, tínhamos que criar 4 objetos para um simples hello world (local), na especificação 3.0 não é obrigatório implementar a interface SessionBean na classe session bean ou a interface Serializable, assim como as classes Interceptors. Viva a injeção de dependência !!!
Veja que ainda precisamos de 3 objetos (sai a interface home) mas o processo já simplifica com a entrada das annotations. Agora na versão 3.1 foi eliminada a necessidade de interfaces (lembre-se, apenas para objetos locais)..
Para criar um EJB sem utilizar a interface, ela deve seguir algumas seguintes regras, ela deve expor os métodos sem interface via definição de classe ou pelo deployment descriptor. E ... :
  • Se o bean não expor nenhuma outra interface(s) e não implementar nenhuma interface, então o bean define uma EJB sem interface.
  • As seguintes interfaces são excluídas quando implementamos um EJB sem interface : java.io.Serializable, java.io.Externalizable; E qualquer outra interface definida no pacote javax.ejb.
  • Todos os métodos da classe bean e qualquer superclasse são expostos como métodos de negócios através de um bean sem interface. E isto inclui os métodos de callback (muito cuidado !!!).
  • Os métodos expostos da classe bean sem interface não devem lançar a exceção java.rmi.RemoteException.

Ainda tem várias coisas legais para discutir, provavelmente será possível rodar EJB no TomCat :D, com o pacote light do EJB, no próximo post vou cobrir o calendário para o Timer e concorrência !!!

0 comentários: