Documente Academic
Documente Profesional
Documente Cultură
GLOSARIO ....................................................................................155
BIBLIOGRAFÍA..............................................................................157
índice_ 1 Introducción
a J2EE
1
1.1. JAVA
1 Introducción
a J2EE
Hace ya varios años que el lenguaje de programación Java vio la luz. Desde entonces ha ido
ganando en popularidad a pasos agigantados. Lo que en principio empezó siendo un lenguaje
para la programación de electrodomésticos, es actualmente una de las mejores opciones
para el desarrollo de software.
La que inicialmente se conoció como la plataforma Java, evolucionó con el paso del tiempo a
la plataforma Java 2 (la versión 1.2) y se crearon tres ediciones distintas, cada una de ellas
especializada en un conjunto de necesidades específicas y con un kit de desarrollo de
software propio:
− La plataforma Java 2 Edición Móvil (J2ME o Java 2 Mobile Edition): Permite la creación
de aplicaciones para pequeños dispositivos y dispositivos inalámbricos, generalmente,
con recursos muy limitados.
3
1.2.1. El período de ejecución
1 Introducción
a J2EE
Desde el punto de vista del desarrollo, esto significa que los desarrolladores pueden
centrarse en la lógica de negocio y despreocuparse de las cuestiones del nivel de sistemas.
De este modo, la arquitectura J2EE nos proporciona un medio uniforme de acceder a los
servicios a escala de la plataforma a través de su entorno de período de ejecución. Entre
estos servicios podemos citar las transacciones distribuidas y la gestión de seguridad, entre
otros.
Ya sabemos que J2EE es un conjunto formado por varios API que nos permite realizar
aplicaciones distribuidas. En este apartado vamos a enumerar los distintos API de los que se
compone, junto con una breve descripción de cada uno de ellos.
4
Este API está formado por dos componentes:
1 Introducción
a J2EE
• Una interfaz a nivel de aplicación que usan los componentes para poder acceder
a la base de datos.
• Una interfaz del proveedor de servicios (base de datos) que hace de puente
entre el controlador JDBC y la plataforma J2EE.
− Java Message Service (JMS). Es un API para colas de mensaje. Publica y suscribe
tipos de servicios de software intermediarios controlados por mensajes.
− Java Naming and Directory Interface (JNDI). Este API pertenece realmente a la
edición estándar de Java (J2SE), no obstante, lo incluimos aquí por la gran relación
que tiene con el desarrollo de aplicaciones J2EE.
Por otra parte, provee métodos para la realización de las operaciones comunes de
directorio, como la asociación de atributos con objetos y la búsqueda de objetos a
partir de sus atributos.
El API JNDI provee el acceso de las aplicaciones al entorno de nombres de JNDI. Este
entorno permite personalizar los componentes sin tener que cambiar su código fuente.
5
1 Introducción
a J2EE
− Java Transaction API (JTA). Permite delimitar las operaciones transaccionales
dentro las aplicaciones distribuidas. Es un medio para trabajar con transacciones y con
transacciones distribuidas independientes de la implementación del gestor de
transacciones.
− Java API for XML Processing (JAXP). Este API soporta el procesamiento de
documentos XML usando DOM (Document Object Model), el API SAX (Simple API for
XML Parsing) y XSLT (XML Stylesheet Language Transformation).
Permite que las aplicaciones puedan analizar y transformar documentos XML con
independencia de cualquier implementación específica.
− Java API for XML Registries (JAXR). Permite el acceso sobre la web a los registros
de empresa y de propósito general.
6
1 Introducción
a J2EE
− Java API for XML-Based RPC (JAX-RPC). Permite que las aplicaciones puedan
realizar llamadas a procedimientos remotos (RPC) sobre Internet mediante el uso de
SOAP y HTTP.
También soporta WSDL (Web Service Description Language), por lo que se pueden
importar y exportar documentos WSDL.
Por último decir que, mediante JAX-RPC y WSDL se puede interactuar con otros
clientes y servicios.
− SOAP with Attachments API for Java (SAAJ). Es un API de bajo nivel del que
depende JAX-RPC. Hace posible la producción y consumo de mensajes que cumplan la
especificación SOAP.
1.3. CONTENEDORES
Podemos definir un contenedor J2EE como un período de ejecución para gestionar los
7
1 Introducción
a J2EE
componentes de la aplicación desarrollados según las especificaciones del API y destinado a
proporcionar acceso a los API de J2EE.
− Clientes EJB. Son aplicaciones que acceden a componentes EJB. Existen tres tipos de
clientes:
• Componentes EJB. Se trata de EJB que acceden a otros EJB y que pueden
utilizar RMI-IIOP o llamadas estándar de métodos estándar Java.
8
1.4. LA ARQUITECTURA MULTICAPA
1 Introducción
a J2EE
Se entiende por capa a un grupo de tecnologías que suministran uno o más servicios a sus
clientes.
La primera tecnología basada en capas que existió fue el modelo cliente/servidor y está
basada en dos capas. En dicho modelo, las aplicaciones cliente son aplicaciones de escritorio
y realizan peticiones a los servidores que ejecutan programas y que responden a las
peticiones de los clientes.
Por el contrario, los sistemas multicapa basados en web no necesitan que se actualice el
software de los clientes cuando se modifica la presentación o la funcionalidad de las
aplicaciones.
En el apartado anterior definimos una capa como un grupo de tecnologías que suministran
servicios a sus clientes. Para entender cómo se organiza una estructura de capas, podemos
ver su similitud con una empresa de tamaño grande:
En el nivel más bajo están los servicios de infraestructura que se componen de los recursos
necesarios para mantener las instalaciones. Entre estos recursos tenemos: la electricidad, los
ascensores, la telefonía y las redes informáticas.
La siguiente capa contiene los servicios de soporte para la actividad principal de la empresa.
Se compone de recursos como: la contabilidad, proveedores e informática.
Sobre esta última se encontraría la capa de producción, que permite producir los productos y
servicios que vende la empresa. Cuenta con recursos como: las compras, diseño de
productos y facturación.
Y por último, la capa más alta de la organización sería la de marketing, que permite
determinar qué productos y servicios vender a los clientes.
9
como ejemplo.
1 Introducción
a J2EE
Por otro lado, hemos de entender por cliente a cualquier recurso que envía una petición de
un servicio a un proveedor de servicios (o, como comúnmente se le hace referencia,
servicio).
Un servicio se define como cualquier recurso que recibe y atiende una petición de un
cliente, y puede ser, a su vez, cliente de otro servicio.
En una arquitectura multicapa, cada capa contiene servicios que incluyen objetos de
software, sistemas de gestión de base de datos o conexiones con sistemas heredados.
Los servicios se enlazan mediante un protocolo de comunicaciones que les permiten recibir y
enviar información de y a otros servicios.
De este modo, el cliente envía una petición de servicio, recibe los resultados y no debe
preocuparse por la forma en que el servicio le debe proporcionar dichos resultados. Esto
significa que podemos desarrollar rápidamente mediante la creación de un programa cliente
que envía peticiones a los servicios que ya existen en la arquitectura multicapa. Estos
servicios ya contienen la funcionalidad necesaria para atender la petición de nuestro
10
programa cliente.
1 Introducción
a J2EE
La arquitectura J2EE está basada en cuatro capas, que son las siguientes:
− Capa web.
Cada capa está orientada para proporcionar a una aplicación un tipo específico de
funcionalidad.
Es preciso diferenciar entre ubicación física y funcionalidad. A pesar de que varias capas
puedan residir físicamente dentro de una misma máquina virtual (JVM), cada una
proporciona a las aplicaciones J2EE distinto tipo de funcionalidad. Las aplicaciones J2EE
tienen acceso únicamente a aquellas capas de las que requiere su funcionalidad.
11
1 Introducción
a J2EE
En la siguiente figura podemos ver la estructura de capas de la arquitectura J2EE.
La mayoría de los API de J2EE tampoco están asociados con ninguna capa en concreto, es
decir, se pueden utilizar en más de una capa. Por ejemplo, el API JDBC se puede utilizar en
las capas web y EJB, mientras que el API EJB sólo es aplicable a la capa EJB.
La capa cliente está compuesta por los programas que interactúan con el usuario de la
aplicación. Estos programas le piden datos al usuario y convierten su respuesta en peticiones
que se reenvían a un componente que procesa la petición y devuelve el resultado al
programa cliente. El componente puede funcionar en cualquier capa, aunque normalmente
las peticiones de clientes suelen ir dirigidas a componentes de la capa web. El programa
cliente también traduce la respuesta del servidor en texto y pantallas que se muestran al
usuario.
Por otro lado, la capa web proporciona funcionalidad web a la aplicación J2EE. Los
componentes de esta capa utilizan el protocolo HTTP para recibir peticiones de los clientes y
enviar respuestas a los mismos (que pueden estar en cualquier capa). Recordemos que un
cliente es cualquier componente que inicia una petición.
12
1 Introducción
a J2EE
Los Enterprise JavaBeans se encuentran dentro del contenedor de EJB, que es un servidor
de objetos distribuidos que trabaja en la capa EJB. El contenedor gestiona las transacciones
y la seguridad, y se asegura que los hilos y la persistencia se implementen de forma correcta
cada vez que se invoca a un Enterprise JavaBeans.
Aunque un EJB puede tener acceso a componentes de cualquier capa, generalmente llama a
componentes y recursos como el DBMS (Database Management System) en la capa de
sistemas de información empresarial (EIS).
Por último, la capa EIS es la que conecta a la aplicación con recursos y sistemas heredados
que están en la red corporativa. En esta capa, la aplicación J2EE se conecta con tecnologías
como DBMS y mainframes que forman parte de los sistemas críticos de la empresa. Los
componentes de esta capa se conectan a los recursos utilizando CORBA o conectores Java
(JCA, Java Connector Architecture).
Normalmente, dichas aplicaciones acceden a los mismos componentes de la capa EJB que
acceden los componentes de la capa web. Ya sabemos que la capa EJB es la que implementa
la lógica de negocio y, por tanto, debe ser la capa que canalice las peticiones y les dé un
trato homogéneo, con independencia del tipo de cliente que esté accediendo.
En el siguiente gráfico se muestra cómo fluyen las peticiones y respuestas entre las distintas
capas. Podemos ver que la aplicación cliente salta la capa web y usa directamente los
servicios de la capa EJB. En dicha figura vemos también que la aplicación cliente y la capa
web acceden a EJB distintos, sin embargo, no tiene que ser necesariamente así. Por ejemplo,
si tenemos un componente EJB que determina las condiciones de venta de un determinado
producto, lo razonable sería que cualquier cliente que necesite conocer dichas condiciones
utilice el servicio que ofrece dicho componente EJB.
13
1 Introducción
a J2EE
14
1 Introducción
a J2EE
− J2EE es una tecnología que se centra fundamentalmente en el desarrollo de
recuerde_
componentes para entornos distribuidos. Es un conjunto de especificaciones,
e indica tanto la estructura para gestionar las aplicaciones de empresa, como
los servicios API para construir dichas aplicaciones.
− J2EE permite definir una arquitectura multicapa en la que cada una de las
capas está orientada a proporcionar a una aplicación un tipo específico de
funcionalidad.
15
índice_ 2 Aplicaciones
web
17
2 Aplicaciones
web
Una aplicación web es un conjunto de páginas JSP, servlets, clases ayudantes, librerías de
clases y recursos estáticos. Entre los recursos estáticos más comunes podemos citar los
documentos HTML y XML y los ficheros de imágenes.
En toda aplicación web podemos distinguir cuatro partes principales, que son:
En el tema anterior vimos que existen principalmente dos tipos de clientes: los clientes web
y los clientes de aplicación. Los clientes de aplicación tienen su origen en la arquitectura
cliente-servidor. En ella, los clientes dirigen la interacción del usuario y la mayor parte de la
lógica de aplicación. A los clientes de aplicación se les conoce también como clientes
robustos y procesan la lógica de la aplicación de forma local.
En una arquitectura multicapa, los clientes de aplicación pueden delegar parte de la lógica de
aplicación y acceso a bases de datos en los componentes de la capa media (como los
Enterprise JavaBeans). A pesar de esta distribución de la lógica de la aplicación en los
componentes de la capa media, los clientes de aplicación requieren su instalación para cada
usuario.
Con la llegada de Internet, los clientes web sustituyeron a muchos clientes de aplicación
autónomos. El principal motivo de este cambio radica en la propia naturaleza de los clientes
web. En las arquitecturas basadas en ellos, la capa de interacción con el usuario está
separada de la capa de cliente tradicional. Los navegadores web gestionan la interacción de
usuario pero dejan el resto a las aplicaciones del lado servidor, incluidos la lógica para
19
2 Aplicaciones
web
Para un usuario final, el navegador es el cliente para todas las aplicaciones basadas en web.
Puesto que este tipo de clientes no impone ningún requisito especial en la instalación, los
clientes web también son conocidos como clientes ligeros.
− El uso de HTML, DHTML y XML para crear la interfaz de usuario. Normalmente se suele
emplear también Javascript para el control dentro de dicha interfaz.
− Los protocolos HTTP y/o HTTPS para el intercambio de información entre el cliente y el
servidor.
La arquitectura J2EE nos ofrece un modelo de programación flexible y rico en funciones para
construir aplicaciones web dinámicas. Dicha arquitectura nos proporciona contenedores web,
el API Java Servlet y el API JavaServer Pages para la construcción y gestión de las
aplicaciones web de forma que el contenedor web ofrece el entorno de período de ejecución
y un marco para proporcionar apoyo a las aplicaciones web y los API Java Servlet y
JavaServer Pages componen la base para el desarrollo de las aplicaciones web.
Hemos de tener presente que HTTP es un protocolo de aplicación que suele implementarse
sobre conexiones TCP/IP y que es un protocolo sin estado basado en solicitudes y
respuestas.
Los clientes envían solicitudes al servidor web para recibir información o para iniciar un
proceso específico en el servidor.
20
2 Aplicaciones
web
El protocolo HTTP define los tipos de solicitudes que los clientes pueden enviar a los
servidores y los tipos de respuestas que los servidores pueden enviar a los clientes. También
especifica cómo están estructuradas estas solicitudes y respuestas.
Los tipos de métodos de solicitud especificados por el protocolo HTTP han cambiado con la
aparición de nuevas versiones. La versión 1.0 especifica tres tipos de métodos de solicitud:
GET, POST y HEAD. La versión 1.1 añade cinco métodos nuevos: OPTIONS, PUT, TRACE,
DELETE y CONNECT. De todos ellos, los métodos GET y POST cumplen la mayoría de los
requisitos más comunes de desarrollo de aplicaciones.
Es el método de solicitud más sencillo y usado con más frecuencia. Se utiliza normalmente
para acceder a recursos estáticos como documentos HTML.
Por otra parte, las solicitudes GET pueden utilizarse para recuperar información dinámica,
incluyendo parámetros de consulta en el URL de la solicitud. Por último, el servidor web
puede utilizar el valor de este parámetro para enviar contenido específico a un cliente.
Se suele emplear para acceder a recursos dinámicos. Las solicitudes POST se utilizan
habitualmente por dos motivos: para transmitir información que depende de la solicitud o
cuando una gran cantidad de información compleja debe ser enviada al servidor.
Por otra parte, existen ciertas diferencias entre las solicitudes GET y POST. Con las
solicitudes GET, los parámetros de solicitud son transmitidos como una cadena de consulta
adjunta en el URL de la solicitud. Por el contrario, en el caso de las solicitudes POST los
parámetros son transmitidos en el cuerpo de la solicitud. Esto tiene dos consecuencias
directas: en primer lugar, dado que la solicitud GET contiene la información completa de
solicitud adjunta en el mismo URL, permite a los navegadores guardar la dirección de la
página y volver a visitarla más tarde. Dependiendo del tipo y de la sensibilidad de los
21
2 Aplicaciones
web
parámetros de la solicitud, esto puede interesarnos o no. En segundo lugar, los servidores
suelen imponer restricciones en cuanto a la longitud del URL. Esto limita la cantidad de
información que podamos adjuntar al URL de solicitud.
Un cliente enviará una solicitud de tipo HEAD cuando sólo desee ver las cabeceras de una
respuesta, como “Contenido-Tipo” o “Contenido-Longitud”.
Junto con el tipo de solicitud, la aplicación de cliente también especifica el recurso que
necesita como parte de la cabecera de la solicitud. Vamos a ver un ejemplo: cuando
introducimos “http://java.sun.com/index.jsp” en la barra de dirección de nuestro navegador,
éste envía la solicitud GET al servidor web identificado por “java.sun.com”, para el recurso
“index.jsp”. Para el protocolo HTTP, un identificador de recursos uniformes (URI, Uniform
Resource Identifier) especifica un recurso. El URI es el URL pero excluyendo el nombre del
dominio. En nuestro caso, el recurso es el archivo “index.jsp” localizado en el documento raíz
del servidor web que sirve al dominio “java.sun.com”.
Cuando un servidor recibe una solicitud HTTP responde con el estado de la respuesta y con
información adicional que describe la respuesta. Todos estos elementos son parte de la
cabecera de respuesta. Además, exceptuando el caso de las solicitudes HEAD, el servidor
también enviará el contenido correspondiente al recurso que se ha especificado en la
solicitud. Es decir, si en un navegador enviamos una solicitud a
“http://java.sun.com/index.html” recibiremos el contenido del archivo “index.html” como
parte del mensaje.
Los campos de cabecera del contenido de la respuesta contienen información útil que los
clientes pueden querer comprobar en determinadas circunstancias. Entre los campos
habituales incluidos en la cabecera están: la fecha, el tipo de contenido y la fecha de
expiración. Como ejemplo, podemos citar que el campo de fecha de expiración de una página
podemos fijarlo con la misma fecha que el campo de fecha para indicar a los navegadores
que no deben guardar la página en la caché. Las aplicaciones Web que suministren
información dependiente del tiempo pueden necesitar establecer estos campos.
Los servidores y clientes que se comunican utilizando el protocolo HTTP hacen uso de “Multi-
Purpose Internet Mail Extensions” (MIME) para indicar el tipo de contenido de los
cuerpos de solicitud y respuesta. Los tipos MIME más comunes son: “text/html” y
22
2 Aplicaciones
web
Los servidores HTTP utilizan cabeceras MIME al principio de cada transmisión y los
navegadores utilizan esta información para decidir cómo analizar y generar el contenido. Los
navegadores también utilizan cabeceras MIME al transmitir datos en el cuerpo de las
solicitudes para describir el tipo de datos que envían.
− Requiere que el cliente establezca conexiones previas a cada solicitud y que el servidor
cierre la conexión después de enviar la respuesta. Esto garantiza que un cliente no
pueda mantener una conexión después de recibir la respuesta. Cualquiera de los dos
puede finalizar prematuramente una conexión.
Las aplicaciones web son aplicaciones de lado servidor. Los requisitos fundamentales para
el desarrollo de este tipo de aplicaciones son los siguientes:
− Soporte de período de ejecución en el servidor que incluya el apoyo para los servicios
de red necesarios y la ejecución de las tareas.
23
2 Aplicaciones
web
Para que estos requisitos se cumplan, la especificación J2EE suministra los siguientes
elementos:
− Servlets y páginas JSP. Estos elementos son los bloques básicos de construcción
para el desarrollo de aplicaciones web. Suelen recibir el nombre de componentes
web.
24
2 Aplicaciones
web
2.4. SERVLETS
Podemos definir un servlet de Java como un programa pequeño que se ejecuta en el lado del
servidor, que es independiente de la plataforma y que amplía la funcionalidad del servidor
web. El API Java Servlet proporciona un marco sencillo para construir aplicaciones en estos
servidores.
El protocolo HTTP no define un medio estándar para integrar la lógica de aplicación durante
la fase de generación de respuestas. Por lo tanto, no existe un modelo de programación
específico para estas tareas. HTTP sólo define cómo pueden los clientes solicitar información
y cómo pueden responder los servidores. Pero no especifica nada en cuanto a cómo puede o
debe ser generada la respuesta.
Los servlets Java no son aplicaciones que puedan invocar los usuarios. Es el contenedor
web en el que está desplegada la aplicación que contiene los servlets, el que invoca a dichos
servlets basándose en solicitudes HTTP entrantes. Cuando un servlet es invocado, el
contenedor web le envía la información de la solicitud entrante de modo que éste pueda
procesarla y generar una respuesta dinámica. El contenedor web actúa sólo como interfaz
con el servidor web, aceptando solicitudes para los servlets y transmitiendo las respuestas
de vuelta al servidor web.
25
2 Aplicaciones
web
Para que podamos entender cómo un servlet interactúa con un servidor web a través de un
contenedor web, vamos a tener en cuenta el proceso de invocación con el que el servidor
web recibe una solicitud HTTP. Antes mencionamos que el protocolo HTTP se basa en un
modelo de solicitudes y respuestas. Un cliente conecta con un servidor web y envía una
solicitud HTTP en la conexión. Basado en un URL de solicitud, la secuencia de eventos que
vemos a continuación tiene lugar en una secuencia típica. En la siguiente figura, las flechas
que apuntan hacia la derecha representan las solicitudes, mientras que las que apuntan
hacia la izquierda indican las respuestas.
− El servidor web tiene que descifrar si la solicitud entrante corresponde a una aplicación
web del contenedor, lo que implica que debe haber un entendimiento entre el servidor
y el contenedor. Los contenedores web utilizan el concepto de contexto de servlet para
identificar las aplicaciones web. Este contexto debe ser especificado cuando la
aplicación se despliega en el contenedor.
26
2 Aplicaciones
web
basada en patrones URL. Todos los recursos indicados forman parte de la aplicación
web. Cuando empaquetamos y desplegamos una aplicación, especificamos esta
información de representación. El contenedor web la utiliza para representar cada
solicitud entrante en un servlet, página JSP o recurso estático. Si el recurso está
representado en un recurso estático, el contenedor se limita a pasar el recurso al
servidor. Esto conforma el cuerpo de la respuesta que el servidor web envía al
navegador.
Para construir y desplegar una aplicación basada en un servlet se requieren los siguientes
dos pasos:
27
2 Aplicaciones
web
− Etiquetas y scriptlets escritos en Java que se utilizan para envolver la lógica que
genera el contenido dinámico.
Dado que una página JSP proporciona una representación general del contenido y puede
producir múltiples vistas en función del resultado de las etiquetas y scriptlets, las páginas
JSP actúan como una plantilla para generar un contenido.
Una plantilla es una página de anotación con marcas especiales (etiquetas y scriptlets)
incrustados. Estas marcas contienen información para el procesador de la plantilla (el que
genera el contenido). En una página JSP, la anotación nos permite definir su estructura
estática y su contenido, y las marcas especiales nos posibilitan incluir lógica de programación
para que se ejecute durante la generación de las páginas.
La gran ventaja de esta tecnología es que ayuda a mantener separados el diseño del
contenido y la lógica de la aplicación. Si utilizamos servlets únicamente ambos aspectos
estarán estrechamente unidos, lo que conlleva que las aplicaciones sean más difíciles de
mantener.
En algunas otras tecnologías controladas por plantillas, éstas son evaluadas en el periodo de
ejecución. Es decir, cada vez que se solicita una página el procesador de plantilla tiene que
interpretar la plantilla.
Algunos contenedores permiten precompilar las páginas en servlets, evitando la demora que
se produce cuando se invoca por primera vez. La mayoría de los contenedores repiten el
proceso de compilación cada vez que se modifica la página.
En la siguiente figura podemos ver las diferentes fases de este proceso. La flecha punteada
representa la compilación de la página.
28
2 Aplicaciones
web
− Definir los servlets y las páginas JSP. Cada servlet y página JSP precompilada que
utilicemos en nuestra aplicación debe estar definida en el descriptor de despliegue. La
definición incluye el nombre del elemento, su clase y una descripción.
− La representación de los servlets y las páginas JSP. Los contenedores web utilizan esta
información para representar solicitudes entrantes a los servlets y las páginas JSP.
− Los tipos MIME. Debido a que cada aplicación web puede contener varios tipos de
contenidos, podemos especificar los tipos MIME para cada tipo.
29
2 Aplicaciones
web
En el descriptor de despliegue también se indican otras características, como cuáles son las
páginas de bienvenida, las páginas de error y la configuración de la sesión.
30
recuerde_ 2 Aplicaciones
web
31
índice_ 3 Enterprises Beans y
servicios de contenedor
33
3 Enterprises Beans y
servicios de contenedor
Antes de explicar qué es un EJB, un Enterprise JavaBean o un Enterprise Bean (se les conoce
por cualquiera de los tres nombres), vamos a aclarar un aspecto que ha sido fuente de
confusión para muchos desarrolladores. Se trata de su nombre.
A pesar de la similitud entre los términos JavaBean y Enterprise JavaBean, no tienen nada en
común. Sus objetivos, implementación y usos son muy distintos.
Los componentes EJB tienen la finalidad de contener la lógica de la empresa (las reglas de
negocio). Encapsulan la funcionalidad crítica y permiten que el desarrollador de aplicaciones
se despreocupe de los servicios del nivel de sistemas como la concurrencia, la persistencia y
las transacciones.
Dado que es tecnología Java, son independientes del sistema operativo y la plataforma
(siempre que no se utilice código nativo). Por ello, se pueden desarrollar en cualquier
plataforma para ser usados posteriormente en cualquier otra plataforma. Actualmente sólo
existe otra tecnología similar, se trata de la tecnología DCOM de Microsoft, pero tiene el
inconveniente de que sólo se pueden utilizar en plataformas Windows.
Como hemos mencionado con anterioridad, J2EE es una especificación. Por lo tanto, la
tecnología EJB también es una especificación, y concretamente define cómo debe ser la
arquitectura de componentes del lado servidor. Dicha especificación es la que deben seguir
los proveedores de contenedores EJB para ser capaces de alojar componentes EJB. Pero
además, dichos proveedores tienen la libertad de darle algún valor añadido a sus
contenedores añadiéndole extensiones propias.
Siempre que un EJB no haga uso de dichas extensiones, puede ser trasladado de un
contenedor a otro en base a las necesidades. Por ejemplo, los EJB pueden desarrollarse
utilizando un contenedor de bajo coste o gratis y posteriormente ponerlos en explotación en
contenedores comerciales de alto rendimiento.
35
3 Enterprises Beans y
servicios de contenedor
Un EJB puede utilizarse con muchos tipos de clientes. Algunos ejemplos comunes de
llamadas de clientes son los siguientes:
− Desde aplicaciones autónomas Java mediante la utilización del API RMI (Invocación de
métodos remotos).
− HTML básico: Sólo contenido estático, puesto que no existe la necesidad de contenido
dinámico.
36
3 Enterprises Beans y
servicios de contenedor
− Un bean de entidad representa información de una base de datos. Por ese mismo
motivo, a un bean de entidad pueden acceder múltiples clientes simultáneamente.
Existen dos tipos: persistencia gestionada por el contenedor y persistencia
gestionada por el bean. La diferencia entre ellos radica en que sea el contenedor
EJB el que interactúe con la base de datos o sea el programador quien tenga que
escribir el código para interactuar con la base de datos.
Los contenedores EJB suelen estar contenidos en servidores de aplicaciones que les
suministran un entorno de ejecución, y que normalmente incluyen también otro tipo de
contenedores, como pueden ser los contenedores web.
37
3 Enterprises Beans y
servicios de contenedor
Los componentes EJB aprovechan los servicios que proporciona el contenedor EJB, por lo que
es importante que conozcamos dichos servicios para así poder sacarles el máximo partido.
− Seguridad declarativa: El acceso a los componentes EJB también puede ser regulado
sin la necesidad de escribir código.
− Portabilidad: Dado que los EJB se escriben siguiendo un API estándar, pueden ser
ejecutados en distintos servidores J2EE sin necesidad de modificar el código.
38
3 Enterprises Beans y
servicios de contenedor
Se define como contrato a las responsabilidades definidas entre las partes de una aplicación
que utiliza componentes EJB: el cliente, el contenedor y el componente. Si cada parte
implicada respeta las reglas de su contrato, podrá interactuar con las demás sin necesidad
de conocerlas.
Por otro lado, el contenedor proporciona los servicios del nivel de sistemas mediante la
interposición. Para ello se interpone entre la interfaz de empresa de cliente y la lógica de
empresa EJB.
− El cliente realiza la llamada mediante un stub RMI, que es la interfaz básica del bean.
Para hacernos una idea clara de la diferencia entre las interfaces básica y remota, la interfaz
básica es el punto de entrada a los EJB y es la única entidad accesible desde el exterior.
Una vez obtenida una instancia de la interfaz básica se le pide que cree o localice un EJB y, a
continuación, se puede acceder a dicho EJB a través de la interfaz remota.
39
3 Enterprises Beans y
servicios de contenedor
Un módulo J2EE se compone de uno o más componentes J2EE (para el mismo tipo de
contenedor) y un descriptor de despliegue, siendo éste último un fichero XML que
describe la configuración de despliegue del componente J2EE.
− Descriptores de módulos EJB. Existe uno por cada módulo EJB y describe la
totalidad de EJB contenidos en él. Su nombre es “ejb-jar.xml” y se almacena dentro
del directorio “META-INF”.
40
3 Enterprises Beans y
servicios de contenedor
− Descriptores de módulos web. Existe uno por cada módulo web. Su nombre es
“web.xml” y se almacena dentro del directorio “WEB-INF”. Los vimos en el tema
“Aplicaciones web”.
41
3 Enterprises Beans y
servicios de contenedor
Este manual pretende ser una introducción al desarrollo de EJB. El servidor de aplicaciones
que hemos elegido para los ejemplos nos va a permitir abstraernos de ciertas complejidades
como, por ejemplo, los descriptores de despliegue. Por lo tanto, dado que la sintaxis y
explicación de los descriptores es sumamente extensa, no vamos a detallarla aquí. La mejor
información para estudiarla detalladamente podemos encontrarla en la documentación de la
especificación J2EE.
Para utilizar un enterprise bean necesitamos saber cómo encontrarlo o crearlo, cómo utilizar
sus métodos y como liberar sus recursos. No tenemos que preocuparnos sobre su
implementación ni los servicios provistos por el contenedor.
− Una interfaz básica y/o una interfaz básica local. Se utilizan para operaciones de ciclo
de vida (crear, encontrar y eliminar EJB). No están asociadas a una instancia de bean
concreta, sólo a un tipo de bean.
42
3 Enterprises Beans y
servicios de contenedor
− Una interfaz remota y/o local. Permiten el acceso a los métodos de empresa. Están
asociadas a una instancia de bean concreta.
En lo que respecta a las interfaces de los EJB de sesión y de entidad, pueden tener las
interfaces básica y remota, pueden tener las interfaces básica local y local, o pueden tenerlas
todas. En el siguiente apartado veremos los usos y diferencias entre ellas.
Los pasos para hacer uso de un componente EJB son los siguientes:
− Crear o encontrar una instancia de un bean que se obtendrá como interfaz remota (o
local).
− Eliminar el bean.
try {
// Obtenemos una referencia al contexto de ejecución
Context ctx = new InitialContext();
// Obtenemos una referencia al componente mediante JNDI
Object obj = ctx.lookup("java:comp/env/ejb/Ventas");
// Obtenemos una referencia a la interfaz básica
VentasHome home = (VentasHome) javax.rmi.PortableRemoteObject.narrow(obj,
VentasHome.class);
// Obtenemos una instancia al bean mediante la interfaz remota
Ventas ventas = home.create();
// Invocamos los métodos de negocio
ventas.calcularDescuentos();
ventas.actualizarDescuentos();
// Eliminamos el EJB
ventas.remove();
} catch (Exception e) {
errText = e.getMessage();
}
43
3 Enterprises Beans y
servicios de contenedor
Una de dichas parejas está formada por las interfaces básica y remota y permiten que los
clientes remotos (aquellos que no están en la misma unidad de despliegue del bean) puedan
hacer uso del enterprise bean.
La otra pareja está formada por las interfaces básica local y local. Permiten que clientes que
están en la misma unidad de despliegue puedan hacer uso del EJB, sin necesidad de utilizar
las interfaces remotas.
El problema del acceso remoto a un EJB reside en que es más costoso (a nivel de recursos) y
lento debido a que hace uso de RMI (y todo lo que conlleva). La interfaz local no hace uso de
RMI, no obstante, tiene el inconveniente de que sólo pueden usarla los clientes que formen
parte de la misma unidad de despliegue en que esté el EJB.
En definitiva, un enterprise bean puede incluir las interfaces para acceso local, remoto o
ambas.
Para las siguientes explicaciones vamos a suponer un bean llamado “VentasBean”. En base a
dicho nombre indicaremos la nomenclatura que se suele emplear para nombrar las clases e
interfaces relacionadas con dicho bean.
Tanto la interfaz remota como la local deben declarar los métodos de negocio que
implementa el bean y pueden utilizar los clientes.
44
3 Enterprises Beans y
servicios de contenedor
Tanto la interfaz básica como la interfaz básica local declararán uno o más métodos que
dependerán del tipo de bean:
− Un bean de entidad añadirá ninguno, uno o varios métodos “create()” y uno o más
métodos “finder()”.
La clase del bean debe implementar los métodos de retrollamada definidos en su respectiva
interfaz, aunque a veces se dejen vacíos. También debe añadir los métodos de empresa
declarados en las interfaces remota y básica.
En los temas correspondientes a los distintos tipos de EJB entraremos en más detalles.
45
3 Enterprises Beans y
servicios de contenedor
Ahora dejemos la teoría y vamos con un ejemplo que nos ayude a asimilar los conocimientos
que hemos tratado.
Para los ejemplos que vamos a estudiar desde este tema en adelante, usaremos los
siguientes productos:
− Firebird versión 1.5. Es una base de datos gratuita (licencia GPL). Tiene su origen en la
liberación del código de la base de datos Interbase de Borland, de ahí que ofrezca un
rendimiento excelente. Podemos descargarla de http://www.firebirdsql.org
− Firebird SQL versión 1.5.0. Contiene los controladores JDBC que vamos a usar con la
base de datos Firebird. También podemos descargarlo desde la dirección anterior.
− NetBeans 4.1. Entorno de desarrollo integrado (IDE) para Java. Es gratuito y podemos
descargarlo de http://www.netbeans.org.
La elección del software anterior se ha realizado, en primer lugar, buscando el menor coste
posible. Es por eso que todos los productos mencionados son gratuitos y que la mayoría
permite su uso bajo licencia GPL. El segundo factor a evaluar ha sido la calidad y simplicidad.
Si estamos interesados en utilizar otro tipo de software, habremos de tener en cuenta sus
diferencias con respecto a los que aquí mencionamos para hacer los cambios pertinentes de
cara a que los ejemplos funcionen. Para los ejemplos crearemos un directorio llamado
“cursoEJB”. Para los usuarios de Linux se asumirá el trayecto “/home/alumno/cursoEJB”,
mientras que para los usuarios de Windows se asumirá el trayecto “c:\cursoEJB”. Dentro de
dicho directorio vamos a crear la base de datos Firebird que utilizaremos en los ejemplos que
lo necesiten.
46
3 Enterprises Beans y
servicios de contenedor
La instalación del servidor de bases de datos Firebird tendremos que realizarla como
administrador del sistema. El proceso de instalación es muy sencillo y no vamos a entrar en
los detalles.
En lo que respecta a Firebird SQL, se trata de un fichero comprimido que contiene los
controladores JDBC para conectar con bases de datos Firebird. De él nos interesa el fichero
“firebirdsql-full.jar”.
47
3 Enterprises Beans y
servicios de contenedor
En el menú “File” seleccionaremos la opción “New database...” para crear una base de datos
nueva. A continuación, se nos muestra una nueva ventana con el título “Create a new
Interbase database” en la que debemos elegir la base de datos a crear. No obstante, lo
primero que hemos de hacer es crear una conexión y para ello, pulsaremos el botón
“Configure databases”.
Nos volverá a aparecer una nueva ventana con el título “Global Database Definition” y
pulsaremos el botón “New”. Nos preguntará por el alias que queremos asignarle a la base de
datos. En nuestro ejemplo usaremos el texto “cursoEJB” y pulsaremos el botón “OK”.
A continuación, podremos ver que en la ventana se nos solicitan varios datos, de entre ellos
debemos considerar los siguientes:
− “User name”. Nombre del usuario. No vamos a crear usuarios, si no que nos
limitaremos a utilizar el nombre del administrador de la base de datos. Tendremos que
indicar el texto “sysdba”.
− “Filename”. Nombre (y trayecto) del fichero que da soporte a la base de datos. Cada
base de datos Firebird se almacena en uno o más ficheros físicos. En nuestro caso
vamos a crear una base de datos de nombre “cursoEJB.gdb”. Los usuarios de Linux
escribirán “/home/alumno/cursoEJB/cursoEJB.gdb”, mientras que los usuarios de
Windows indicarán “c:\cursoEJB\cursoEJB.gdb”.
48
3 Enterprises Beans y
servicios de contenedor
Tras lo que hemos visto, la ventana tendrá el siguiente aspecto (recordemos que los usuarios
de Windows verán un valor distinto en “Filename”):
Seguidamente pulsaremos los botones “Save” y “OK”, en ese orden. Se cerrará la ventana y
nos pedirá confirmación para guardar los cambios. Responderemos afirmativamente.
En adelante, cada vez que entremos en la utilidad IBAccess seleccionaremos la opción “Open
database...” del menú “File” y en el combo “Databases” elegiremos el alias “CURSOEJB”. Una
vez hecho esto, aparecerá una ventana que será donde manipularemos la base de datos
(crear tablas, introducir datos, etc.) de cara a los ejemplos que se van a tratar más adelante.
La instalación la haremos siguiendo los pasos que nos va sugiriendo el asistente. Tendremos
que prestar especial atención cuando se nos pregunte por el directorio del kit de desarrollo
de Java 2 Edición Estándar (JDK).
49
3 Enterprises Beans y
servicios de contenedor
No obstante, la ventana más importante de todas las que nos va a mostrar el proceso de
instalación es la siguiente:
50
3 Enterprises Beans y
servicios de contenedor
A continuación nos mostrará un mensaje que nos indicará que el servicio está iniciado.
Lo siguiente que veremos será una página de registro en la que se nos solicita el nombre y
clave de la cuenta de administración. Indicaremos los mismos que pusimos durante la
instalación y pasaremos a la página principal de administración que tiene el siguiente
aspecto:
51
3 Enterprises Beans y
servicios de contenedor
52
3 Enterprises Beans y
servicios de contenedor
Para cerrar la página de bienvenida utilizaremos el botón izquierdo del ratón sobre el icono
cerrar (“x”) de la pestaña “Welcome”.
3.7.2. El desarrollo
Queremos construir una aplicación que reciba peticiones de clientes web, invoque a un
componente de empresa para obtener los resultados y muestre dicho resultado al cliente que
lo solicitó.
− Un módulo web que estará compuesto por una única página que solicitará los datos y
mostrará los resultados.
53
3 Enterprises Beans y
servicios de contenedor
54
3 Enterprises Beans y
servicios de contenedor
Para poder desarrollar aplicaciones J2EE (concretamente para poder compilar) desde dentro
de NetBeans, necesitaremos indicarle el fichero de clases “j2ee.jar”. Puesto que vamos a
utilizar el servidor de aplicaciones de Sun Microsystems, lo razonable es indicar el que viene
incluido en el servidor dentro del directorio “lib”. Para ello, pulsaremos el botón derecho
sobre el nodo “Libraries”, pincharemos sobre la opción “Add JAR/Fólder” y seleccionaremos el
fichero de clases “j2ee.jar”.
Lo próximo que haremos será crear las tres clases que necesitará nuestro EJB. Para ello,
sobre el nodo “gestion” pulsamos el botón derecho del ratón y seleccionamos la opción
“New” y subopción “Java Class”. Esta operación la realizaremos tres veces y los nombres de
clases que emplearemos serán los siguientes: “VentasHome”, “Ventas” y “VentasBean”.
55
3 Enterprises Beans y
servicios de contenedor
Habremos podido comprobar que NetBeans crea cada clase incluyendo en ella el nombre del
paquete al que pertenece y añadiéndole un constructor vacío. En breve modificaremos el
contenido de cada clase para que se ajuste a lo que queremos de ella. Concretamente, dos
de esas clases deberían ser interfaces, por lo que las cambiaremos. Pero veamos primero
cual es la finalidad de cada una de ellas:
56
3 Enterprises Beans y
servicios de contenedor
package gestion;
import javax.ejb.*;
public interface VentasHome extends javax.ejb.EJBHome {
public gestion.Ventas create()
throws javax.ejb.CreateException, java.rmi.RemoteException;
}
package gestion;
import javax.ejb.*;
public interface Ventas extends javax.ejb.EJBObject {
// Calcula el importe de la venta añadiendo el % de comisión
public float calculaImporte(int cantidad, float precio) throws
java.rmi.RemoteException;
}
Y por último a la clase “VentasBean”, que es la que contiene el código que soporta las reglas
de negocio, le escribimos el siguiente código:
package gestion;
import javax.ejb.*;
public class VentasBean implements javax.ejb.SessionBean {
private javax.ejb.SessionContext context;
private static final int comision = 5;
public void setSessionContext(javax.ejb.SessionContext aContext) {
context=aContext;
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void ejbRemove() {
}
public void ejbCreate() {
57
3 Enterprises Beans y
servicios de contenedor
}
// Calcula el importe de la venta añadiendo el % de comisión
public float calculaImporte(int cantidad, float precio) {
float importe = cantidad * precio;
return importe + importe * this.comision / 100;
}
}
Si hemos escrito correctamente todo el código anterior, no debería haber problemas con la
compilación, no obstante, lo comprobaremos compilando las clases.
Un método rápido que podemos utilizar para compilar la clase y las interfases es usar el
botón derecho sobre el nombre del paquete (“gestion”) y elegir del menú emergente la
opción “Compile Package”. En caso de que se muestren errores, revisaremos las líneas en
cuestión y los solventaremos.
En este momento tenemos compilados los ficheros Java que componen el EJB. Según la
especificación EJB nos faltaría el descriptor de despliegue, el fichero “ejb-jar.xml”. También
nos faltaría el fichero de despliegue específico de la plataforma en la que vamos a desplegar
el EJB. En nuestro caso no hemos de preocuparnos, ya que, el servidor de aplicaciones que
hemos elegido incorpora una herramienta para el despliegue que será la que se va a
encargar de crear ambos ficheros XML.
El siguiente paso es crear el módulo EJB que debe contener a nuestro Enterprise Bean pero
también nos despreocuparemos de esta labor debido a que también la realiza la herramienta
de despliegue que acabamos de mencionar.
Hemos decidido que se va a componer de una única página JSP. Dicha página mostrará el
nombre de nuestra empresa y decidirá si los parámetros cantidad y precio que recibe son
correctos. Si es así mostrará el importe de la venta. En cualquier caso, mostrará un
formulario para poder realizar un nuevo cálculo.
Para añadir una página JSP en NetBeans necesitaremos en primer lugar añadir un módulo
web. Para evitar complejidad a este ejemplo, vamos a crear manualmente la página JSP en
el directorio del ejemplo (recordemos que se trata del directorio
58
3 Enterprises Beans y
servicios de contenedor
<%@page contentType="text/html"%>
<%@page import="javax.naming.*"%>
<%@page import="javax.sql.*"%>
<%@page import="java.sql.*"%>
<%@page import="gestion.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
<title>cursoEJB/ejemplo1</title>
</head>
<body>
<%
String errText = "";
String nombreEmpresa = "";
int cantidad = 0;
float precio = 0;
float importe = 0;
try {
// Obtenemos una referencia al contexto de ejecución
Context ctx = new InitialContext();
59
3 Enterprises Beans y
servicios de contenedor
<%
// Si se ha producido algún error, lo mostramos
if (!errText.equals("")) {
%>
<%=errText%>
<%
60
3 Enterprises Beans y
servicios de contenedor
} else {
%>
<h2><%=nombreEmpresa%></h2>
<hr>
<table border="0" cellspacing="2" cellpadding="2">
<tbody>
<tr>
<td><span style="font-weight: bold;">Cantidad:</span><br> </td>
<td><span style="text-decoration:
underline;"><%=cantidad%></span><br> </td>
<td><span style="font-weight: bold;">Precio:</span><br> </td>
<td><span style="text-decoration:
underline;"><%=precio%></span><br> </td>
<td><span style="font-weight: bold;">Importe:</span><br> </td>
<td><span style="text-decoration:
underline;"><%=importe%></span><br> </td>
</tr>
</tbody>
</table>
<hr>
<br>
<form name="calculos" method="post" action="index.jsp">
<table border="0" cellspacing="2" cellpadding="2">
<tbody>
<tr>
<td>Cantidad:</td>
<td><input type="text" name="cantidad" value="0"></td>
<td rowspan="2"><input type="submit" value="Calcular"></td>
</tr>
<tr>
<td>Precio:</td>
<td><input type="text" name="precio" value="0"><br> </td>
</tr>
</tbody>
</table>
</form>
61
3 Enterprises Beans y
servicios de contenedor
<%
}
%>
</body>
</html>
En la página JSP hacemos uso de JNDI en dos ocasiones: la primera es para obtener una
conexión del pool de conexiones del servidor y la segunda es para obtener la referencia al
componente de negocio (el EJB) y poder hacer uso de él. Los nombres JNDI de ambos
recursos son “java:comp/env/jdbc/cursoEJB” y “java:comp/env/ejb/Ventas”,
respectivamente.
La creación del primer recurso la tendremos que hacer manualmente desde la consola de
administración del servidor de aplicaciones. La creación del segundo formará parte del
despliegue de nuestra aplicación web, pero para que sea así tendremos que indicarlo dentro
del fichero de despliegue, más concretamente, en el fichero descriptor de despliegue del
componente EJB.
Si observamos el código de la página JSP, veremos que mediante JDBC utilizamos una
sentencia SQL para obtener información de una tabla. Esto implica que tendremos que crear
dicha tabla para hacer funcionar nuestro ejemplo. La tabla en cuestión se llama “empresa” y
contendrá una única columna denominada “nombre”, de tipo VARCHAR y longitud 30. Tras
crear la tabla le añadiremos una fila con el nombre de nuestra empresa ficticia.
Para crear y rellenar la tabla utilizaremos el programa IBAccess y una vez dentro
emplearemos la opción de abrir una base de datos existente y seleccionaremos el alias
“CURSOEJB”, que creamos en el apartado en el que tratábamos de la instalación de
IBAccess. Una vez abierta la ventana de la base de datos, seleccionaremos el botón “Shows
the exec SQL window”, que aparece resaltado en la siguiente figura.
62
3 Enterprises Beans y
servicios de contenedor
A continuación, observaremos una ventana que nos va a permitir ejecutar sentencias SQL
interactivamente. Ejecutaremos las líneas siguientes teniendo en cuenta que se han de
ejecutar de una en una:
Cuando cerremos esta ventana nos pedirá confirmación para guardar la transacción en curso
(la que hemos generado con la sentencia INSERT), responderemos afirmativamente y
podremos salir de IBAccess. No lo vamos a utilizar más por el momento.
Para cerrar este apartado, mencionaremos que nos faltaría el descriptor de despliegue del
módulo web (el fichero “web.xml”) y empaquetarlo todo en el correspondiente fichero WAR.
Pero al igual que vimos en el apartado del componente EJB, también serán tareas que
delegaremos en la herramienta de despliegue del servidor de aplicaciones.
El último uso que hicimos del servidor de aplicaciones fue entrar en la consola de
administración para hacernos una idea de su estructura. Sabemos que nuestro servidor
funciona, pero hasta el momento poco uso podemos darle puesto que no hemos hecho nada
con él.
En este apartado realizaremos dos labores de administración. Por una parte, el servidor de
aplicaciones debe saber donde están las clases que le van a permitir conectarse a la base de
datos, es decir, el controlador JDBC. La segunda tarea depende de la primera y consiste en
63
3 Enterprises Beans y
servicios de contenedor
definirle un recurso de tipo fuente de datos (Data Source) y asignarle un nombre JNDI. El
servidor de aplicaciones puede implementar un pool de conexiones (uno o más), de donde
puede suministrar conexiones a los clientes que las solicitan y con la ventaja de que reutiliza
dichas conexiones.
Lo primero que vamos a hacer es indicarle al servidor donde se encuentran las clases del
controlador JDBC para que pueda cargarlas cuando las necesite. Recordemos que las clases
estaban almacenadas en el fichero JAR siguiente: “firebirdsql-full.jar” que estaba almacenado
en “/opt/firebird/jdbc” o en “c:\firebird\jdbc”, según la plataforma fuera Linux o Windows,
respectivamente.
Una vez identificados, seleccionaremos el nodo “Application Server” y se nos mostrará una
ventana con información general del servidor. Seguidamente, seleccionaremos la pestaña
“Configuración JVM” y la opción “Configuración de ruta”. Podemos verlo en la figura
siguiente.
64
3 Enterprises Beans y
servicios de contenedor
Dentro de esta ventana se configuran los directorios y ficheros de clases que utiliza el
servidor agrupados en distintas categorías. En nuestro caso tenemos que añadir el fichero
JAR del controlador JDBC, y un buen lugar para él es el apartado “Sufijo de ruta de clase”.
Veamos en la siguiente figura cómo debe quedar después de que lo hayamos añadido.
Las dos primeras líneas no las hemos puesto nosotros, si no que van incluidas cuando se
instala el servidor y sirven para una base de datos que lleva para usar con los ejemplos.
La figura anterior corresponde a un ordenador con Linux. Los usuarios de Windows tendrán
que ponerlo acorde a su plataforma. Sirva como ejemplo que la línea
“/opt/firebird/jdbc/firebirdsql-full.jar” mostrada en la figura, tendrían que indicarla como
“c:\firebird\jdbc\firebirdsql-full.jar”.
Tras este cambio utilizaremos el botón “Guardar” que aparece en la parte superior derecha
de esta misma página, reiniciaremos el servidor para que tenga en cuenta los cambios
realizados y volveremos a entrar en la consola de administración.
65
3 Enterprises Beans y
servicios de contenedor
A continuación, nos irá solicitando una serie de datos de los cuales detallamos sólo los más
importantes junto con los valores que hemos de utilizar. Los campos que no estén en la
siguiente lista no es necesario (ni conveniente) que los cambiemos:
− Propiedad “User”. Cuenta para la conexión con la base de datos. Utilizaremos la del
administrador, que es “sysdba”.
− Propiedad “url”. Tendremos que añadirla utilizando el botón “Agregar propiedad”. Hace
referencia a la url necesaria para conectar con la base de datos. Utilizaremos los
valores “jdbc:firebirdsql:localhost/3050:/home/alumno/cursoEJB/cursoEJB.gdb” o
“jdbc:firebirdsql:localhost/3050:c:\cursoEJB\cursoEJB.gdb” dependiendo de que
estemos en Linux o Windows.
Según vayamos cumplimentando los datos, iremos avanzando con el botón “Siguiente”. Así
hasta terminar utilizando el botón “Finalizar”, tras el cual quedará registrado el pool de
conexiones que acabamos de definir. Para verificar que funciona correctamente es
importante que hagamos uso del botón “Sondeo” y que no devuelva un mensaje de error.
Una vez que tenemos el pool de conexiones definido, vamos a crear un recurso JDBC con un
nombre JNDI para que podamos utilizarlo desde la aplicación. En nuestro ejemplo concreto lo
utilizamos en la página “index.jsp” para obtener el nombre de la empresa desde la base de
66
3 Enterprises Beans y
servicios de contenedor
datos. Seleccionamos el nodo “Recursos JDBC” que cuelga del nodo “JDBC” y pulsamos el
botón “Nuevo”, como podemos ver en la siguiente figura.
Los datos que nos solicita son simples. Conozcamos para qué sirve cada uno y qué valor
hemos de ponerles:
− “Nombre JNDI”. Nombre que queremos asignar al recurso y que será utilizado en las
búsquedas JNDI. No podemos olvidar que los recursos JNDI se encuadran dentro de un
subcontexto y que en nuestro caso se trata del subcontexto “jdbc”. Por lo tanto, en
nuestro ejemplo vamos a llamarlo “jdbc/CursoEJB”.
− “Nombre de conjunto”. Nombre del pool de conexiones del que va a obtener las
conexiones. Seleccionaremos el valor correspondiente al pool que creamos
anteriormente, es decir, el elemento “CursoEJBPool”.
− “Estado”. Indica si está disponible o no. Lo dejaremos seleccionado para poder hacer
uso de él.
67
3 Enterprises Beans y
servicios de contenedor
− Hemos escrito las clases que componen el componente EJB y las hemos compilado.
Dichas clases son “VentasHome” (interfaz básica), “Ventas” (interfaz remota) y
“VentasBean” (clase de implementación de la lógica de negocio). No hemos creado el
descriptor de despliegue del componente y, en consecuencia, no hemos podido crear el
módulo EJB que contendrá el EJB.
− Hemos escrito una página JSP para que forme parte del módulo web pero no el
descriptor de despliegue del módulo web y, por lo tanto, tampoco hemos podido crear
dicho módulo. En el módulo web hay elementos (la página JSP) que hacen referencia a
un recurso JNDI llamado “java:comp/env/jdbc/cursoEJB” y a otro recurso JNDI
llamado “java:comp/env/ejb/Ventas”. El primero no nos preocupa porque lo hemos
definido a nivel del servidor, pero el segundo forma parte de la aplicación que vamos a
desplegar. Por lo tanto no podemos olvidar que el módulo web hace referencia al
módulo EJB. De no tenerlo en cuenta, no va a encontrar el recurso JNDI (el
componente EJB) y obtendremos un error.
− Un descriptor de despliegue llamado “web.xml” por cada módulo web. Debe ir incluido
en el fichero WAR correspondiente al módulo, dentro del directorio “WEB-INF”
68
3 Enterprises Beans y
servicios de contenedor
Por su parte, el servidor de aplicaciones que hemos elegido requiere de los siguientes
descriptores:
Ya hemos visto qué tenemos y qué nos falta. Afortunadamente lo que nos falta nos lo va a
suministrar la herramienta de despliegue.
69
3 Enterprises Beans y
servicios de contenedor
El área de trabajo está dividida en dos zonas principales (sin tener en cuenta el menú y
la barra de botones):
− A la izquierda tenemos un panel que nos muestra una estructura de tipo árbol. En ella
podremos desplazarnos por las aplicaciones que vayamos creando o abriendo con esta
herramienta y por la lista de servidores que hayamos definido (por defecto viene
incluido el servidor local).
− En la parte derecha tenemos un panel que irá mostrando información relevante del
elemento seleccionado en el árbol de la izquierda.
Lo primero que tenemos que hacer es crear una aplicación J2EE. Para ello seleccionaremos
en el menú la opción “Archivo”, después la opción “Nuevo” y, por último, la opción
“Application...”. Aparecerá una caja de diálogo solicitándonos el nombre del fichero y la
descripción de la aplicación.
Ya hemos creado una aplicación, pero está vacía, ya que, no tiene ningún componente. El
próximo paso será crearle un módulo EJB con las clases que hemos desarrollado para el EJB
de nuestro ejemplo.
A continuación, vamos a pulsar el botón izquierdo del ratón sobre el nodo “ejemplo1”, que es
la aplicación recién creada, para seleccionarlo. Después, elegimos en el menú la opción
“Archivo” y las subopciones “Nuevo” y “Enterprise Bean...”. Aparecerá un asistente que nos
guiará a través del proceso de adición del componente EJB.
70
3 Enterprises Beans y
servicios de contenedor
1
2
1. Tenemos que decidir dónde incluir el EJB y se nos ofrecen tres opciones. En nuestro
caso no disponemos de ningún módulo, por lo que elegiremos que se cree uno nuevo
para almacenar el EJB.
2. Nombre del fichero JAR que se creará para el módulo EJB. Puesto que sólo vamos a
tener uno, lo hemos llamado “móduloEJB” para que destaque. Una aplicación J2EE
real podría tener más de uno y sería conveniente que tuvieran nombres descriptivos.
71
3 Enterprises Beans y
servicios de contenedor
2
3
4
5
La siguiente ventana sirve para que indiquemos qué papel juega cada una de las clases que
componen el EJB.
3. Como tipo, tendremos que elegir entre bean con estado y bean sin estado. En
nuestro caso se trata de un bean sin estado (“Stateless Session”).
En la figura anterior pudimos ver que existen dos grupos relativos a las interfaces:
− “Interfaces Locales”. Las interfaces locales son aquellas que permiten comunicar con
componentes EJB que estén en la misma unidad de despliegue. En nuestro ejemplo no
72
3 Enterprises Beans y
servicios de contenedor
podemos utilizarlas porque, aunque la página JSP y el componente EJB forman parte
de la misma aplicación, no las hemos definido.
Una vez terminado el asistente, nos aparecerá un nuevo nodo llamado “moduloEJB” colgando
del nodo de la aplicación. A su vez, del módulo EJB colgarán tantos nodos como enterprise
beans tengamos. En nuestro caso sólo el que acabamos de crear y que hemos llamado
“VentasBean”.
Recordemos que es en el descriptor de despliegue del módulo EJB donde se define el nombre
JNDI que van a utilizar los enterprise beans que contiene dicho módulo. Con la creación del
módulo que acabamos de realizar ya se le ha asignado un nombre JNDI por defecto al bean
de nuestro ejemplo. Además, en la página JSP hacíamos uso de dicho nombre y habíamos
decidido que sería “java:comp/env/ejb/Ventas”. Por lo tanto, vamos a seleccionar el nodo
“VentasVean” y, a continuación, el botón “Preferencias específicas de Sun...” en la pestaña
“General”. En la ventana que aparece podremos ver que el nombre JNDI asignado es
“VentasBean”. Se lo cambiaremos por el de “ejb/Ventas” editando la celda correspondiente.
En la figura siguiente podemos ver cómo quedaría.
De nuevo, nos detendremos sólo en aquellas ventanas que tengan datos trascendentes. En
las demás podremos simplemente pulsar el botón “Siguiente >”.
Empezamos por la siguiente ventana, muy parecida a la que ya viéramos cuando añadimos
el enterprise bean.
73
3 Enterprises Beans y
servicios de contenedor
1
2
1. De las tres opciones posibles, nos interesa crear un nuevo módulo web (hasta el
momento nuestra aplicación no tiene ninguno).
2. Nombre del fichero WAR que va a contener el módulo web. Al igual que con los
módulos EJB, debería tener un nombre más descriptivo, pero en nuestro caso no nos
importa porque es el único que va a contener la aplicación J2EE.
3. Este botón se utiliza para indicar qué elementos forman parte del módulo web.
Nosotros sólo vamos a añadirle la página “index.jsp” pero en una situación real
tendríamos que añadir todos aquellos ficheros que compongan el módulo web
(páginas JSP, ficheros HTML, hojas de estilo, imágenes, etc.).
En la ventana en que nos solicite el tipo de componente web, elegiremos que no es ningún
componente. La utilidad de esta ventana es que podamos definir características adicionales
dependiendo del tipo de componente.
74
3 Enterprises Beans y
servicios de contenedor
Tras la creación del módulo web nos quedan una serie de cambios por realizar. Para todos
ellos tendremos que tener seleccionado el nodo “moduloWEB” en el árbol.
También definiremos una página de bienvenida por defecto para que sea la que se muestre
cuando se omita la página durante la navegación. A nosotros nos conviene que sea la página
“index.jsp” porque además es la única que tiene nuestro módulo web.
Lo próximo va a ser añadir la referencia al enterprise javabean para que el módulo web
pueda hacer uso de él. Seleccionaremos la pestaña “Referencias de EJB” y utilizaremos el
botón “Añadir...”. La información que tenemos que completar es la siguiente:
− “Tipo de EJB”. Tipo de bean. Podemos elegir entre los tipos “Session” y “Entity”. En
nuestro caso estamos hablando de un bean de sesión.
75
3 Enterprises Beans y
servicios de contenedor
− “Interfaz de inicio”. Interfaz básica del EJB. Tenemos que incluir el nombre del paquete
al que pertenece.
No obstante, nuestro módulo web no sólo hace uso del EJB. Recordemos que también
utilizamos el pool de conexiones del servidor para obtener una conexión con la base de datos
y realizar una consulta. No accedemos directamente al pool de conexiones, sino que
hacemos referencia a un recurso JNDI que suministra una fuente de datos.
76
3 Enterprises Beans y
servicios de contenedor
− “Nombre JNDI”. Éste será el nombre del recurso JNDI que queremos utilizar. Le
asignaremos el valor “jdbc/CursoEJB”, que es con el que lo definimos en la consola de
administración del servidor de aplicaciones.
− “Nombre de usuario”. Nombre del usuario para la conexión con la base de datos.
Utilizaremos la cuenta de administración por lo que el usuario es “sysdba”.
En la siguiente figura podemos ver cómo quedarían los cambios que tenemos que realizar.
77
3 Enterprises Beans y
servicios de contenedor
Con todo lo que hemos realizado, ya hemos finalizado. El proceso ha sido un poco largo y
delicado y si algo no está como debiera, la aplicación no va a funcionar. Afortunadamente
esta herramienta suministra un mecanismo de comprobación de errores. Concretamente se
basa en comprobar que la aplicación cumple con la especificación J2EE. Por ejemplo, puede
ayudarnos a resolver problemas derivados de errores al hacer referencia a recursos JNDI.
Nos aparecerá una ventana en la que podemos discriminar los mensajes obtenidos en base a
su tipo. Elegiremos el modo “Sólo fallos y advertencias” para sólo ver los mensajes de aviso
y error, pulsaremos el botón “OK” y empezará la comprobación. Cuando el proceso termine
nos informará del éxito o de los errores encontrados. En la parte inferior de la ventana se
nos mostrará el detalle del mensaje de error seleccionado (si lo hubiera).
78
3 Enterprises Beans y
servicios de contenedor
En caso de error tendremos que analizar su detalle y realizar la acción correctora que
corresponda.
Se nos mostrará una ventana en la que tendremos que elegir el servidor de aplicaciones e
indicar el nombre y contraseña del administrador de dicho servidor. Seguidamente,
pulsaremos el botón “Aceptar” y comenzará el proceso de despliegue. Iremos viendo una
consola en la que se mostrarán mensajes relativos al proceso.
79
3 Enterprises Beans y
servicios de contenedor
3.7.3. El funcionamiento
Ahora viene la parte más esperada por todo desarrollador: ver en ejecución el código
desarrollado. No esperaremos más y veamos cómo funciona. Posteriormente analizaremos el
trabajo realizado y sacaremos algunas conclusiones.
Para ver el fruto de nuestro esfuerzo tenemos que abrir un navegador y navegar a la
dirección “http://localhost:8080/ejemplo1”. Recordemos lo siguiente:
Las conclusiones que podemos extraer son variadas. Lo primero que nos puede resultar poco
razonable es el volumen de trabajo que hemos tenido que realizar para obtener un producto
tan simple. Utilizando otras tecnologías hubiéramos podido obtener los mismos resultados
con menos esfuerzo. Por lo tanto, alguna ventaja debe tener el esfuerzo realizado.
80
3 Enterprises Beans y
servicios de contenedor
− Si decidimos cambiar la base de datos porque queremos migrar a otra más potente,
bastará con cambiar el recurso JNDI del servidor para que apunte a la nueva base de
datos, si hemos sido cuidadosos.
− Por otro lado, si la aplicación vuelve a dar problemas porque las exigencias ha
aumentado excesivamente, podemos distribuir la carga añadiendo otros servidores y
equilibrando el reparto de componentes J2EE.
81
3 Enterprises Beans y
servicios de contenedor
82
índice_ 4 Beans de sesión
83
4 Beans de sesión
Podemos distinguir dos tipos de beans de sesión: con estado y sin estado. A pesar de tener
matices diferenciadores, ambos tipos tienen muchos aspectos comunes:
− Pueden interactuar con datos compartidos pero no los representan como lo hacen los
beans de entidad.
Tanto tienen en común, que la única forma de distinguirlos es mediante el fichero descriptor
de despliegue. Mediante dicho fichero es capaz el contenedor EJB de saber de qué tipo de
bean de sesión se trata.
La diferencia fundamental entre ambos tipos de beans radica en la forma en la que tratan el
estado. Por estado del bean se entiende el conjunto de variables miembros. De este modo,
un bean de sesión con estado puede mantener los valores de sus variables entre distintas
llamadas a sus métodos, mientras que un bean de sesión sin estado, no.
Esta diferencia entre ambos tipos implica una gran repercusión en el diseño de una
aplicación. Una regla básica a tener en cuenta es que los beans de sesión con estado sólo
debemos utilizarlos en la frontera del modelo de objeto, es decir, en el extremo colindante
con los clientes de la aplicación.
85
4 Beans de sesión
Aunque vamos a tratarlos en el siguiente tema, podemos adelantar que un bean de entidad
es una entidad completa de datos. Por el contrario, los beans de sesión se utilizan para
controlar la interacción entre las entidades de datos y el proceso en su conjunto.
Una arquitectura muy adecuada para muchos objetivos en las aplicaciones corporativas
consiste en organizar los beans en dos capas:
− La capa superior proporciona acceso controlado a estos servicios desde los clientes y
se denomina capa de control de acceso.
Para que esta arquitectura sea efectiva, debemos mantener una cierta cantidad de datos,
llamada estado conversacional, que representa la información creada durante un
intercambio de solicitudes y respuestas. A medida que avanza el diálogo, el estado
conversacional va acumulando información sobre el intercambio, y permite que las
solicitudes que vayan a continuación puedan hacer uso de dicha información.
El contenedor EJB tiene libertad para descartar el estado conversacional después de pasado
un tiempo considerable de inactividad. Es decir, puede destruir el bean de sesión con estado.
A partir de ese momento, si se intentara utilizar se generaría una excepción.
De los patrones de diseño que existen para la tecnología J2EE, uno de los más interesantes
es el que utiliza un bean de sesión para proporcionar una fachada a un cliente.
Se define como patrón a una solución usada comúnmente para la resolución de un problema
común.
86
4 Beans de sesión
Por otro lado, una fachada es una interfaz de nivel superior que se utiliza para configurar un
grupo de interfaces en un subsistema.
En la siguiente figura podemos ver que utilizar un bean de sesión para proporcionar una
fachada implica que sólo exista un punto de entrada a nuestro sistema. Desde el punto de
vista del cliente sólo hay un bean de sesión con el que interactuar. Así pues, la interacción
del cliente queda limitada a la fachada, que a su vez pasa las solicitudes al resto del sistema.
Este patrón es conocido como el patrón de fachada porque proporciona un frontal a
nuestro sistema.
Existen varias razones para utilizar el patrón de fachada. Las más obvias son que se reduce
la complejidad y se minimizan las dependencias entre los subsistemas.
87
4 Beans de sesión
invocación y cada respuesta viaja por la red. Si el cliente invoca la fachada del bean (y
en él se realiza la suma), sólo es necesario que una invocación y una respuesta viajen
por la red.
− La lógica de empresa estará en el nivel correcto. Cuando son necesarios varios beans
para proporcionar una función de empresa, suele haber algún orden y relación en sus
llamadas. Además de vincular estrechamente la capa cliente a la implementación de la
capa de lógica de empresa, realizar las llamadas desde el cliente significa que cierta
cantidad de flujo de trabajo se ubica en la capa cliente. Utilizando un bean de sesión
como fachada, el cliente deja de estar estrechamente vinculado a la lógica de empresa
y se preserva más flujo de trabajo en el servidor. Normalmente se organizan limitando
los beans de entidad a representar filas en su base de datos mientras que un único
método de empresa en un bean de sesión agrupa varias llamadas en uno o varios
beans de entidad.
Como norma general, todos nuestros accesos a los enterprise Javabeans desde las
aplicaciones cliente deberían ser a través de un número reducido de fachadas de beans de
sesión. Lógicamente, esta regla no se aplica a los mismos EJB, que pueden tener beans de
sesión sin fachada o de entidad como clientes; si no fuera así, la fachada necesitaría otra
fachada y así sucesivamente.
88
4 Beans de sesión
Los beans de sesión permiten otro espacio en el que puede almacenarse el estado (lo que
puede facilitar la programación en determinadas circunstancias). El estado puede ser
almacenado de forma unificada con independencia del tipo de clientes.
Sin embargo, las ventajas potenciales que nos puede ofrecer en el desarrollo de la aplicación
debemos equilibrarlas frente al coste de reajustabilidad y rendimiento. Para entender esto,
veamos algunos detalles acerca del funcionamiento del contenedor EJB.
89
4 Beans de sesión
En la figura anterior podemos ver que todos los beans de sesión están asociados uno a uno
con un cliente dado. El contenedor crea el EJB cuando el cliente invoca el método “create ()”,
y lo destruye cuando invoca el método “remove ()”.
Por ejemplo, si hubiera mil clientes web utilizando nuestra aplicación, habría mil beans de
sesión en el servidor. Por el contrario, con un bean de sesión sin estado, damos al
contenedor la oportunidad de optimizar los recursos utilizando la reserva de beans. Al no
tener estado, no hay ningún efecto sobre el bean desde ninguna llamada de cliente, por lo
que puede ser reutilizado en múltiples clientes. En la siguiente figura podemos ver un
ejemplo.
Esto tiene un efecto muy importante sobre los recursos disponibles en el servidor. En lugar
de tener mil beans de sesión sin estado, el contenedor puede tener una reserva de sólo cien.
Claro está que la reserva debería ajustarse dinámicamente para cubrir un número
indeterminado de solicitudes.
Por lo que acabamos de ver, una reserva es una colección de instancias que se encuentran
disponibles para ser utilizadas. El servidor podría reservar componentes, hilos, conexiones a
bases de datos y otros recursos. Y una gran ventaja es que estas reservas son transparentes
de cara al desarrollo de las aplicaciones.
Quizás nos preguntemos que si los beans de sesión sin estado no tienen estado, por qué
habrían de tener variables. La respuesta es que pueden tener estado que no esté asociado a
un bean concreto (un socket, por ejemplo).
Tampoco deben ser multihilo por motivos de diseño, ya que, se facilita el modelo de
desarrollo para componentes EJB. A un EJB nunca accede más de un hilo a la vez, por lo que
no necesitamos preocuparnos de la sincronización del acceso al estado.
90
4 Beans de sesión
Un bean de sesión sin estado pueden tener estado asociado a un cliente en una llamada de
método dado, pero no puede mantener dicho estado entre distintas llamadas a métodos.
Para ayudar al contenedor EJB en la gestión de un gran número de beans de sesión con
estado, la especificación J2EE incluye las retrollamadas y reglas que otorgan al contenedor la
capacidad de trasladar un bean de sesión con estado a almacenamiento temporal, y de
restaurarlo posteriormente desde ese almacén. El algoritmo que utilice el contenedor para
decidir cuándo debe almacenarlo es específico del contenedor EJB. La operación de
almacenamiento de un bean se denomina pasivación y la reactivación se conoce como
activación.
La activación y pasivación son métodos muy efectivos para tratar los problemas de la
limitación de recursos en los servidores. No obstante, si la reajustabilidad es importante para
nuestra aplicación, lo mejor es mantener el estado en el cliente y pasarlo de vuelta al
servidor con cada invocación.
4.5. LA PERSISTENCIA
Sin embargo, podemos no hacer uso de los beans de entidad y acceder directamente a un
almacén de datos desde nuestros beans de sesión. Es decir, nuestros beans de sesión
pueden ser los encargados de soportar la funcionalidad de los beans de entidad. Pero como
es normal, dejaremos de usar determinados servicios de persistencia del contenedor, lo que
añadirá complejidad a nuestro desarrollo.
En algunas ocasiones nos puede merecer la pena utilizar el acceso directo a los datos
mediante JDBC desde nuestros beans de sesión en lugar de utilizar beans de entidad. En
otras ocasiones, utilizar un enfoque de bean de sesión puede tener un efecto perjudicial en el
rendimiento porque terminaríamos implementando muchos servicios complejos que los
servidores de aplicación tienen más que optimizados. Las situaciones más comunes
requerirán el uso de beans de entidad con persistencia gestionada por contenedor para
operaciones sobre entidades individuales y conjuntos reducidos de valores. Los beans de
sesión serán más apropiados para operaciones sobre un conjunto de datos.
91
4 Beans de sesión
4.6. UN EJEMPLO
La funcionalidad del ejemplo se va a limitar a una página JSP que nos muestre la relación de
clientes de nuestra base de datos y nos permita las operaciones de creación, modificación y
borrado sobre dichos clientes.
− Componente EJB de sesión sin estado. Va a ser la fachada de cliente. El único punto
posible a través del cual los clientes de la aplicación podrán acceder a la capa EJB.
Para el desarrollo y despliegue del ejemplo vamos a usar las mismas herramientas utilizadas
en el tema anterior, por lo que no vamos a repetir los detalles ya descritos.
Lo primero será ejecutar el entorno de desarrollo NetBeans y crear el proyecto con el nombre
“ejemplo2”, creándose la siguiente estructura de directorios en los respectivos sistemas
operativos:
En Linux: /home/alumno/cursoEJB/ejemplo2
En Windows: c:\cursoEJB\ejemplo2
92
4 Beans de sesión
package gestion;
public class ClienteDetalle implements java.io.Serializable {
private Integer cliente = null;
private String nombre = null;
Anteriormente vimos que el bean de sesión que vamos a desarrollar va a formar parte de la
fachada de la capa EJB. Nuestro bean estará en la frontera del modelo de componentes EJB y
será el único punto de entrada para los clientes que necesiten los servicios de la capa. En
dicha capa reside la lógica de negocio, y la fachada de cliente será la responsable de
canalizar los accesos.
93
4 Beans de sesión
Nuestro ejemplo es muy sencillo porque tras la fachada sólo se esconde un bean de entidad,
y la relación entre los métodos del bean de sesión y del bean de entidad es de uno a uno. Es
decir, cuando se invoca cualquier método del bean de sesión, éste llama en cada caso a un
método concreto (y sólo a uno) del bean de entidad. Esta situación puede parecer absurda,
ya que, el bean de sesión es un mero intermediario que no aporta nada (aparentemente). La
página JSP podría prescindir del bean de sesión y llamar directamente al método específico
del bean de entidad.
No obstante, las situaciones reales no suelen ser tan simples. Nos podemos encontrar con
beans de sesión que invocan a uno o más métodos de uno o más beans (del tipo que sean) y
en un orden determinado. Forma parte de la lógica de negocio que dichas llamadas se
realicen, y que sea en el orden necesario. Confiar esto al cliente (la página JSP) no es lo más
acertado. Estaríamos pasando la lógica de negocio a una capa que no le corresponde.
Además, si en un futuro necesitamos desarrollar una página JSP adicional o un cliente de
aplicación, también tendríamos que implementar en ellos la lógica de negocio, con el
consiguiente riesgo de errores.
Cuando decimos que el bean de sesión es el único punto de entrada a la capa donde reside la
lógica de negocio, no estamos haciendo referencia a que sea aconsejable acceder a través de
él. En este caso concreto se trata de que sea imposible acceder al bean de entidad puesto
que sólo el bean de sesión define una interfaz remota. Aunque lo vamos a ver en el próximo
tema, comentaremos aquí que el bean de entidad sólo tiene interfaz local, lo que impide que
se tenga acceso a él desde fuera de la unidad de despliegue de la que forma parte.
Lo próximo que vamos a hacer es crear la clase y las dos interfaces que necesitará nuestro
EJB de sesión. Utilizaremos el botón derecho del ratón sobre el nodo “gestion” y
seleccionamos la opción New y subopción Java Class. Esta operación la realizaremos tres
veces y los nombres que emplearemos serán los siguientes: MtoClientesHome,
MtoClientes y MtoClientesBean.
A continuación, vamos a modificar el contenido de cada clase para que se ajuste a lo que
queremos de ella. Concretamente, dos de esas clases deberían ser interfaces, por lo que las
cambiaremos. Pero veamos primero cual es la finalidad de cada una de ellas:
94
4 Beans de sesión
package gestion;
import javax.ejb.*;
public interface MtoClientesHome extends javax.ejb.EJBHome {
public gestion.MtoClientes create ()
throws javax.ejb.CreateException, java.rmi.RemoteException;
package gestion;
import javax.ejb.*;
public interface MtoClientes extends javax.ejb.EJBObject {
95
4 Beans de sesión
Por último, a la clase MtoClientesBean, que es la que contiene el código que soporta las
reglas de negocio, le escribimos el siguiente código:
package gestion;
import javax.naming.*;
import javax.ejb.*;
import java.util.*;
public class MtoClientesBean implements javax.ejb.SessionBean {
private javax.ejb.SessionContext context;
public void setSessionContext (javax.ejb.SessionContext aContext) {
context=aContext;
}
public void ejbActivate () { }
public void ejbPassivate () { }
public void ejbRemove () { }
public void ejbCreate () { }
public java.util.Vector findAll()
throws javax.naming.NamingException, javax.ejb.FinderException {
Vector clientesDetalle = new Vector();
Context ctx = new InitialContext();
Object obj = ctx.lookup ("java:comp/env/ejb/Clientes");
LocalClientesHome home = (LocalClientesHome)
javax.rmi.PortableRemoteObject.narrow(obj,
LocalClientesHome.class);
Collection clientes = home.findAll();
if (clientes!=null) {
Iterator iter = clientes.iterator();
while (iter.hasNext()) {
ClienteDetalle detalle =
((LocalClientes)iter.next()).getClienteDetalle();
clientesDetalle.addElement(detalle);
}
}
return clientesDetalle;
}
public void alta(java.lang.Integer cliente, java.lang.String nombre)
throws javax.naming.NamingException, javax.ejb.CreateException,
96
4 Beans de sesión
java.sql.SQLException {
Context ctx = new InitialContext();
Object obj = ctx.lookup ("java:comp/env/ejb/Clientes");
LocalClientesHome home = (LocalClientesHome)
javax.rmi.PortableRemoteObject.narrow(obj,
LocalClientesHome.class);
try {
LocalClientes existe = home.findByPrimaryKey (cliente);
throw new java.sql.SQLException ("Ya existe un cliente con el
código " + cliente.toString ());
} catch (FinderException e) {
home.create (cliente, nombre);
}
}
public void baja (java.lang.Integer cliente)
throws javax.naming.NamingException, javax.ejb.FinderException,
javax.ejb.RemoveException {
Context ctx = new InitialContext ();
Object obj = ctx.lookup ("java:comp/env/ejb/Clientes");
LocalClientesHome home = (LocalClientesHome)
javax.rmi.PortableRemoteObject.narrow(obj,
LocalClientesHome.class);
LocalClientes clientes = home.findByPrimaryKey (cliente);
clientes.remove ();
}
public void modificacion (java.lang.Integer cliente, java.lang.String nombre)
throws javax.naming.NamingException, javax.ejb.FinderException {
Context ctx = new InitialContext ();
Object obj = ctx.lookup ("java:comp/env/ejb/Clientes");
LocalClientesHome home = (LocalClientesHome)
javax.rmi.PortableRemoteObject.narrow(obj,
LocalClientesHome.class);
LocalClientes clientes = home.findByPrimaryKey (cliente);
clientes.modify (new ClienteDetalle (cliente, nombre));
}
}
97
4 Beans de sesión
Vamos ahora con el componente web. Hemos determinado que se va a componer de una
única página JSP. Dicha página mostrará la relación de los clientes que existen y nos
permitirá realizar las operaciones de creación, modificación y borrado sobre ellos.
<%@page contentType="text/html"%>
<%@page import="javax.naming.*"%>
<%@page import="java.rmi.*"%>
<%@page import="javax.ejb.*"%>
<%@page import="java.util.Iterator"%>
<%@page import="gestion.ClienteDetalle"%>
<%@page import="gestion.MtoClientesHome"%>
<%@page import="gestion.MtoClientes"%>
<%
String errorText = "";
Iterator clientes = null;
try {
// Obtenemos una referencia al contexto de ejecución
Context ctx = new InitialContext ();
// Obtenemos una referencia al componente EJB
Object obj = ctx.lookup ("java: comp/env/ejb/MtoClientes");
98
4 Beans de sesión
99
4 Beans de sesión
100
4 Beans de sesión
<TD> </TD>
</TR>
<%
while (clientes.hasNext()) {
ClienteDetalle item = (ClienteDetalle)clientes.next();
%>
<TR>
<TD align="right"><A href="javascript:;" title="Borrar el cliente"
onclick=
"javascript:baja(<%=item.getCliente()%>);">
<%=item.getCliente()%></A></TD>
<TD><INPUT type="TEXT" name="nombre<%=item.getCliente()%>"
value="<%=item.getNombre()%>"></TD>
<TD><INPUT type="BUTTON" value="Guardar modificación"
onclick="javascript:modificacion(<%=item.getCliente()%>);"></TD>
</TR>
<%
}
%>
</TABLE>
<HR>
Código:
<INPUT type="text" name="cliente"><br>
Nombre:
<INPUT type="text" name="nombre"><br>
<INPUT type="submit" value="Crear">
</FORM>
<%
}
%>
</body>
</html>
En la página JSP hacemos uso de JNDI para obtener la referencia al componente de negocio
(el EJB de sesión) y poder hacer uso de él. El nombre JNDI del recurso es
“java:comp/env/ejb/MtoClientes” y su creación forma parte del despliegue de la aplicación
J2EE que contiene los componentes EJB. No obstante, para que sea así tendremos que
indicarlo dentro del fichero descriptor de despliegue del componente EJB. En nuestro caso lo
101
4 Beans de sesión
Nos falta crear el bean de entidad para tener todo el código fuente que necesitamos, pero
vamos a dejarlo para el siguiente tema, en el que estudiaremos este tipo de EJB.
En lo que respecta a la aplicación J2EE que se va a componer del módulo web, ya estamos
preparados para desplegarla, no necesitamos nada más.
La ejecutaremos y, una vez dentro, lo primero que tenemos que hacer es crear una
aplicación J2EE. Para ello, seleccionaremos en el menú la opción Archivo, después la opción
Nuevo y, por último, la opción Aplicación.... Aparecerá una caja de diálogo solicitándonos
el nombre del fichero y la descripción de la aplicación.
Ya hemos creado una aplicación, pero está vacía. No tiene ningún componente. El próximo
paso será crearle un módulo web con la página JSP que hemos desarrollado.
A continuación, pulsaremos el botón izquierdo del ratón sobre el nodo “ejemplo2”, que es la
aplicación recién creada, para seleccionarlo. En el menú Archivo seleccionaremos las
opciones Nuevo y Componente Web... Nos aparecerá un asistente que nos guiará a través
del proceso de creación.
Nos detendremos sólo en aquellas ventanas que tengan datos trascendentes. En las demás
podremos simplemente pulsar el botón Siguiente >.
102
4 Beans de sesión
Como vemos en la figura anterior, no podemos olvidar añadir la clase ClienteDetalle y las
interfaces MtoClientes y MtoClientesHome, ya que se hace referencia a ellas en la página
JSP.
103
4 Beans de sesión
Tras la creación del módulo web nos quedan una serie de cambios por realizar. Para todos
ellos tendremos que tener seleccionado el nodo moduloWEB en el árbol.
También definiremos una página de bienvenida por defecto para que sea la que se muestre
cuando se omita la página durante la navegación. A nosotros nos conviene que sea la página
“index.jsp”, porque además es la única que tiene nuestro módulo web.
Lo próximo va a ser añadir la referencia al enterprise javabean para que el módulo web
pueda hacer uso de él. Seleccionaremos la pestaña “Referencias de EJB” y utilizaremos el
botón “Agregar...”. La información que tenemos que completar es la siguiente:
− “Tipo EJB”. Tipo de bean. Podemos elegir entre los tipos “Session” y “Entity”. En
nuestro caso estamos hablando de un bean de sesión.
− “Interfaz de inicio”. Interfaz básica del EJB. Tenemos que incluir el nombre del paquete
al que pertenece.
104
4 Beans de sesión
enterprise bean al que queremos hacer referencia. Nosotros vamos a optar por hacer
referencia a él utilizando su nombre JNDI. Por lo tanto, será su nombre JNDI el que
indicaremos aquí, es decir, el valor “ejb/MtoClientes”.
Se nos mostrará una ventana en la que tendremos que elegir el servidor de aplicaciones e
indicar el nombre y contraseña del administrador de dicho servidor, y seguidamente
pulsaremos el botón Aceptar para comenzar el proceso de despliegue. Iremos viendo una
consola en la que se mostrarán mensajes relativos al proceso.
Una vez finalizado el despliegue de la aplicación habremos terminado la primera parte del
ejemplo. Si queremos podemos probarla abriendo el navegador web y apuntando a la
105
4 Beans de sesión
106
4 Beans de sesión
− Un bean de sesión con estado puede mantener los valores de sus variables
entre distintas llamadas a sus métodos, mientras que un bean de sesión sin
estado, no.
− Uno de los patrones de diseño más interesantes que existe para la tecnología
J2EE es el que utiliza un bean de sesión para proporcionar una fachada a un
cliente.
− Una fachada es una interfaz de nivel superior que se utiliza para configurar
un grupo de interfaces en un subsistema. La utilización de fachadas aporta
numerosas ventajas en el desarrollo y mantenimiento de los sistemas de
software.
107
índice_ 5 Beans de entidad
109
5 Beans de entidad
El concepto fundamental que debemos tener presente acerca de los Enterprises JavaBeans
de entidad es que no se trata de meras clases, sino que representan entidades del modelo
de análisis de una aplicación. Estas entidades pueden corresponderse con conceptos
concretos, como clientes y proveedores, o con conceptos abstractos, como un proceso
productivo.
Un bean de sesión puede acceder a datos, no obstante, no puede darnos una representación
de objeto de dichos datos. ¿Qué significa esto? ¿Qué diferencia supone frente a la utilización
de beans de sesión?
En el tema anterior dijimos que el estado que mantienen los beans de sesión con estado es
privado. Esto significa que sólo el cliente que lo utiliza puede manipular el estado del bean.
Si varios clientes acceden a un mismo bean de sesión, cada uno de ellos accede a una
instancia distinta que le suministra el contenedor EJB. En el caso de los beans de entidad, el
estado es almacenado en la base de datos, por lo que varios clientes pueden acceder
simultáneamente a un mismo bean. Cada bean de entidad es único y cualquier cliente que
acceda a los datos tendrá que pasar por él.
111
5 Beans de entidad
acceso a datos, sino que también nos puede proporcionar optimizaciones propias del
contenedor.
5.2. LA PERSISTENCIA
El contenedor EJB puede gestionar el proceso de guardar y restaurar el estado de los beans
de entidad. Esta característica recibe el nombre de persistencia gestionada por contenedor
(CMP, Container Managed Persistence). No obstante, podemos desear que nuestros beans
puedan gestionar la persistencia por si mismos. En este último caso hablaremos de
persistencia gestionada por bean (BMP, Bean Managed Persistence).
Es una decisión muy importante en cualquier aplicación que utilice la tecnología Enterprise
JavaBeans, la relacionada con la forma de gestión de la persistencia que utilizarán los beans
de entidad. La experiencia nos demuestra que la persistencia es uno de los servicios de nivel
de sistema más tediosos cuando se desarrolla la lógica de empresa. Suele utilizarse código
SQL y es un proceso que consume mucho tiempo y es muy proclive a errores.
La especificación EJB ha sido desarrollada, entre otras cosas, para trasladar estos temas de
nivel de sistema al contenedor EJB. Para la mayoría de los casos, lo más adecuado será
aprovechar esa capacidad del contenedor para gestionar la persistencia. Sin embargo, la
persistencia gestionada por bean es una elección importante en determinadas circunstancias.
Tendremos que determinar si nuestro contenedor EJB tiene el nivel de compatibilidad que
necesitamos para aprovechar su gestión de la persistencia. De ser así, deberíamos utilizarlo
para evitarnos escribir el código necesario para la persistencia.
El modelo CMP también aporta otras interesantes ventajas. Por ejemplo, si utilizamos el
modelo BMP, una consulta que recupere varios beans la traduciremos normalmente en dos
llamadas distintas a la base de datos: una para consultar las claves primarias y otra para
recuperar los beans. Si utilizamos CMP, el contenedor puede realizar sólo una solicitud a la
base de datos, cosa que es imposible con BMP. CMP tiene muchas otras ventajas, por lo que
utilizar BMP lo haremos en situaciones muy concretas. En caso de dudas, será mejor que nos
decidamos por CMP.
Si nuestro almacén de datos fuese un sistema ERP o alguna otra aplicación existente,
probablemente necesitaríamos utilizar persistencia gestionada por bean. La diferencia estaría
en que, en lugar de escribir código SQL, probablemente necesitáramos utilizar protocolos
específicos del almacén que no serían compatibles con el contenedor EJB.
112
5 Beans de entidad
Por otra parte, si nuestro almacén de datos es una base de datos relacional y el contenedor
EJB no es compatible con CMP para nuestra aplicación, podríamos optar entre las siguientes
opciones:
− Utilizar otro contenedor EJB. Esta opción no es siempre posible, no obstante, existen
servidores de aplicación con sofisticadas funciones de asociación objeto/relacional en
el mercado. Debemos tener en cuenta las capacidades de los distintos servidores de
aplicación a la hora de decidirnos por alguno de ellos.
113
5 Beans de entidad
Un bean de entidad con persistencia gestionada por contenedor se define por sus campos
CMP, los cuales son los campos Java que están asociados a las tablas de la base de datos.
La especificación EJB impone que estos campos se definan mediante accesores abstractos.
Por ejemplo, un campo de tipo texto llamado “direccionCliente” lo declararemos como:
Podría darse el caso de que en nuestra base de datos no tuviéramos una columna llamada
“direccionCliente”, pero no importa puesto que en el descriptor de despliegue sólo
tendríamos que especificar cómo asociarlo.
El contenedor tiene control total sobre el acceso a los campos. Generará código para cada
accesor y puede optimizar los accesos a la base de datos. Gracias a esto, hay algunas
ventajas que suelen proporcionar los contenedores EJB:
− Posponer la carga de los beans. Dado que el contenedor sabe qué campos son
accesibles y cuándo puede acceder a ellos, no tiene que cargar el estado completo del
bean inicialmente. Puede posponer la carga para cuando realmente se necesite la
información.
− Actualizar sólo los cambios. El contenedor sabe en todo momento qué campos han
sido modificados, por lo tanto, es capaz de actualizar en la base de datos sólo las
columnas correspondientes a los campos que hayan cambiado.
114
5 Beans de entidad
5.4. EJB-QL
Uno de los mayores beneficios de la persistencia gestionada por contenedor es que nos
abstrae del almacén de datos. No necesitamos saber cómo se almacenan los beans o qué
tipo de base de datos está utilizando. El inconveniente de esto, es que se necesita un
lenguaje de consulta que sea independiente de la base de datos.
Una consulta del lenguaje EJB QL es una cadena que se compone de las siguientes tres
cláusulas:
La sintaxis del lenguaje EJB QL es muy extensa y aquí vamos a exponerla brevemente. En la
documentación de la especificación J2EE podremos consultarla al completo.
115
5 Beans de entidad
− Métodos buscadores. Sólo pueden devolver beans del mismo tipo que los creados por
su interfaz inicial. Por lo tanto, el tipo de la cláusula “SELECT” debe coincidir con el
nombre de esquema abstracto del EJB.
Podemos utilizar la palabra clave opcional “DISTINCT” para descartar valores duplicados de
la consulta.
SELECT OBJECT(u)
FROM Usuarios AS u
WHERE u.login = 'juan' AND u.password = '1424'
En nuestro ejemplo, declaramos una variable de tipo “Usuarios”. No se trata del nombre del
bean, en una consulta EJB QL tenemos que utilizar el nombre especificado en la etiqueta
“<abstract-schema-name>” del descriptor de despliegue. Claro que, para simplificar, se
suele utilizar el mismo nombre que el nombre del EJB.
Esta cláusula es opcional y sirve para indicar una expresión que limite los resultados de la
consulta.
La expresión puede ser una combinación de expresiones mediante las palabras “AND” y
“OR”. También podemos incluir los literales “TRUE” y “FALSE”, y operadores como
“BETWEEN”, “IN” o “LIKE”. La igualdad se expresa con el símbolo “=” y la no igualdad con
"<>".
116
5 Beans de entidad
SELECT OBJECT(u)
FROM Usuarios AS u
WHERE u.login = ?1 AND u.password = ?2
Todo bean de entidad debe tener una clave primaria que lo identifique de forma única que se
representa mediante una clase. Dicha clase contendrá la información necesaria para
encontrar su entidad en el almacén persistente y tanto el cliente como el contenedor la
utilizarán cuando necesiten encontrar una determinada instancia. Esta clase tiene que
cumplir determinados requisitos que veremos a continuación. En el caso de los beans de tipo
BMP, los requisitos son:
− La clave primaria puede ser cualquier tipo de objeto Java que se pueda codificar, es
decir, que implemente la interfaz “java.io.Serializable”.
− La clave primaria debe ser un valor único dentro del conjunto de todos los beans.
Adicionalmente, hay algunos requisitos extra para los beans de tipo CMP. El contenedor es el
responsable de manipular los beans y necesita poder crear una clave primaria. Para ello, la
clase de la clave primaria debe suministrar un constructor público sin argumentos.
El contenedor también necesita asociar el estado del bean con el estado de la clase de clave
primaria y viceversa. Para que sea posible, la especificación proporciona dos métodos
diferentes que proporcionan clases clave para beans CMP. El primero es válido para cualquier
117
5 Beans de entidad
caso en general, con independencia del número de campos, mientras que el segundo es para
el caso concreto de un único campo.
El primer método realiza la asociación utilizando una convención de nombres: los campos
públicos de la clase de clave primaria se corresponden con los campos públicos equivalentes
de la clase del bean. Por ejemplo, dado un bean de entidad llamado “Usuarios” y cuya clave
primaria está compuesta por los campos “empresa” y “usuario”, definiremos una clase de
clave primaria llamada “UsuariosPK” que necesitará los dos correspondientes campos (del
mismo tipo y con el mismo nombre). La clase de la clave primaria sería como sigue:
package paquete;
public class UsuariosPK implements java.io.Serializable {
public UsuariosPK() { }
public UsuariosPK(String empresa, Integer Usuario) {
this.empresa = empresa;
this.usuario = usuario;
}
public String getEmpresa() { return this.empresa; }
public Integer getUsuario() { return this.usuario; }
public int hashCode() {
return (empresa+usuario).hashCode();
}
public boolean equals(Object obj) {
if (obj==null || !(obj instanceof UsuariosPK)) {
return false;
}
UsuariosPK pk = (UsuariosPK) obj;
return this.empresa.equals(pk.getEmpresa()) && this.usuario ==
pk.getUsuario();
}
}
118
5 Beans de entidad
Es muy importante tener en cuenta que una vez que hayamos asociado un bean de entidad a
una clave primaria, no debemos reutilizar el mismo objeto de clave primaria para otro bean.
Para evitarlo, no debemos definir métodos “set... ()” en las clases de clave primaria.
Para el segundo método no es necesario definir una nueva clase, ya que, al tratarse de una
clave primaria de un único campo, la clase para dicha clave puede ser cualquiera de las
suministradas por Java.
Para los beans de entidad de tipo BMP tendremos que especificar la clase de la clave primaria
en el descriptor de despliegue.
Siguiendo con la clase del ejemplo anterior, la especificación de la clave primaria del
descriptor sería la siguiente (tanto para beans BMP como CMP):
<prim-key-class>paquete.UsuariosPK</prim-key-class>
En el caso de que la clave estuviera formada por un único campo, supongamos que sea de
tipo texto, el descriptor sería como sigue:
<prim-key-class>java.lang.String</prim-key-class>
Por último, para un bean de entidad de tipo CMP que utiliza un único campo para la clave
primaria, especificaremos en el descriptor de despliegue el campo del bean que contiene la
clave primaria. El tipo del campo debe ser el mismo que el tipo de clave primaria. A
continuación podemos ver un ejemplo:
<primkey-field>usuario<primkey-field>
119
5 Beans de entidad
El acceso al almacén de datos está compuesto por cuatro tipo de operaciones: creación,
lectura, modificación y borrado.
Para cada una de estas operaciones existe un tipo de retrollamada en los beans de entidad.
Los métodos correspondientes son:
− “ejbLoad()”. Lectura.
− “ejbStore()”. Modificación.
− “ejbRemove()”. Borrado.
Los métodos de retrollamada se definen igualmente tanto para los beans CMP como BMP. Por
el contrario, la implementación es diferente:
− En los beans de tipo CMP los métodos de retrollamada suelen dejarse vacíos (con
excepción del método “ejbCreate()”), dado que es responsabilidad del contenedor
implementar la funcionalidad necesaria de cada método en cuestión.
5.6.1. Creación
El método “create()” se define en la interfaz inicial del bean y puede estar sobrecargado.
Debe devolver la interfaz remota del bean para que cuando el cliente lo invoque, mediante la
interfaz inicial, obtenga una referencia a dicho bean para poder utilizar sus métodos de
empresa.
De igual forma, este método debe lanzar la excepción “java.rmi.RemoteException”, pero sólo
en el caso de los métodos remotos (recordemos que los métodos locales no usan RMI).
También deben lanzar la excepción “javax.ejb.CreateException”, que se utilizará en caso de
que haya algún problema en la creación.
Por cada método “create()” que se defina en la interfaz inicial, deben existir dos métodos
120
5 Beans de entidad
El tipo de retorno del método “ejbCreate()” debe ser la clase de la clave primaria. El código a
escribir en el método irá en función del tipo de persistencia del bean:
− Para los beans CMP, los parámetros del método se utilizarán para inicializar los campos
de estado.
− Para los beans BMP, los parámetros se utilizarán para inicializar los campos de estado
y se escribirá el código correspondiente (normalmente utilizando JDBC) para
almacenar los datos en el almacén de datos.
Por otra parte, el tipo de retorno del método “ejbPostCreate()” debe ser “void”. Este método
es invocado después “ejbCreate()” y, en la mayoría de los casos, suele dejarse vacío.
5.6.2. Lectura
5.6.3. Modificación
En el caso de los beans de tipo CMP este método se suele dejar vacío, mientras que para los
beans BMP habrá que escribir el código correspondiente para que se guarde el estado del
bean en el almacén de datos.
121
5 Beans de entidad
5.6.4. Borrado
Ya hemos visto que los beans de entidad representan datos compartidos entre todos los
clientes. Como es lógico, existe un mecanismo para que dichos clientes tengan acceso a un
determinado bean de entidad. Con los beans de sesión no ocurría esto, puesto que el cliente
obtiene acceso al bean cuando se crea y nadie más lo puede utilizar. Por el contrario, un
bean de entidad puede ser creado por un cliente y utilizado por todos los demás, y para ello,
cada uno de estos clientes debe ser capaz de encontrarlo.
Los métodos buscadores, al igual que el resto de métodos remotos, declararán que pueden
lanzar la excepción “java.rmi.RemoteException”. Adicionalmente, también deben declarar la
excepción “java.ejb.FinderException”.
Los métodos buscadores que devuelven como máximo un único resultado, tendrán como tipo
de retorno la interfaz remota del bean. El ejemplo más claro es el caso del buscador para la
clave primaria; si tenemos un bean cuya clase de implementación se llama “UsuariosBean” y
la clave primaria es un entero, el método “findByPrimaryKey(...)” lo definiremos en la
interfaz “paquete.UsuariosHome”, recibirá un parámetro del tipo de la clase de la clave
primaria y su tipo de retorno será “paquete.Usuarios”. En caso de que no se encuentre
ninguna entidad, este tipo de métodos lanzará la excepción
“javax.ejb.ObjectNotFoundException”, que es una subclase de la clase
122
5 Beans de entidad
“javax.ejb.FinderException”.
En el caso de métodos buscadores que pueden devolver cero o más resultados, el tipo de
retorno será “java.util.Enumeration” o “java.util.Collection”. Un ejemplo posible sería el de
un método buscador por nombre de usuario (puede haber más de uno). Si un buscador de
este tipo no encuentra ninguna entidad, no se lanzará ninguna excepción, simplemente
devolverá una enumeración (o colección) vacía.
La especificación EJB requiere que todos los beans de entidad declaren un buscador llamado
“findByPrimaryKey()” con un único parámetro y que sea del mismo tipo que la clase de clave
primaria. Este método debe devolver la instancia del bean de entidad con dicha clave
primaria o generar una excepción “javax.ejb.ObjectNotFoundException”. El resto de métodos
buscadores son opcionales y podrían no existir.
Por el contrario, en los beans de tipo BMP tendremos que proporcionar la implementación de
los métodos buscadores en la clase de implementación del bean. Dicho método tendrá
parámetros idénticos y tendrá un nombre que coincida, según la siguiente convención: si el
método buscador se denomina “findXXX()”, la implementación del método se denominará
“ejbFindXXX()”.
El tipo de retorno para un método buscador que tiene como máximo un resultado será una
instancia de la clase de clave primaria para esa entidad y el tipo de retorno para una
implementación de método buscador que tiene cero o más resultados será una
implementación concreta de “Collection” o “Enumeration”, según el tipo de retorno del
correspondiente método buscador de la interfaz inicial. Los elementos de “Collection” o
“Enumeration” serán instancias de las clases de clave primaria de las entidades encontradas.
123
5 Beans de entidad
Para terminar con las retrollamadas, nos quedan por describir las dos siguientes:
“ejbActivate()” y “ejbPassivate()”.
La implementación de estos métodos suele dejarse vacía, sin embargo, en algunas ocasiones
puede interesarnos añadirles código para realizar algún tipo de gestión.
En el tema anterior iniciamos un ejemplo conjunto sobre los beans de entidad y los beans de
sesión. Pospusimos la parte correspondiente al bean de entidad hasta que supiéramos qué es
y de qué se compone. Ya estamos listos para ponernos a trabajar.
El bean de entidad que proponemos va a ser un bean con persistencia gestionada por el
contenedor. No vamos a escribir código Java, no utilizaremos JDBC ni sentencias SQL, pero
sí que utilizaremos el lenguaje EJB QL para uno de los métodos buscadores.
Lo primero que haremos será ejecutar el entorno de desarrollo NetBeans. Una vez dentro, el
proyecto “ejemplo2” debería estar creado y de no ser así procederemos a crearlo.
Recordemos que en el tema anterior mencionamos que todas las clases e interfaces las
crearíamos dentro del paquete “gestion”.
En primer lugar vamos a crear la clase y las dos interfaces que necesitará nuestro EJB de
entidad. Utilizaremos el botón derecho del ratón sobre el nodo “gestion” y seleccionamos la
opción “New” y subopción “Java Class”. Esta operación la realizaremos tres veces y los
nombres que emplearemos serán los siguientes: “LocalClientesHome”, “LocalClientes” y
“ClientesBean”.
124
5 Beans de entidad
package gestion;
import javax.ejb.*;
public interface LocalClientesHome extends javax.ejb.EJBLocalHome {
public gestion.LocalClientes findByPrimaryKey(java.lang.Integer aKey)
throws javax.ejb.FinderException;
public LocalClientes create(java.lang.Integer cliente, java.lang.String nombre)
throws javax.ejb.CreateException;
public java.util.Collection findAll() throws javax.ejb.FinderException;
package gestion;
import javax.ejb.*;
public interface LocalClientes extends javax.ejb.EJBLocalObject {
public gestion.ClienteDetalle getClienteDetalle();
public void modify(gestion.ClienteDetalle clienteDetalle);
}
package gestion;
import javax.ejb.*;
public abstract class ClientesBean implements javax.ejb.EntityBean {
private javax.ejb.EntityContext context;
public void setEntityContext(javax.ejb.EntityContext aContext) {
context=aContext;
}
public void ejbActivate() { }
public void ejbPassivate() { }
public void ejbRemove() { }
125
5 Beans de entidad
126
5 Beans de entidad
Ya estamos listos para crear la aplicación web que se compone del módulo EJB con los dos
beans. Nos aseguraremos de que el servidor de aplicaciones se está ejecutando y, a
continuación, ejecutaremos la herramienta de despliegue “deploytool”.
Una vez dentro, lo primero que tenemos que hacer es crear la aplicación J2EE. Para ello,
seleccionaremos en el menú la opción Archivo, después la opción Nuevo y por último la
opción Aplicación... Aparecerá una caja de diálogo solicitándonos el nombre del fichero y la
descripción de la aplicación.
Lo primero que haremos será añadir el bean de entidad. Para ello, pulsaremos el botón
izquierdo del ratón sobre el nodo “ejemplo2b”, que es la aplicación recién creada, para
seleccionarlo. En el menú Archivo seleccionaremos las opciones Nuevo y Enterprise
bean… Nos aparecerá un asistente que nos guiará a través del proceso de creación.
Nos detendremos sólo en aquellas ventanas que tengan datos trascendentes. En las demás
podremos simplemente pulsar el botón Siguiente >.
127
5 Beans de entidad
En la figura podemos ver que no debemos olvidar añadir la clase “ClienteDetalle”, puesto que
es una clase auxiliar que forma parte del bean de entidad.
En la figura anterior podemos ver que hemos de marcar los campos que nos interesa que
sean persistentes, en nuestro caso son “cliente” y “nombre”. También hemos de prestar
atención a seleccionar el campo “cliente” como la clase de la clave primaria. El botón
128
5 Beans de entidad
Tras los pasos anteriores, el proceso de creación del módulo EJB y de adición del bean de
entidad, habrán terminado, exceptuando algunos detalles que vamos a ver seguidamente.
129
5 Beans de entidad
130
5 Beans de entidad
En la lista de campos que nos muestra, hemos modificado la longitud del campo “nombre” a
50 caracteres, ya que, el valor por defecto es excesivo. También haremos uso del botón
Preferencia para la generación de tablas para permitir que se cree la tabla
automáticamente cuando se despliegue la aplicación, y para evitar que se pierda cuando se
elimine la aplicación.
Es muy importante ser extremadamente cuidadoso si se están haciendo pruebas con una
base de datos en explotación y sobre tablas con información importante debido a que, por
defecto, la casilla Eliminar tablas al anular implementación estará seleccionada y eso
implica que se elimine la tabla cuando se elimine la aplicación del servidor. El aspecto de la
ventana de la que estamos hablando es el siguiente:
Lo próximo será añadir el bean de sesión al módulo EJB. Pulsaremos el botón izquierdo del
ratón sobre el nodo moduloEJB y en el menú Archivo seleccionaremos las opciones Nuevo
y Enterprise bean... Nos aparecerá un asistente que nos guiará a través del proceso de
creación.
Al igual que en casos anteriores, sólo iremos viendo las ventanas más importantes. La
primera de ellas es la que nos permite indicar el contenido y en ella podemos ver que ya
están la clase ayudante “ClienteDetalle” y la clase e interfaces del bean de entidad:
“ClientesBean”, “LocalClientes” y “LocalClientesHome”. Ahora tendremos que añadir la clase
e interfaces del bean de sesión, que son: “MtoClientes”, “MtoClientesBean” y
“MtoClientesHome”, como se muestra en la siguiente figura.
131
5 Beans de entidad
En la ventana que se muestra en la figura anterior, hemos de tener en cuenta que el bean de
sesión que estamos añadiendo es sin estado (“Stateless Session”) y que sólo dispone de
interfaces remotas, al contrario de lo que sucedía con el bean de entidad, que sólo ofrecía
132
5 Beans de entidad
interfaces locales.
Tras finalizar el asistente para la adición del bean a nuestro módulo EJB, tendremos que
realizar aún algunas labores previas a poder desplegar la aplicación J2EE.
Recordaremos que el bean de sesión que acabamos de añadir hace uso del bean de entidad,
lo que significa que tenemos que indicarle a la herramienta “deploytool” la información
necesaria para que ésta sea capaz de añadirla al fichero de despliegue.
133
5 Beans de entidad
En esta ventana tendremos que modificar el nombre JNDI, que por defecto tendrá el valor
“MtoClientes”, y ponerle el valor “ejb/MtoClientes”.
Se nos mostrará una ventana en la que tendremos que elegir el servidor de aplicaciones e
indicar el nombre y contraseña del administrador de dicho servidor. Seguidamente
pulsaremos el botón Aceptar y comenzará el proceso de despliegue. Iremos viendo una
consola en la que se mostrarán mensajes relativos al proceso.
Una vez finalizado el despliegue de la aplicación habremos terminado el ejemplo por
completo. Ahora podemos probarla abriendo el navegador web y apuntando a la dirección
“http://localhost:8080/ejemplo2”.
134
5 Beans de entidad
Probemos a crear dos clientes; el primero con código “4” y nombre “Juan”, y el segundo con
código “33” y nombre “Luis”. Para ello, colocaremos los valores en los campos
correspondientes y pulsaremos el botón Crear. Tras la creación de los dos, la ventana
mostrará el siguiente aspecto:
135
5 Beans de entidad
En este ejemplo podemos ver que los tiempos de respuesta de la aplicación son muy buenos,
máxime si tenemos en cuenta que se trata de una aplicación web. Si el servidor de
aplicaciones está instalado en nuestra máquina local y tenemos una red, sería interesante
probar la aplicación desde otro ordenador distinto al nuestro para que comprobemos si se
aprecia diferencia en los tiempos de respuesta.
136
5 Beans de entidad
137
índice_ 6 Beans controlados por mensaje.
Empaquetado y roles
139
6 Beans controlados por mensaje.
Empaquetado y roles
Para el correcto entendimiento de los beans controlados por mensaje es imprescindible tener
unos conocimientos básicos del Servicio de Mensajería de Java, es decir, del API JMS (Java
Messaging Service), el cual no se tratará aquí por quedar fuera del ámbito de este manual.
Un bean controlado por mensaje (también llamado MDB, Message Driven Bean) es un
receptor de mensajes que puede consumir los mensajes de una cola o de una suscripción (o
apartado) mediante el contenedor J2EE. Cuando el contenedor recibe un mensaje, invoca al
bean que corresponda.
Bajo el punto de vista del cliente del bean, un bean controlado por mensaje es un
consumidor que se adapta al API JMS (Java Message Service) y que implementa la lógica de
negocio, pero al cual sólo se puede acceder enviándole un mensaje mediante JMS. Por su
parte, un bean controlado por mensaje puede comunicar con beans de sesión y de entidad.
La consecuencia lógica de esta filosofía es que un bean controlado por mensaje no tiene
estado conversacional con los clientes. Por el mismo motivo, y al contrario de lo que ocurría
con los beans de entidad y de sesión, no tienen interfaces iniciales ni remotas. Esto se debe
a que los clientes no pueden interactuar con ellos, recordemos que es el contenedor quien se
encarga de invocarlos.
Por otro lado, los beans controlados por mensaje tienen una limitación importante debida a
que la cola o el apartado (de la suscripción) es definido en el descriptor de despliegue, en el
período de implementación en lugar del período de ejecución. Además, los selectores de
mensaje también son definidos en dicho descriptor. Esta situación es una característica de
JMS y, aunque la mayoría de los servidores de aplicación existentes permiten saltarse esto,
corremos el riesgo de depender de algún servidor en concreto si hacemos uso de sus
opciones.
141
6 Beans controlados por mensaje.
Empaquetado y roles
6.1.1. Transacciones
Para los beans controlados por mensaje existen dos tipos de gestión de transacciones:
Estos tipos afectan a los atributos transaccionales del método “onMessage()” y al manejo de
la entrega garantizada de los mensajes.
En el caso de las transacciones gestionadas por contenedor, la entrega del mensaje está
garantizada. En cuanto a los atributos transaccionales, sólo se permiten dos:”NotSupported”
y “Required”. Con el primero de ellos, el bean es ejecutado fuera de ninguna transacción,
mientras que con el segundo se abrirá una nueva transacción.
Por último, en lo que respecta a las transacciones gestionadas por bean, los límites de la
transacción deben estar dentro del método “onMessage()”. La entrega del mensaje no está
garantizada a menos que se utilice “javax.transaction.UserTransaction”, y en tal caso la
aplicación cliente será la que demarcará los límites de la transacción.
6.1.2. Ejemplo
import java.util.Date;
import javax.ejb.EJBException;
import javax.ejb.MessageDrivenBean;
142
6 Beans controlados por mensaje.
Empaquetado y roles
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
143
6 Beans controlados por mensaje.
Empaquetado y roles
Al igual que ocurre con los beans de sesión y de entidad, es necesario el descriptor de
despliegue para que la implementación sea posible.
Para la creación del fichero de despliegue, bien sea mediante alguna herramienta o bien
manualmente, es necesario indicar la siguiente información:
− El tipo de bean, que en este caso se trata de un bean controlado por mensaje.
144
6 Beans controlados por mensaje.
Empaquetado y roles
A lo largo de este manual hemos visto en qué consiste la especificación J2EE, concretamente
la parte relacionada con los Enterprise JavaBeans, cómo desarrollar el código y ejecutarlo en
el servidor.
Sin embargo, incluso un conocimiento básico de la tecnología J2EE debe pasar por conocer
cuál es la estructura de empaquetado de una aplicación J2EE y de los distintos componentes
que la forman.
− Un descriptor de despliegue.
6.2.1. Contenedores
J2EE hace una clara distinción entre los recursos que se pueden empaquetar en un archivo
de aplicación (EAR, Enterprise Application Archive) y los recursos que se ejecutan en un
contenedor.
145
6 Beans controlados por mensaje.
Empaquetado y roles
− Archivo WAR de aplicación web. Cada archivo WAR sólo puede contener una única
aplicación web y en caso de que el archivo EAR contenga más de una, cada una de
ellas habrá de tener un nombre de contexto único.
− Archivo JAR de cliente de aplicación. Contendrá una única aplicación Java destinada a
ejecutarse en un contenedor cliente de aplicación.
− Archivo RAR de adaptador de recursos. Contendrá clases Java y las librerías nativas
necesarias para implementar un adaptador de recursos de Arquitectura de Conectores
Java a un sistema de información de empresa.
Cada uno de los componentes debe ser desarrollado y empaquetado por separado, junto con
su correspondiente descriptor de despliegue. El archivo EAR unifica uno o más de estos
componentes en un único paquete, incluyendo su propio descriptor de implementación. Sin
embargo, existen componentes que no se pueden declarar dentro de los archivos EAR, a
146
6 Beans controlados por mensaje.
Empaquetado y roles
pesar de ser utilizados frecuentemente. Entre ellos podemos citar las fuentes de datos JDBC.
Recordemos que en algunos ejemplos de los temas anteriores teníamos componentes que
necesitaban tener acceso a la fuente de datos utilizando el nombre JNDI “jdbc/CursoEJB”.
6.2.3. Paquetes
La construcción de una aplicación J2EE es un proceso compuesto por las siguientes fases:
− Construcción de los componentes individuales como los servlets, las páginas JSP y los
Enterprise JavaBeans.
− Algunos de estos componentes se empaquetan en archivos con formato JAR junto con
el correspondiente descriptor de despliegue de módulo J2EE. Este último se compone
de uno o más componentes J2EE del mismo tipo, por lo tanto, un módulo EJB puede
estar compuesto por uno o más EJB y un módulo web puede estar compuesto por
múltiples servlets y páginas JSP.
− Uno o más módulos J2EE se unen en un archivo EAR junto con un descriptor de
despliegue para obtener la aplicación J2EE.
Un paquete de aplicación J2EE está compuesto por uno o varios módulos J2EE y un
descriptor de despliegue llamado “application.xml” que se ubica en el directorio “META-INF”.
Los archivos se empaquetan (o comprimen) utilizando el formato JAR y se almacenan en un
fichero con extensión “ear”.
147
6 Beans controlados por mensaje.
Empaquetado y roles
Por ejemplo, un archivo EAR que contenga dos módulos EJB y un módulo web podría tener el
siguiente contenido:
− moduloEJB1.jar
− moduloEJB2.jar
− moduloWEB.war
− META-INF\application.xml
Siguiendo con el ejemplo anterior, para crear un fichero de aplicación llamado “MiApl,
utilizaríamos la herramienta “jar” con la siguiente sintaxis:
148
6 Beans controlados por mensaje.
Empaquetado y roles
“http://java.sun.com/dtd/application_1_3.dtd”>
− “<icon>”.
− “<display-name>”.
− “<description>”.
− “<module>”.
Las tres primeras proporcionan información sobre la aplicación, mientras que la última
engloba los módulos J2EE que conforman la aplicación.
<application>
<display-name>Aplicación de ejemplo</display-name>
<module>
<ejb>moduloEJB.jar</ejb>
</module>
</module>
<web>
<web-uri>moduloWEB.war</web-uri>
<context-root>/miaplicacion</context-root>
</web>
</module>
</application>
<?xml version=”1.0” encoding=”UTF-8”?>
149
6 Beans controlados por mensaje.
Empaquetado y roles
El primer rol que aparece en escena está relacionado con la instalación de los productos y
herramientas J2EE. Una vez instalados, el proveedor de componentes de aplicación puede
desarrollar dichos componentes, los ensambladores de aplicaciones pueden ensamblarlos, y
los implementadores pueden desplegarlos.
En una organización pequeña la mayoría de estos roles suelen estar desempeñados por la
misma persona o equipo, sin embargo, en las organizaciones grandes suelen estar
distribuidos en distintos equipos. Cada rol utiliza como entrada la salida del rol precedente,
hasta llegar al final del flujo de trabajo. Por ejemplo, en la fase de desarrollo de
componentes el desarrollador de Enterprise JavaBeans entregará ficheros JAR que serán
utilizados en la fase de ensamblaje, en la cual otro desarrollador combinará los ficheros JAR
en una aplicación J2EE para crear un fichero EAR.
150
6 Beans controlados por mensaje.
Empaquetado y roles
También existen roles, dentro de la especificación J2EE, que pueden tener características
similares a las de los proveedores de componentes. Entre estos roles se incluyen los
desarrolladores de documentos, autores de JSP, desarrolladores de beans de negocio y
desarrolladores de adaptadores de recursos.
El ensamblador de aplicaciones es la organización que recibe los ficheros JAR con los
componentes de la aplicación y los ensambla en un fichero EAR. También es responsable de
crear el descriptor de despliegue de la aplicación J2EE y de identificar cualquier recurso
externo del que pueda depender dicha aplicación. Entre estos recursos puede haber
bibliotecas de clases, roles de seguridad y entornos de nombrado. El ensamblador de
aplicaciones suele utilizar las herramientas suministradas por el proveedor de productos J2EE
y por el proveedor de herramientas.
6.3.5. El implementador
151
6 Beans controlados por mensaje.
Empaquetado y roles
En todos los ejemplos de este manual nosotros hemos asumido cuatro de los seis roles
expuestos. Exceptuando los roles de proveedor de productos J2EE y proveedor de
herramientas, hemos desempeñado los siguientes:
152
6 Beans controlados por mensaje.
Empaquetado y roles
153
A
glosario_
API. Siglas de Application Program Interface (Interfaz para programas de
aplicación). Conjunto de convenciones de programación que definen cómo se
invoca un servicio desde un programa.
155
P
glosario_
PROTOCOLO. Conjunto de normas comunes que deben ser observadas por dos
elementos que desean comunicarse para poder entender los mensajes que se
reciben. En general, este término se emplea para la definición de técnicas de
comunicación en una red. En el caso de Internet, un ejemplo es el protocolo IP,
que define el formato de los mensajes que se envían de un nodo a otro. Sobre
este protocolo básico se pueden construir otros que proporcionan mayor
funcionalidad, como es el caso de HTTP, que permite transmitir la información
contenida en páginas web.
156
Scott W. Ambler y Tyler Jewell. Mastering Enterprise JavaBeans Second Edition.
bibliografía_
Ed. The Middleware Company. 2002.
Paul J. Perrone. J2EE Developer's Handbook. Ed. Sams Publishing. 2003.
157