07 noviembre, 2006

Enterprise JavaBeans

Los Enterprise JavaBeans (EJB) son componentes Java que viven (y corren) dentro del EJB Container, en la capa de aplicación o de negocio.

Veamos el gráfico de las n-capas para ubicarnos:



El concepto de componente implica que “tiene sentido dentro de un contexto”. Es decir, un EJB solo tiene sentido dentro del contexto del EJB Container y no se puede utilizar fuera de él.

Los EJB “viven” dentro del EJB container y este les provee una serie de servicios que llamaremos servicios de plataforma. Por ejemplo:
  • Seguridad
  • Multithreading
  • Networking (objetos distribuidos)
  • Transacciones
  • Connection Pooling
son algunos de los servicios provistos por el container y por consiguiente temas resueltos para el programador del EJB.

Pero claro, para poder disfrutar de este conjunto de servicios debemos “acatar” ciertas condiciones que hacen que programar un EJB no sea una tarea tan simple y feliz como programar una clase java común.

Por ejemplo: para programar un EJB minimamente tenemos que escribir tres archivos .java y mantener dos archivos .xml.

Si consideramos un EJB Calculadora, entonces los archivos que debemos escribir son:
  • CalculadoraHome.java - interface home, define los métodos de instanciación del componente
  • Calculadora.java - interface remota, define los "métodos de negocio" del componente (en nuestro caso estos métodos serán sumar, restar, multiplicar, etc)
  • CalculadoraBean.java - clase de implementación, implementa las interfaces home y remota
y
  • ejb-jar.xml - descriptor estandar J2ee
  • propietario.xml - descriptor propietario. Este archivo dependerá del container con el que estemos trabajando
Al ser el EJB un componente, el mismo se ejecuta dentro del container y el usuario (el programador que lo quiere usar) solo puede accederlo a través de dos interfaces que el EJB debe proveer: la interface home y la interface remota.

En la interface home se definen los métodos de instanciación del EJB. Debe quedar claro que para instanciar un EJB no se puede simplemente hacer:



porque el EJB es un componente que vive en un container. Es un objeto remoto. La instancia se creará en otra maquina (en el server) y nosotros, desde el main (cliente) obtendremos una referencia a dicha instancia. Luego le aplicaremos métodos como si fuera un objeto local pero fisicamente la instancia y la ejecución del método será remota (en el servidor).

Para acceder al EJB primero debemos buscarlo en el servicio de nombres y directorios utilizando JNDI. Decimos que hacemos un lookup. JNDI nos dará una referencia a la interface home y la inteface home nos dará acceso a la interface remota que tiene la definición de los métodos del componente (los que nos interesa invocar).



Tipos de EJB

Existen tres tipos de EJB: Session Beans, Entity Beans y Message Driven Beans. En este apunte solo consideraremos los Session Beans.


Session Beans

Los Session EJB representan una sesión entre el cliente y el servidor. Se pueden definir satatefull o stateless. En el primer caso el container mantendrá una instancia del EJB asociada a cada usuario que establezca una sesión. En el segundo caso el container manejará una única instancia (o un pool de instancias) pero ninguna será excusiva de ningún cliente.

Si necesitamos tener información asociada a un cliente (por ejemplo los items cargados en un changuito de compras) tenemos que utilizar un statefull.


Desarrollando un EJB (tutorial)

Desarrollaremos un EJB Calculadora utilizando Eclipse y MyEclipse como plugin J2ee.

Nuestro EJB tendrá un método sumar el cual recibirá como parámetros los operandos (double) y retornará la suma de los mismos (double).


Resumen Antes de Comenzar

1 - Vamos a crear un nuevo proyecto "EJB Project". Este tipo de proyectos aparecen luego de instalar el plugin MyEclipse.

2 - Solo nos preocuparemos por programar la clase de implementación que (en el caso del EJB Calculadora) será CalculadoraBean.java. Las interfaces home y remota así como el descriptor ejb-jar.xml las crearemos automaticamente utilizando XDoclet.

3 - XDoclet es una herramienta open source para generar código automaticamente a partir de ciertos atributos o "meta data". Es decir: si documentamos apropiadamente la clase CalculadoraBean.java XDoclet la podrá tomar como base para generar todo lo demás.

XDoclet viene incorporado con MyEclipse por lo tanto debemos configurarlo en el proyecto en el que estamos desarrollando el EJB. Una vez configurado lo corremos para generar las interfaces y el descriptor xml.

4 - Con todo generado tenemos que levantar el servidor y luego hacer el deploy del componente. El término "hacer el deploy" se refiere a instalar el EJB en el server.

5 - Por último tenemos que programar un cliente (un main) para "lookupear" el EJB y aplicarle el método sumar. Recordemos que el EJB queda publicado en el servicio de nombres por lo tanto lo tenemos que buscar usando JNDI.


Ahora si...

1 - Primero tenemos que crear un nuevo proyecto con capacidad para contener EJBs. Como tenemos instalado MyEclipse, el wizard de proyectos nos mostrará una carpeta "MyEclipse" con una subcarpeta "J2EE Projects" y dentro de esta encontraremos "EJB Project".



