Los contenidos que vamos a ver en el tema son:
Con la tecnología J2EE Enterprise JavaBeans es posible desarrollar componentes
(enterprise beans) que luego puedes reutilizar y ensamblar en distintas
aplicaciones que tengas que hacer para la empresa. Por ejemplo, podrías
desarrollar un bean Cliente que represente un cliente en una base
de datos. Podrías usar después ese bean Cliente en
un programa de contabilidad o en una aplicación de comercio electrónico
o virtualmente en cualquier programa en el que se necesite representar un cliente.
De hecho, incluso sería posible que el desarrollador del bean y el ensamblador
de la aplicación no fueran la misma persona, o ni siquiera trabajaran
en la misma empresa.
El desarrollo basado en componentes promete un paso más en el camino de la programación orientada a objetos. Con la programación orientada a objetos puedes reutilizar clases, pero con componentes es posible reutilizar n mayor nivel de funcionalidades e incluso es posible modificar estas funcionalidades y adaptarlas a cada entorno de trabajo particular sin tocar el código del componente desarrollado. Aunque veremos el tema con mucho más detalle, en este momento puedes ver un componente como un objeto tradicional con un conjunto de servicios adicionales soportados en tiempo de ejecución por el contenedor de componentes. El contenedor de componentes se denomina contenedor EJB y es algo así como el sistema operativo en el que éstos residen. Recuerda que en Java existe un modelo de programación de objetos remotos denominado RMI. Con RMI es posible enviar peticiones a objetos que están ejecutándose en otra máquina virtual Java. Podemos ver un componente EJB como un objeto remoto RMI que reside en un contenedor EJB que le proporciona un conjunto de servicios adicionales.
Cuando estés trabajando con componentes tendrás que dedicarle tanta atención al despliegue (deployment) del componente como a su desarrollo. Entendemos por despliegue la incorporación del componente a nuestro contenedor EJB y a nuestro entorno de trabajo (bases de datos, arquitectura de la aplicación, etc.). El despliegue se define de forma declarativa, mediante un fichero XML (descriptor del despliegue, deployment descriptor) en el que se definen todas las características del bean.
El desarrollo basado en componentes ha creado expectativas sobre la aparición de una serie de empresas dedicadas a implementar y vender componentes específicos a terceros. Este mercado de componentes nunca ha llegado a tener la suficiente masa crítica como para crear una industria sostenible. Esto es debido a distintas razones, como la dificultad en el diseño de componentes genéricos capaces de adaptarse a distintos dominios de aplicación, la falta de estandarización de los dominios de aplicación o la diversidad de estos dominios. Aun así, existe un campo creciente de negocio en esta área (puedes ver, como ejemplo, www.componentsource.com).
En el apartado anterior hemos comentado que la diferencia fundamental entre los componentes y los objetos clásicos reside en que los componentes viven en un contenedor EJB que los envuelve proporcionando una capa de servicios añadidos. ¿Cuáles son estos servicios? Los más importantes son los siguientes:
Pensad en lo complicado que sería programar una clase "a mano" que implementara todas estas características. Como se suele decir, la programación de EJB es sencilla si la comparamos con lo que habría que implementar de hacerlo todo por uno mismo. Evidentemente, si en la aplicación que estás desarrollando no vas a necesitar estos servicios y va a tener un interfaz web, podrías utilizar simplemente páginas JSP y JDBC.
El funcionamiento de los componentes EJB se basa fundamentalmente en el trabajo del contenedor EJB. El contenedor EJB es un programa Java que corre en el servidor y que contiene todas las clases y objetos necesarios para el correcto funcionamiento de los enterprise beans.
En la figura siguiente puedes ver una representación de muy alto nivel del funcionamiento básico de los enterprise beans. En primer lugar, puedes ver que el cliente que realiza peticiones al bean y el servidor que contiene el bean están ejecutándose en máquinas virtuales Java distintas. Incluso pueden estar en distintos hosts. Otra cosa a resaltar es que el cliente nunca se comunica directamente con el enterprise bean, sino que el contenedor EJB proporciona un EJBObject que hace de interfaz. Cualquier petición del cliente (una llamada a un método de negocio del enterprise bean) se debe hacer a través del objeto EJB, el cual solicita al contenedor EJB una serie de servicios y se comunica con el enterprise bean. Por último, el bean realiza las peticiones correspondientes a la base de datos.
El contenedor EJB se preocupa de cuestiones como:
Figura 1.1: representación de alto nivel del funcionamiento de los enterprise beans.
Vamos a ver un ejemplo para que puedas entender mejor el flujo de llamadas.
Supongamos que tenemos una aplicación de bolsa y el bean proporciona
una implementación de un Broker. La interfaz de negocio del Broker está
compuesta de varios métodos, entre ellos, por ejemplo, los métodos
compra o vende. Supongamos que desde el objeto cliente
queremos llamar al método compra. Esto va a provocar la
siguiente secuencia de llamadas:
Por cierto, la idea de usar este tipo de diálogos para describir el funcionamiento de un proceso o una arquitectura de un sistema informático es de Kathy Sierra en sus libros "Head First Java" y "Head First EJB". Se trata de un tipo de libros radicalmente distintos a los habituales manuales de Java que consiguen que realmente aprendas este lenguaje cuando los sigues. Échales un vistazo si tienes oportunidad.
Los contenidos que vamos a ver en este apartado son:
En este apartado vamos a repasar algunos conceptos fundamentales para entender el funcionamiento de la arquitectura EJB: los stubs y skeletons, los objetos remotos y el paso de parámetros y devolución de resultados en las llamads remotas.
RMI (Remote Method Invocation) define la forma de comunicación remota entre objetos Java situados en máquinas virtuales distintas. Supongamos que un objeto cliente quiere hacer una petición a un objeto remoto situado en otra JVM (Máquina Virtual Java, Java Virtual Machine). RMI pretende hacer trasparente la presencia de la red, de forma que cuando escribas el código de los objetos clientes y remotos no tengas que tratar con la complicación de gestionar la comunicación física por la red.
Para ello, RMI proporciona al cliente un objeto proxy (llamado stub) que recibe la petición del objeto cliente y la transforma en algo que se puede enviar por la red hasta el objeto remoto. Este stub se hace cargo de todos los aspectos de bajo nivel (streams y sockets). En el lado del servidor, un objeto similar (llamado skeleton) recibe la comunicación, la desempaqueta y lanza el método correspondiente del objeto remoto al que sirve. Al igual que la petición, se deben empaquetar los argumentos de la llamada. El programador sólo debe definir el código del método en el objeto remoto. Los objetos stub y skeleton los construye el compilador de RMI de forma automática.
La siguiente figura muestra un ejemplo con el objeto remoto SaludoImpl:

El objeto remoto SaludoImpl está sombreado para indicar
que en él se encuentra la implementación de los métodos
remotos.
Una vez realizada la llamada, el stub queda en espera (y el objeto cliente que ha llamado al método correspondiente del stub) hasta recibir la respuesta del skeleton (respuesta que debe proprocionar el método invocado en el objeto remoto). Si la respuesta no se recibe, el stub lanza una excepción que debe capturar el objeto cliente.
Es posible que en el servidor no exista un objeto skeleton por cada objeto remoto sino que, para hacer la arquitectura más eficiente, se pueda definir un objeto genérico que distribuya las peticiones a los objetos remotos con algún mecanismo de identificación de la petición y de caché de objetos remotos. Por eso en las siguientes imágenes que mostremos no aparecerá este objeto.

Concretando más, para implementar y usar una clase remota con RMI debemos cumplir las siguientes condiciones
java.rmi.Remote.RemoteException.rmic para que cree las clases
stub y skeletonLa siguiente imagen muestra la estructura de clases e interfaces que se usan
para definir una sencilla clase remota llamada Saludo. Se define
la interfaz Saludo que extiende la interfaz Remote.
La clase SaludoImpl la escribe el programador e implementa la interfaz
Saludo. La clase SaludoStub también implementa
la interfaz Saludo y la construye el compilador de RMI. Por último,
el cliente se comunica con un objeto instancia de la clase SaludoStub
que implementa la interfaz Saludo. Esta interfaz es la única
que ve el cliente.

Al implementar la interfaz Saludo, los objetos de la clase SaludoImp
son también objetos Remote, ya que la interfaz Saludo
hereda de la interfaz Remote.
Muy importante: a clase stub se debe instalar en la JVM del objeto cliente para que éste pueda usarla.
Pregunta:
En cuanto a los argumentos y los valores devueltos por las llamadas remotas, deben ser de uno de los siguientes tipos:
java.rmi.RemoteUn caso muy interesante, por la frecuencia con la que sucede en la arquitectura EJB, es el de un objeto remoto que es devuelto en una llamada a otro objeto también remoto. El siguiente ejemplo proporciona una representación de lo que ocurre en este caso.
Supongamos que tenemos un conjunto de objetos remotos de la clase Estudiante,
cada uno con un identificador determinado. Supongamos también un objeto
remoto llamado estudianteFactory que puede localizar al objeto
Estudiante con un identificador determinado.
estudianteFactory. El stub trasmite la llamada al skeleton.estudianteFactory. El objeto remoto localiza
el objeto estudiante que se solicita y devuelve su referencia al Skeleton.
Al ser estudiante un objeto remoto, estará disponible su stub en la
JVM.estudianteFactory serializa el stub de
estudiante y se lo pasa al skeleton, el cual lo trasmite al stub. El stub
lo desempaqueta y crea una copia local en la JVM del cliente. Por último,
el stub devuelve al objeto cliente una referencia local al stub de estudiante.
Pregunta:
Es muy importante recordar que cuando pasas un objeto serializado de una JVM a otra, la JVM que lo recibe debe tener disponible el fichero class correspondiente al tipo del objeto que pasas. Esto sucede así incluso cuando pasas un stub. Si la JVM a la que pasas el stub no tiene su definición, obtendrás un error.
¿Cómo usa la arquitectura EJB la tecnología RMI? Comencemos por presentar la siguiente figura, que muestra la diferencia fundamental entre EJB y RMI.

En el lado del cliente todo es igual. El cliente sigue comunicándose
con un stub que implementa la interfaz Saludo. Sin embargo, los
cambios se encuentran en el lado del servidor. El objeto remoto (Saludo
EJBObject) no tiene la implementación realizada por el programador,
sino que ésta se encuentra en el llamado objeto Bean (SaludoBean).
El objeto remoto hace de "cortafuegos" que separa el bean de los clientes y
permite intercalar las llamadas al contenedor en las peticiones de los clientes.
Así es posible incorporar los servicios de transacciones, seguridad,
etc., proporcionados por el servidor.
Además del objeto remoto que implementa la interfaz del bean (llamada
interfaz componente), la arquitectura EJB proporciona otro objeto remoto llamado
objeto home que hace el papel de factoría del bean. Este objeto home
(SaludoHome, en el ejemplo) proprociona al cliente métodos
remotos para crear y localizar instancias de beans. La siguiente figura proporciona
una imagen completa de ambas características:

Veamos con detalle las clases que se deben implementar para defir un enterprise bean, así como las interfaces que extienden:
SaludoBean que implementa los métodos
de negocio y que debe heredar de la clase javax.bean.SessionBean.Saludo que define los métodos
accesibles por el cliente y que debe heredar de la interfaz javax.ejb.EJBObject.SaludoHome que define los métodos
de creación de beans y que debe heredar de la interfaz javax.ejb.EJBHome.A su vez, las interfaces EJBObject y EJBHome heredan
de la interfaz RMI Remote. Esto hace que las interfaces Saludo
y SaludoHome sean también remotas. Sin embargo, ni la clase
SaludoBean ni la clase SessionBean implementan la
interfaz Remote. Esto es muy importante para entender la arquitectura
de EJB: la clase bean no es remota. Recuerda que el objetivo
principal de la estructura de clases e interfaces de EJB es evitar que el cliente
llame directamente al bean. Existe siempre un objeto intermedio, el EJBObject,
que intercepta las llamadas de los clientes y realiza todos los servicios que
proporciona el servidor de aplicaciones.
La siguiente figura representa la estructura de clases del ejemplo que hemos visto.