2 - Luego de crear el proyecto podremos ver en el package explorer la siguiente estructura.



3 - Sobre el proyecto o sobre la carpeta src (con el botón derecho) creamos un "new Session Bean"



4 - Lo que realmente estaremos creando será la clase de implementación del EJB. En nuestro ejemplo (de calculadora) esta clase la llamaremos CalculadoraBean.

Vemos que también podemos definir si el Session Bean será stateless o statefull. Es este caso eligiremos stateless.



5 - Luego de crear el Session Bean tendremos la clase CalculadoraBean.java. Esta clase (generada a partir de un template) tendrá un método genérico como el siguiente:



Tenemos que reemplazarlo por el método que realmente queremos que tenga el EJB. Nosotros lo reemplazaremos por el método sumar.



Es muy importante no borrar la documentación. Dentro de la documentación vemos el tag:
  • @ejb.interface-method view-type = "remote"
Este tag es información de XDoclet. Utilizaremos XDoclet para generar (a partir de CalculadoraBean.java) las interfaces home, remota y los descriptores xml del EJB.


6 - Tenemos que configurar el proyecto para poder ejecutar XDoclet sobre el EJB. En las propiedades del proyecto, en la opcion "MyEclipse-XDoclet" presionando el botón "Add Standard" aparece la ventana para elegir la configuración. Seleccionamos "Standard EJB".



7 - Ahora que nuestro proyecto tiene configurado XDoclet podemos ejecutarlo para generar los descriptores y las interfaces home y remota. Esto lo podemos hacer presionando "botón derecho" sobre el proyecto, "MyEclipse" -> "Run XDoclet".



8 - Luego de la corrida de XDoclet vemos que se generaron (en el package explorer) los siguientes archivos:
  • CalculadoraHome.java
  • Calculadora.java
  • ejb-jar.xml
Los archivos CalculadoraSession.java y CalculadoraUtil.java los genera MyEclipse a partir del template que trae por default. Simplemente los vamos a ignorar.



9 - El próximo paso es levantar el server. En este caso utilizaremos JBoss 3.2.7



10 - Ahora tenemos que hacer el deploy del componente (el EJB) en el server. Esto significa empaquetar las clases, las interfaces y los descriptores en un .jar e instalarlo en el servidor.

En JBoss deployar un EJB es simple: solo tenemos que copiar el .jar en el directorio /deploy de la configuración de server que estemos utilizando. Pero Teniendo MyEclipse es más simple todavía. El botón inmediatamente a la izquierda del que utilizamos para levantar el server es el botón para deployar. Lo presionamos y aparecerán estas ventanas:



11 - Si todo salio bien la consola mostrará la siguiente salida:




12 - Ahora desarrollamos un cliente (un método main) para instanciar (hacer el lookup de) nuestro EJB e invocarle el método sumar.




13 - Solo falta tener en cuenta un último temita: las clases que encapsulan la comunicación entre el cliente y el servidor. Estas clases las provee el fabricante del container y, en el caso de JBoss vienen dentro del archivo /jboss-home/client/jbossall-client.jar. Es decir: tenemos que incorporar este archivo al CLASSPATH del cliente antes de poderlo correr.


Mejorando el Cliente

Podemos ver que invocar (o lookupear) un EJB es demasiado engorroso. El cliente (en este caso la función main()) no se puede abstraer del hecho de que está utilizando un EJB.

En dos pasos vamos a intentar eliminar toda esa complejidad de forma tal que invocar un EJB sea algo transparente para el cliente.


Service Locator

Definiremos una clase cuyo objetivo es lookupear un EJB. Se la llama genericamente Service Locator. Podemos decir que en esta clase "hacemos el trabajo sucio".



Vemos que esta clase tiene un método getService() que recibe el nombre de JNDI del EJB y la clase de su interface home.

Antes de mandar el lookup verifica si el EJB (o mejor dicho su interface home) no está previamente en una Hashtable. Si está entonces no lookupea y directamente retorna la de la tabla.

Si no esta en la tabla entonces hace el lookup, guarda la interface home en la tabla y retorna el objeto.

Con esto ahorramos lookupear más de una vez la misma interface home.

Debemos notar que la interface home es "una fábrica de instancias". Con una instancia de la interface home podemos crear varias instancias del EJB.

Ahora el main() queda de la siguiente manera:




Business Delegate

Viendo el main() anterior podemos observar que, si bien mejoró bastante, no termina de abstraerse del hecho de que trabaja con un EJB. Simplemente con tener que castear a la interface home ya hace que parezca algo complicado.

El patrón de diseño Business Delegate nos ayudará a solucionar este problema.

Business Delegate propone hacer una clase Java común que tenga los mismos métodos que expone el EJB en su interface remota y resolverlos delegando en el EJB. En nuestro caso el único método será sumar().



Ahora veamos como queda la función main()



Como podemos ver, logramos que el main() no se entere de que existe un EJB por detrás. Para el main() simplemente existe un objeto común llamado CalculadoraDelegate que tiene un método sumar().

Más adelante volveremos a explicar este tema pero con más detalle.