¿Quién crea las clases que implementan todas estas interfaces de las que estamos hablando? Evidentemente, como ya hemos dicho, esa es tarea del contenedor EJB. La siguiente tabla resume quién escribe cada una de las clases de un bean.
| Programador | Contenedor de aplicaciones |
| La interfaz componente que hereda de javax.ejb.EJBObject. (Saludo.java) |
La clase EJBObject que implementa la interfaz componente. (Objeto remoto SaludoEJB.java) |
La clase stub EJBObject que implementa la interfaz componente y sirve
para comunicarse por la red con la clase EJBObject anterior. (SaludoStub.java) |
|
| La interfaz home que hereda de |
La clase EJBHome que implementa la interfaz home. (SaludoHome.java) |
La clase stub EJBHome que implementa la interfaz home y sirve para comunicarse
por la red con la clase EJBHome anterior. (SaludoHomeStub.java) |
|
La clase bean que implementa javax.ejb.SessionBean o javax.ejb.EntityBean.( SaludoBean.java) |
Hasta ahora hemos considerado siempre que el cliente y el bean se encuentran en distintas máquinas virtuales. Por eso es necesario RMI para conectar el cliente y el EJBObject. Pero ¿por qué introducir RMI cuando el cliente y el EJBObject se encuentran en la misma JVM? La introducción de RMI en la arquitectura la dota de flexibilidad, pero también le añade penalización en el rendimiento, debido sobre todo a la necesidad de serializar todos los argumentos y llamadas. Esta penalización no está justificada cuando el objeto cliente del bean es, por ejemplo, un servlet o una página JSP que reside en la misma JVM que el bean. Tampoco está justificado el uso de RMI cuando se están comunicando dos beans que residen en la misma JVM.
La especificación 2.0 de EJB propone el uso de las interfaces locales
EJBLocalHome y EJBLocalObject como una solución
a estas situaciones. A la hora de programar el bean lo único que cambia
es que las interfaces home y componente deben heredar de EJBLocalHome
y EJBLocalObject. Estas interfaces ya no son remotas, por lo que
los métodos no van a tener que declarar la excepción RemoteException.
En el nombre de las interfaces suelen añadirse la palabra local, para
indicar que se tratan de objetos que van a llamarse sin usar RMI. En el caso
del bean Saludo, llamaríamos SaludoLocal a
la interfaz componente y SaludoLocalHome a la interfaz home.
package moduloEjb;
import javax.ejb.*;
public interface SaludoLocal extends EJBLocalObject {
public String saluda();
}
package moduloEjb;
import javax.ejb.*;
public interface SaludoHomeLocal extends EJBLocalHome {
public SaludoLocal create() throws CreateException;
}
En la arquitectura EJB, la única diferencia es que las llamadas entre el cliente y el EJBObject serían llamadas normales entre objetos Java y no tendrían que ser serializadas. Pero el objeto EJBObject seguiría haciendo las mismas funciones de proteger a los beans del exterior y de incorporar los servicios del contenedor EJB.
Por último, en el lado del cliente existen dos detalles que hay que
modificar cuando se están utilizando interfaces locales. En primer lugar,
las interfaces locales no declaran la excepción RemoteException,
por lo que no es necesario que el cliente capture estas excepciones con un try/catch.
El segundo aspecto tiene que ver con el casting del objeto home que
obtenemos de JNDI. En el caso de que el objeto home sea remoto, vimos
en la sesión anterior que antes de hacer el casting había que
convertirlo en un objeto Java con la llamada al método PotableRemoteObject.narrow().
En el caso de estar trabajando con interfaces locales no es necesario hacer
esta conversión con lo que para hacer el casting bastaría con
hacer:
Object ref = jndiContext.lookup("SaludoEJB");
SaludoHome home = (SaludoHome) ref;
Terminamos con unas breves consideraciones que nos pueden ayudar a determinar si conviene usar interfaces locales o remotas:
La clase home de un bean define los métodos de creación de instancias del bean. Cuando se despliega un bean en el contenedor EJB, se crea automáticamente una única instancia de la clase home del bean desplegado. Esta instancia reside en el contenedor EJB y queda a la espera de recibir peticiones de los clientes para crear instancias del bean.
El funcionamiento completo de la clase home se podría resumir con la siguiente figura y los siguientes pasos (para simplificar vamos a usar como ejemplo el bean de sesión sin estado SaludoBean que vimos en la sesión anterior).

Esta descripción del funcionamiento de la clase home, como hemos dicho, contempla únicamente el caso en de los beans de sesión sin estado. El funcionamiento es distinto según el tipo de bean. Cuando veamos el ciclo de vida de cada uno de los tipos de bean explicaremos cómo funciona la clase home en el resto de casos.
La tecnología EJB define tres tipos de beans: beans de sesión, beans de entidad y beans dirigidos por mensajes.
Los beans de entidad representan un objeto concreto que tiene existencia en alguna base de datos de la empresa. Una instancia de un bean de entidad representa una fila en una tabla de la base de datos. Por ejemplo, podríamos considerar el bean Cliente, con una instancia del bean siendo Eva Martínez (ID# 342) y otra instancia Francisco Gómez (ID# 120).
Los beans dirigidos por mensajes pueden escuchar mensajes de un servicio de mensajes JMS. Los clientes de estos beans nunca los llaman directamente, sino que es necesario enviar un mensaje JMS para comunicarse con ellos. Los beans dirigidos por mensajes no necesitan objetos EJBObject porque los clientes no se comunican nunca con ellos directamente. Un ejemplo de bean dirigido por mensajes podría ser un bean ListenerNuevoCliente que se activara cada vez que se envía un mensaje comunicando que se ha dado de alta a un nuevo cliente.
Por último, un bean de sesión representa un proceso o una acción de negocio. Normalmente, cualquier llamada a un servicio del servidor debería comenzar con una llamada a un bean de sesión. Mientras que un bean de entidad representa una cosa que se puede representar con un nombre, al pensar en un bean de sesión deberías pensar en un verbo. Ejemplos de beans de sesión podrían ser un carrito de la compra de una aplicación de negocio electrónico o un sistema verificador de tarjetas de crédito.
Vamos a describir con algo más de detalle estos tipos de bean. Comenzamos con los beans de sesión para continuar con los de entidad y terminar con los dirigidos por mensajes.
Los beans de sesión representan sesiones interactivas con uno o más clientes. Los bean de sesión pueden mantener un estado, pero sólo durante el tiempo que el cliente interactúa con el bean. Esto significa que los beans de sesión no almacenan sus datos en una base de datos después de que el cliente termine el proceso. Por ello se suele decir que los beans de sesión no son persistentes.
A diferencia de los bean de entidad, los beans de sesión no se comparten entre más de un cliente, sino que existe una correspondencia uno-uno entre beans de sesión y clientes. Por esto, el contenedor EJB no necesita implementar mecanismos de manejo de concurrencia en el acceso a estos beans.
Existen dos tipos de beans de sesión: con estado y sin él.
Los beans de sesión sin estado no se modifican con las llamadas de los clientes. Los métodos que ponen a disposición de las aplicaciones clientes son llamadas que reciben datos y devuelven resultados, pero que no modifican internamente el estado del bean. Esta propiedad permite que el contenedor EJB pueda crear una reserva (pool) de instancias, todas ellas del mismo bean de sesión sin estado y asignar cualquier instancia a cualquier cliente. Incluso un único bean puede estar asignado a múltiples clientes, ya que la asignación sólo dura el tiempo de invocación del método solicitado por el cliente.
Una de las ventajas del uso de beans de sesión, frente al uso de clases Java u objetos RMI es que no es necesario escribir los métodos de los beans de sesión de una forma segura para threads (thread-safe), ya que el contenedor EJB se va a encargar de que nunca haya más de un thread accediendo al objeto. Para ello usa múltiples instancias del bean para responder a peticiones de los clientes.
Cuando un cliente invoca un método de un bean de sesión sin estado, el contenedor EJB obtiene una instancia de la reserva. Cualquier instancia servirá, ya que el bean no puede guardar ninguna información referida al cliente. Tan pronto como el método termina su ejecución, la instancia del bean está disponible para otros clientes. Esta propiedad hace que los beans de sesión sin estado sean muy escalables para un gran número de clientes. El contenedor EJB no tiene que mover sesiones de la memoria a un almacenamiento secundario para liberar recursos, simplemente puede obtener recursos y memoria destruyendo las instancias.
Los beans de sesión sin estado se usan en general para encapsular procesos
de negocio, más que datos de negocio (tarea de los entity beans). Estos
beans suelen recibir nombres como ServicioBroker o GestorContratos
para dejar claro que proporcionan un conjunto de procesos relacionados con un
dominio específico del negocio.
Es apropiado usar beans de sesión sin estado cuando una tarea no está ligada a un cliente específico. Por ejemplo, se podría usar un bean sin estado para enviar un e-mail que confirme un pedido on-line o calcular unas cuotas de un préstamo.
También puede usarse un bean de sesión sin estado como un puente de acceso a una base de datos o a un bean de entidad. En una arquitectura cliente-servidor, el bean de sesión podría proporcionar al interfaz de usuario del cliente los datos necesarios, así como modificar objetos de negocio (base de datos o bean de entidad) a petición de la interfaz. Este uso de los beans de sesión sin estado es muy frecuente y constituye el denominado patrón de diseño session facade.
Algunos ejemplos de bean de sesión sin estado podrían ser:
En un bean de sesión con estado, las variables de instancia del bean almacenan datos específicos obtenidos durante la conexión con el cliente. Cada bean de sesión con estado, por tanto, almacena el estado conversacional de un cliente que interactúa con el bean. Este estado conversacional se modifica conforme el cliente va realizando llamadas a los métodos de negocio del bean. El estado conversacional no se guarda cuando el cliente termina la sesión.
La interacción del cliente con el bean se divide en un conjunto de pasos.
En cada paso se añade nueva información al estado del bean. Cada
paso de interacción suele denominarse con nombres como setNombre
o setDireccion, siendo nombre y direccion
dos variables de instancia del bean.
Algunos ejemplos de beans de sesión con estado podrían ser:
El estado del bean persiste mientras que existe el bean. A diferencia de los beans de entidad, no existe ningún recurso exterior al contenedor EJB en el que se almacene este estado.
Debido a que el bean guarda el estado conversacional con un cliente determinado, no le es posible al contenedor crear un almacén de beans y compartirlos entre muchos clientes. Por ello, el manejo de beans de sesión con estado es más pesado que el de beans de sesión sin estado.
En general, se debería usar un bean de sesión con estado si se cumplen las siguientes circunstancias:
Los beans de entidad modelan conceptos o datos de negocio que puede expresarse
como nombres. Esto es una regla sencilla más que un requisito formal,
pero ayuda a determinar cuándo un concepto de negocio puede ser implementado
como un bean de entidad. Los beans de entidad representan cosas:
objetos del mundo real como hoteles, habitaciones, expedientes, estudiantes,
y demás. Un bean de entidad puede representar incluso cosas abstractas
como una reserva. Los beans de entidad describen tanto el estado como la conducta
de objetos del mundo real y permiten a los desarrolladores encapsular las reglas
de datos y de negocio asociadas con un concepto específico. Por ejemplo
un bean de entidad Estudiante encapsula los datos y reglas de negocio
asociadas a un estudiante. Esto hace posible manejar de forma consistente y
segura los datos asociados a un concepto.
Los beans de entidad se corresponden con datos en un almacenamiento persistente (base de datos, sistema de ficheros, etc.). Las variables de instancia del bean representan los datos en las columnas de la base de datos. El contenedor debe sincronizar las variables de instancia del bean con la base de datos. Los beans de entidad se diferencian de los beans de sesión en que las variables de instancia se almacenan de forma persistente.
Aunque entraremos en detalle más adelante, es interesante adelantar que el uso de los beans de entidad desde un cliente conlleva los siguientes pasos:
ejbLoad().ejbStore().Son muchas las ventajas de usar beans de entidad en lugar de acceder a la base
de datos directamente. El uso de beans de entidad nos da una perspectiva orientada
a objetos de los datos y proporciona a los programadores un mecanismo más
simple para acceder y modificar los datos. Es mucho más fácil,
por ejemplo, cambiar el nombre de un estudiante llamando a student.setName()
que ejecutando un comando SQL contra la base de datos. Además, el uso
de objetos favorece la reutilización del software. Una vez que un bean
de entidad se ha definido, su definición puede usarse a lo largo de todo
el sistema de forma consistente. Un bean Estudiante proporciona
un forma completa de acceder a la información del estudiante y eso asegura
que el acceso a la información es consistente y simple.
La representación de los datos como beans de entidad puede hacer que el desarrollo sea más sencillo y menos costoso.
Los beans de entidad se diferencian de los beans de sesión, principalmente, en que son persistentes, permiten el acceso compartido, tienen clave primaria y pueden participar en relaciones con otros beans de entidad:
Persistencia
Acceso compartidoDebido a que un bean de entidad se guarda en un mecanismo de almacenamiento se dice que es persistente. Persistente significa que el estado del bean de entidad existe más tiempo que la duración de la aplicación o del proceso del servidor J2EE. Un ejemplo de datos persistentes son los datos que se almacenan en una base de datos.
Los beans de entidad tienen dos tipos de persistencia: Persistencia Gestionada por el Bean (BMP, Bean-Managed Persistence) y Persistencia Gestionada por el Contenedor (CMP, Container-Managed Persistence). En el primer caso (BMP) el bean de entidad contiene el código que accede a la base de datos. En el segundo caso (CMP) la relación entre las columnas de la base de datos y el bean se describe en el fichero de propiedades del bean, y el contenedor EJB se ocupa de la implementación.
Clave primariaLos clientes pueden compartir beans de entidad, con lo que el contenedor EJB debe gestionar el acceso concurrente a los mismos y por ello debe usar transacciones. La forma de hacerlo dependerá de la política que se especifique en los descriptores del bean.
Cada bean de entidad tiene un identificador único. Un bean de entidad alumno, por ejemplo, puede identificarse por su número de expediente. Este identificador único, o clave primaria, permite al cliente localizar a un bean de entidad particular.
Relaciones
De la misma forma que una tabla en una base de datos relacional, un bean de entidad puede estar relacionado con otros EJB. Por ejemplo, en una aplicación de gestión administrativa de una universidad, el bean
alumnoEjby el beanactaEjbestarían relacionados porque un alumno aparece en un acta con una calificación determinada.Las relaciones se implementan de forma distinta según se esté usando la persistencia manejada por el bean o por el contenedor. En el primer caso, al igual que la persistencia, el desarrollador debe programar y gestionar las relaciones. En el segundo caso es el contenedor el que se hace cargo de la gestión de las relaciones. Por ello, estas últimas se denominan a veces relaciones gestionadas por el contenedor.
Son el tercer tipo de beans propuestos por la última especificación de EJB. Estos beans permiten que las aplicaciones J2EE reciban mensajes JMS de forma asíncrona. Así, el hilo de ejecución de un cliente no se bloquea cuando está esperando que se complete algún método de negocio de otro enterprise bean. Los mensajes pueden enviarse desde cualquier componente J2EE (una aplicación cliente, otro enterprise bean, o un componente Web) o por una aplicación o sistema JMS que no use la tecnología J2EE.
La diferencia más visible es que los clientes no acceden a los beans dirigidos por mensajes mediante interfaces (explicaremos esto con más detalle más adelante), sino que un bean dirigido por mensajes sólo tienen una clase bean.
En muchos aspectos, un bean dirigido por mensajes es parecido a un bean de sesión sin estado.
Las variables de instancia de estos beans pueden contener algún estado referido al manejo de los mensajes de los clientes. Por ejemplo, pueden contener una conexión JMS, una conexión de base de datos o una referencia a un objeto enterprise bean.
Cuando llega un mensaje, el contenedor llama al método onMessage
del bean. El método onMessage suele realizar un casting
del mensaje a uno de los cinco tipos de mensajes de JMS y manejarlo de forma
acorde con la lógica de negocio de la aplicación. El método
onMessage puede llamar a métodos auxiliares, o puede invocar a un bean
de sesión o de entidad para procesar la información del mensaje
o para almacenarlo en una base de datos.
Un mensaje puede enviarse a un bean dirigido por mensajes dentro de un contexto de transacción, por lo que todas las operaciones dentro del método onMessage son parten de un única transacción.
Hasta ahora hemos simplificado la descripción de la arquitectura EJB suponiendo que teníamos un único cliente y centrando la discusión en el acceso de los clientes a los beans.
Vamos a ampliar en este punto la explicación de la arquitectura EJB, centrándonos en la gestión de la concurrencia de acceso de los clientes y en el proceso de creación de los beans. Vamos a ver que la gestión de la concurrencia es distinta según tengamos un bean de sesión con estado, un bean de sesión sin estado, un bean de entidad o un bean dirigido por mensajes. Lo mismo sucede con el proceso de creación de los beans y su ciclo de vida en el contenedor EJB.
Todas estas consideraciones hacen que cada tipo de bean tenga unas características propias, en cuanto a eficiencia, escalabilidad, tiempo de respuesta, etc.. Es muy importante conocer estas características, ya que van a incidir directamente en el rendimiento de la aplicación que estés diseñando. Cuando veamos el ciclo de vida de cada uno de los tipos de beans haremos más consideraciones que también afectarán al rendimiento de cada tipo de bean.
Comencemos por recordar que los beans de sesión sin estado (por ejemplo,
el bean SaludoEJB) proporcionan al cliente un conjunto de métodos
remotos que representan tareas que el cliente puede solicitar. No existe ningún
estado que deba mantener el bean: el cliente realiza la petición al EJBObject,
el EJBObject pasa la petición al bean, el bean realiza la operación
y devuelve la respuesta al cliente.
La simplicidad del funcionamiento de este tipo de beans hace posible que sean muy escalables y que tengan un rendimiento muy bueno. El contenedor puede utlizar bastantes técnicas para optimizar su rendimiento, como son el mantener una reserva (pool) de beans, el reutilizar una instancia de un bean para distintos clientes o el usar un único EJBObject para varios clientes.
La siguiente figura muestra un ejemplo en el que más de un cliente está solicitando servicios de un bean de sesión sin estado.

¿Cuándo crea el contenedor nuevas instancias de beans? Cuando
lo considera necesario para mantener el rendimiento del servicio a los clientes.
Puede ser que en un momento dado haya una avalancha de peticiones concurrentes
y sea necesario aumentar el número de beans del pool de beans. Eso sí,
como ya ha quedado claro, el método create() no hace que
se cree un objeto nuevo. Todos sabemos que en Java las operaciones más
costosas tienen que ver con la creación y desaparición de objetos
y con la posible puesta en marcha del recolector de basura que ello conlleva.
Vamos a detallar el ciclo de vida del contenedor EJB durante el proceso de creación y uso de los beans de sesión sin estado.

create() de la interfaz home
para crear una instancia de un objeto EJBObject cuya referencia (su stub)
se pasa al cliente.setSessionContext().ejbCreate() en la instancia del bean.remove() en el objeto EJBObject cuando ha
terminado de usarlo, como una forma de comunicarle al contenedor EJB que ya
no va a necesitar más el bean.ejbRemove()
en la instancia del bean que va a eliminar.Los beans de sesión con estado mantienen un estado distinto para cada cliente. Por ello, ya no es posible usar alguna de las optimizaciones que hemos comentado para los beans de sesión sin estado. Siempre hay una relación uno a uno entre clientes y objetos EJBObjects e instancias del bean.

El proceso de creación de un bean de sesión con estado es idéntico al de un bean de sesión sin estado, con la salvedad de que, al mantenerse en el bean un estado propio de la conexión con el cliente, la instancia del bean asignada al cliente es la que debe ejecutar todas las llamadas. No es posible, como se hacía en el punto 4 del ciclo de vida anterior, invocar a distintas instancias del bean.

En los beans de entidad, cada instancia de bean representa un objeto de negocio concreto. Por ejemplo, el estudiante ID213 o el aula ID101. Cada instancia de bean de entidad representa una fila de una o varias tablas (en el caso en que las tablas de la base de datos estén normalizadas y que la información de un objeto esté repartida en más de tabla).
En este caso, es posible que más de un cliente esté accediendo al mismo bean, por lo que el contenedor debe tener esto en cuenta y gestionar la concurrencia. Más adelante veremos qué tipo de estrategias existen para gestionar estos accesos concurrentes de los clientes a los beans de entidad.

En cuanto al ciclo de vida del bean se puede resumir en los siguientes pasos:
setEntityContext().ejbLoad().ejbStore().Esta es una descripción muy simplificada en la que no se contemplan muchas de las técnicas de optimización y caché que se utilizan para mejorar el rendimiento reduciendo el trabajo en cada paso o eliminándolo completamente.

El proceso de despliegue de un bean es algo complejo y es muy común que se produzcan errores en su desarrollo. Para complicar más las cosas, es un proceso que no está estandarizado por J2EE y que depende de cada servidor de aplicaciones.
Vamos a intentar explicar los elementos fundamentales que sí contempla J2EE. Se trata de la especificación del contenido que de los distintos tipos de ficheros que intervienen en el despliegue. Son los siguientes:
Fichero EJB JAR (podría llamarse saludoEjb.jar).
/saludoEjb
/saludoEjb/moduloEjb
/saludoEjb/moduloEjb/SaludoBean.class
/saludoEjb/moduloEjb/Saludo.class
/saludoEjb/moduloEjb/SaludoHome.class
/saludoEjb/META-INF
/saludoEjb/META-INF/ejb-jar.xml
/saludoEjb/META-INF/weblogic-ejb-jar.xml
El directorio moduloEjb es necesario porque todas las clases SaludoBean,
Saludo y SaludoHome se incluyen el paquete Java moduloEjb
(para evitar solapamiento de nombres con otros posibles beans desarrollados
por terceros).
El fichero weblogic-ejb-jar.xml es el descriptor de despliegue
específico del servidor de aplicaciones WebLogic. Es muy importante saber
que en él se define el nombre JNDI del bean o los beans del fichero EJB
JAR.
Fichero EAR (podría llamarse saludoEar.jar):
/saludoEar
/saludoEar/saludoEjb.jar
/saludoEar/saludoWar.jar
/saludoEar/META-INF
/saludoEar/META-INF/application.xml
/saludoEar/META-INF/weblogic-application.xml
El fichero weblogic-application.xml es el descriptor de despliegue de la aplicación enterprise específico del servidor de aplicaciones WebLogic.
El contenido de los descriptores de despliegue se muestra a continuación:
ejb-jar.xml:
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD
Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<!-- Generated XML! -->
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>SaludoBean</ejb-name>
<home>moduloEjb.SaludoHome</home>
<remote>moduloEjb.Saludo</remote>
<ejb-class>moduloEjb.SaludoBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>SaludoBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
weblogic-ejb-jar.xml:
<!DOCTYPE weblogic-ejb-jar PUBLIC '-//BEA Systems, Inc.//DTD WebLogic 8.1.0
EJB//EN' 'http://www.bea.com/servers/wls810/dtd/weblogic-ejb-jar.dtd'>
<!-- Generated XML! -->
<weblogic-ejb-jar>
<weblogic-enterprise-bean>
<ejb-name>SaludoBean</ejb-name>
<stateless-session-descriptor>
<pool>
</pool>
<stateless-clustering>
</stateless-clustering>
</stateless-session-descriptor>
<transaction-descriptor>
</transaction-descriptor>
<jndi-name>SaludoBean</jndi-name>
</weblogic-enterprise-bean>
</weblogic-ejb-jar>