Tema 6 Enterprise Java Beans Dr Diego Lz
Tema 6 - Enterprise Java. Beans Dr. Diego Lz. de Ipiña Gz. de Artaza http: //paginaspersonales. deusto. es/dipina/cursos/J 2 EECesine. zip dipina@eside. deusto. es
Enterprise Java. Beans n n Tecnología por excelencia para el desarrollo de componentes en la parte servidora con la plataforma J 2 EE Deben de conformar a la especificación de J 2 EE y sólo se pueden ejecutar en un contenedor de EJBs n n Los contenedores de EJBs son lo que hacen a esta tecnología tan atractiva, ya que ofrecen soporte para: n n Última especificación EJB 2. 1 disponible en: http: //java. sun. com/products/ejb/docs. html Transacciones Seguridad Persistencia … tanto de manera programática como declarativa
Beneficios de EJBs n Aplicaciones basadas en EJBs son difíciles de desarrollar (tecnología difícil de asimilar), sin embargo: n n Aplicaciones basadas en EJBs nos permiten concentrarnos en la lógica de negocio, sin preocuparnos de transacciones y connection pooling provista por contenedor Los EJBs son componentes REUTILIZACIÓN Clara separación entre desarrollo, explotación y administración de una aplicación EJB El contenedor de EJBs gestiona transacciones, detalles de manejo de estado, multi-threading, connection pooling, seguridad y otros detalles de bajo nivel que el desarrollador no necesita conocer.
Puesto de EJB container en una aplicación de empresa Web Container Browser Web Application EJB Container Enterprise Bean Database
¿Cuando usar EJBs? n n n No siempre, para simples aplicaciones web con servlets y JSPs sobra Si necesitas alta disponibilidad y escalabilidad entonces usa EJBs JDO y Hibernate
Tecnologías usadas por EJBs n EJBs hacen uso: n n RMI-IIOP JNDI (Java Naming and Directory Interface) JMS (Java Messaging Service)
Categorías de EJBs n 3 tipos de Enterprise Java. Beans: n n n Entity Beans: representan datos de negocio y proveen acceso a datos a través de métodos Session Beans: modelan procesos de negocio que son accedidos de manera síncrona Message-driven Beans: modelan procesos de negocio que son accedidos de manera asíncrona, permiten el uso de JMS desde EJBs
Contenedores de EJBs n JBoss: http: //www. jboss. org n n Contenedor más popular en el mundo y open source Download: http: //prdownloads. sourceforge. net/jboss 3. 2. 3. zip? download Documentación: http: //www. jboss. org/modules/html/docs/jbossj 2 e e. pdf Otros muy conocidos y muy usados comercialmente: BEA Web. Logic, IBM Websphere o Oracle 10 g
Escribiendo tu primer EJB n n Seleccionar un contenedor de EJBs: vamos a usar jboss-4. 0. 1 sp 1 disponible en http: //www. jboss. org Pasos a seguir para desarrollar un EJB: n n n Escribir la clase del bean Escribir el descriptor de explotación (deployment descriptor) Crear un fichero de explotación Desarrollar el bean Escribir la aplicación cliente para probar el bean Ejemplo “EJB que suma dos números”
Escribiendo el bean Adder I n Crea un directorio con el nombre de la aplicación (adder) y crea un subdirectorio en él llamado es/deusto/ejb donde guardarás los ficheros Adder. Home. java, Adder. java y Adder. Bean. java y otro llamado META-INF donde guardaras un fichero de configuración XML. package es. deusto. ejb; import java. rmi. Remote. Exception; import javax. ejb. Create. Exception; import javax. ejb. EJBHome; public interface Adder. Home. Remote extends EJBHome { Adder. Remote create() throws Remote. Exception, Create. Exception; }
Escribiendo el bean Adder II package es. deusto. ejb; import javax. ejb. EJBObject; import java. rmi. Remote. Exception; public interface Adder. Remote extends EJBObject { public int add(int a, int b) throws Remote. Exception; }
Escribiendo el bean Adder III package es. deusto. ejb; import java. rmi. Remote. Exception; import javax. ejb. Session. Bean; import javax. ejb. Session. Context; public class Adder. Bean implements Session. Bean { public int add(int a, int b) { System. out. println("from Adder. Bean"); return (a + b); } public void ejb. Create() {} public void ejb. Remove() {} public void ejb. Activate() {} public void ejb. Passivate() {} public void set. Session. Context(Session. Context sc) {} }
Escribiendo el deployment descriptor: META-INF/ejb-jar. xml <? xml version="1. 0" encoding="UTF-8"? > <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc. //DTD Enterprise Java. Beans 2. 0//EN" "http: //java. sun. com/dtd/ejb -jar_2_0. dtd"> <ejb-jar> <description>Your first EJB application </description> <display-name>Adder Application</display-name> <enterprise-beans> <session> <ejb-name>Adder. EJB</ejb-name> <home>es. deusto. ejb. Adder. Home. Remote</home> <remote>es. deusto. ejb. Adder. Remote</remote> <ejb-class>es. deusto. ejb. Adder. Bean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Bean</transaction-type> </session> </enterprise-beans> </ejb-jar>
Creando un archivo (. jar) de explotación 1. 2. 3. 4. 5. cd <adder-dir>set CLASSPATH=<jbossdir>serverdefaultlibjboss-j 2 ee. jar; . adder-dir>javac esdeustoejb*. java adder-dir>jar cvf adder. jar es/deusto/ejb/* META-INF/ejb-jar. xml¡ adder-dir>copy adder. jar <jbossdir>serverdefaultdeploy
Explotación del EJB adder n Al copiar el fichero. jar a: <jboss-dir>serverdefaultdeploy, la siguiente información será visualizada si JBoss es arrancado: 18: 08: 57, 970 INFO [Main. Deployer] Starting deployment of package: file: %JBOSS_HOME%/server/default/deploy/adder. jar 18: 08: 58, 140 INFO [Ejb. Module] Creating 18: 08: 58, 160 INFO [Ejb. Module] Deploying Adder. EJB 18: 08: 58, 280 INFO [Ejb. Module] Created 18: 08: 58, 280 INFO [Ejb. Module] Starting 18: 08: 58, 341 INFO [Ejb. Module] Started 18: 08: 58, 341 INFO [Main. Deployer] Deployed package: file: /F: /Doc. Man/tools/jboss-3. 0. 6_tomcat 4. 1. 18/server/default/deploy/adder. jar 18: 08: 58, 341 INFO [Main. Deployer] Starting deployment of package: file: /F: /Doc. Man/tools/jboss-3. 0. 6_tomcat 4. 1. 18/server/default/deploy/ejb-management. jar 18: 08: 58, 451 INFO [Ejb. Module] Creating 18: 08: 58, 491 INFO [Ejb. Module] Deploying MEJB 18: 08: 58, 511 INFO [Ejb. Module] Created 18: 08: 58, 511 INFO [Ejb. Module] Starting 18: 08: 58, 531 INFO [Ejb. Module] Started …
EJBs en detalle n Un Enterprise Java Bean no consta de una sola clase sino que también tiene dos (realmente cuatro, si contamos que pueden ser locales y remotos) interfaces: n n n n Home. Remote/Home. Local interfaces Remote/Local interfaces Se puede implementar o bien una tupla Home. Remote/Remote, o Home. Local/Local o las 4 a la vez Los interfaces locales fueron añadidos en EJB 2. 0 La clase bean contiene la implementación de las reglas de negocio Las clases home y remote sirven como entrada a un bean de empresa desde un cliente Todo EJB se construye implementando o extendiendo clases del paquete javax. ejb.
El interfaz Home n n Lleva a cabo operaciones de ciclo de vida: crear, buscar y eliminar un EJB Debe extender la interfaz javax. ejb. EJBHome o javax. ejb. EJBLocal. Home n EJBHome deriva de java. rmi. Remote, es usado para identificar interfaces cuyos métodos pueden ser invocados desde una virtual machine no local
El interfaz Remote n n n Ofrece los métodos de negocio disponibles a clientes remotos Un interfaz remoto debe extender el interfaz javax. ejb. EJBObject, el cual a su vez también deriva de java. rmi. Remote La versión local de Remote deriva de javax. ejb. EJBLocal. Object
La clase del bean de empresa n n Provee la implementación de los métodos de ciclo de vida en la interfaz home así como los métodos de negocio en la interfaz remota Un EJB puede ser: n n Session bean que implementa javax. ejb. Session. Bean Entity bean que implementa javax. ejb. Entity. Bean Message-driven bean implementa javax. ejb. Message. Driven. Bean Un EJB debe ofrecer la implementación de los métodos callback invocados por el contenedor y definidos en javax. ejb. Session. Bean o javax. ejb. Entity. Bean (ejb. Create, ejb. Post. Create, ejb. Find, etc. )
Descriptor de explotación n n Especifica meta-información para el contenedor de EJB Contiene la siguiente información: n n n El nombre El tipo El nombre bean del EJB completo del home interface completo del remote interface completo de la clase que implementa el
Escribiendo aplicaciones cliente n Un cliente de un EJB no invoca métodos directamente en el bean n Solamente puede ver los interfaces home y remote Las aplicaciones cliente acceden al bean de empresa a través de JNDI Vamos a ver dos ejemplos de aplicación cliente: n n Clase simple llamada Bean. Client JSP que demuestra como usar un bean de empresa desde un servlet o JSP
Java Naming and Directory Interface (JNDI) I n Un servicio de nombre permite encontrar un objeto dado su nombre n n n DNS: nombres dominio direcciones IP EJB: nombre EJB referencia a EJB Para comunicarse con un EJB una aplicación cliente sólo necesita saber el nombre Un servicio de directorio extiende un servicio de nombres puesto que asocia atributos que describen el objeto El paquete javax. naming ofrece la funcionalidad de JNDI
Java Naming and Directory Interface (JNDI) II n javax. naming. Context Ofrece el método: public Object lookup (String name) throws javax. naming. Naming. Exception n n javax. naming. Initial. Context n Implementa la interfaz Context, nos permite desde un programa buscar un EJB
Obteniendo una referencia a un EJB I 1. 2. 3. 4. Crear un objeto java. util. Properties Añadir las propiedades (la clase que implementa la factoría del naming service y su localización) a java. util. Properties Construcción de un objeto javax. naming. Initial. Context Uso del método lookup de javax. naming. Context para obtener una referencia al home del bean, pasándole el nombre del bean
Obteniendo una referencia a un EJB II Properties properties = new Properties(); properties. put(Context. INITIAL_CONTEXT_FACTORY, "org. jnp. interfaces. Naming. Context. Factory"); properties. put(Context. PROVIDER_URL, "localhost: 1099"); try { // Get an initial context Initial. Context jndi. Context = new Initial. Context(properties); // Get a reference to the Bean Object ref = jndi. Context. lookup("Adder. EJB"); } catch (Naming. Exception e) { }
Creando una instancia de un bean n n Usando JNDI hemos obtenido una referencia al home interface del bean Para llamar al método create del home interface de Adder tenemos que hacer un downcast n Para conformar con RMI-IIOP en vez de (Adder. Home. Remote) se utiliza el método narrow de la clase javax. rmi. Portable. Remote. Object: Adder. Home. Remote home = (Adder. Home. Remote)Portable. Remote. Object. n arrow(ref, Adder. Home. Remote. class); n Una vez obtenido el objeto home podemos crearlo usando el método create: Adder. Remote adder = home. create(); n Finalmente podemos usar ese bean: int i = adder. add(2, 5);
Una clase cliente para Adder I package es. deusto. client; import import javax. naming. *; javax. rmi. Portable. Remote. Object; java. util. Properties; es. deusto. ejb. Adder. Remote; es. deusto. ejb. Adder. Home. Remote; public class Bean. Client { public static void main(String[] args) { // preparing properties for constructing an Initial. Context object Properties properties = new Properties(); properties. put(Context. INITIAL_CONTEXT_FACTORY, "org. jnp. interfaces. Naming. Context. Factory"); properties. put(Context. PROVIDER_URL, "localhost: 1099"); try { // Get an initial context Initial. Context jndi. Context = new Initial. Context(properties); System. out. println("Got context");
Una clase cliente para Adder II // Get a reference to the Bean Object ref = jndi. Context. lookup("Adder. EJB"); System. out. println("Got reference"); // Get a reference from this to the Bean's Home interface Adder. Home. Remote home = (Adder. Home. Remote) Portable. Remote. Object. narrow (ref, Adder. Home. Remote. class); // Create an Adder object from the Home interface Adder. Remote adder = home. create(); System. out. println ("2 + 5 = " + adder. add(2, 5)); } catch(Exception e) { System. out. println(e. to. String()); } } }
Compilando y ejecutando Bean. Client adder-dir>set CLASSPATH=<jbossdir>clientjbossall-client. jar; <jbossdir>clientlog 4 j. jar; . 2. adder-dir>javac esdeustoclientBean. Client. java 3. adder-dir>java es. deusto. client. Bean. Client 4. Respuesta: Got context Got reference 2 + 5 = 7 1.
Llamando a adder desde un JSP I <%@ page import="javax. naming. *"%> <%@ page import="javax. rmi. Portable. Remote. Object"%> <%@ page import="java. util. Properties"%> <%@ page import="es. deusto. ejb. Adder. Home"%> <% // preparing a Properties object for constructing // an initial context Properties properties = new Properties(); properties. put(Context. INITIAL_CONTEXT_FACTORY, "org. jnp. interfaces. Naming. Context. Factory"); properties. put(Context. PROVIDER_URL, "localhost: 1099"); try { // Get an initial context Initial. Context jndi. Context = new Initial. Context(properties); System. out. println("Got context");
Llamando a adder desde un JSP II // Get a reference to the Bean Object ref = jndi. Context. lookup("Adder"); System. out. println("Got reference"); // Get a reference from this to the Bean's Home interface Adder. Home home = (Adder. Home) Portable. Remote. Object. narrow (ref, Adder. Home. class); // Create an Adder object from the Home interface Adder adder = home. create(); out. println ("2 + 5 = " + adder. add(2, 5)); } catch(Exception e) { System. out. println(e. to. String()); } %>
Instalando adder. ear en Jboss I n Necesario crear un ‘enterprise archive’. ear que contiene: n n n Adder. war parte web de nuestra aplicación (servlets/jsps) Adder-ejb. jar parte de los EJBs Application. xml deployment descriptor que documenta la relación entre el. war y el. jar de esta aplicación
Instalando adder. ear en Jboss II <? xml version="1. 0" encoding="UTF-8"? > <!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc. //DTD J 2 EE Application 1. 2//EN' 'http: //java. sun. com/j 2 ee/dtds/application_1_2. dtd'> <application> <display-name>Adder</display-name> <description>This is the implementaion of the Adder application</description> <module> <ejb>adder-ejb. jar</ejb> </module> <web> <web-uri>adder. war</web-uri> <context-root>/</context-root> </web> </module> </application>
Instalando adder. ear en Jboss III n n adder-ejb. jar contiene todas las clases correspondientes a los EJBs + ejb -jar. xml (deployment descriptor de EJBs) adder. war contiene todos los JSPs y servlets + web. xml + clases clientes para comunicarse con EJBs
Instalando adder. ear en Jboss IV n Usamos magia de Ant para generar el fichero. ear
Session Beans n Es un bean de empresa que implementa lógica de negocio para sus clientes n n n Puede realizar operaciones Procesar ordenes Encriptar y desencriptar datos, Buscar datos en una base de datos, etc. Siguiendo patrón de diseño Fachada, los sesión beans son los componentes con los que se comunican servlets y JSPs Los beans de sesión viven tanto tiempo como el cliente que los usa n Incluso a veces más tiempo dado que el contenedor los coloca en un pool de beans
Stateful y Stateless Session Beans n Un stateful session bean está asociado con un cliente único n n n Retiene información específica a un cliente Mantiene “estado conversacional” Análogo a javax. servlet. http. Http. Session Passivation es el proceso a través del cual el estado conversacional de un stateful session bean es transferido a almacenamiento secundario Activation es el proceso opuesto Un stateless session bean NO es específico a un cliente n Son muy buenos para conseguir aplicaciones escalables, se guardan en un pool y son reutilizables por cualquier cliente
Escribiendo un Session Bean n Necesitamos las siguientes clases: n n El home interface El remote interface La clase sesión bean La razón de existencia de los interfaces home y remote es que el cliente no crea el bean directamente, y tampoco invoca el método de un sesión bean directamente.
Interfaz javax. ejb. Session. Bean n Los cuatro métodos que provee son: n public void ejb. Activate throws javax. ejb. EJBException, java. rmi. Remote. Exception n n public void ejb. Passivate() throws javax. ejb. EJBException, java. rmi. Remote. Exception n n El contenedor lo invoca cuando el bean se pasiva public void ejb. Remove() throws javax. ejb. EJBException, java. rmi. Remote. Exception; n n El contenedor invoca este método cuando activa un sessión bean que había sido ‘pasivado’ El contenedor invoca este método cuando el cliente invoca en Remote remove() o un time-out en el session object es alcanzado public void set. Session. Context(Session. Context context) throws javax. ejb. EJBException, java. rmi. Remote. Exception n El contenedor invoca este método después de que una instancia del bean se crea. Suele ser conveniente cachear este objeto en una variable del bean
El interfaz javax. ejb. Session. Context n Provee algunos métodos útiles para obtener información acerca de: el cliente realizando una invocación, el objeto home asociado, el entorno, etc: n get. EJBHome(), get. EJBLocal. Home(), get. Primary. Key(), is. Identical(), get. EJBLocal. Object(), get. EJBObject(), get. Message. Context()
Como guardar referencias a un stateful session bean desde un JSP n Usar objeto Http. Session session = request. get. Session(true), Session. set. Attribute(“cart”, cart. Bean); … Cart cart. Bean = (Cart)session. get. Attribute(“cart ”)
El deployment descriptor de un session bean n n Toda aplicación EJB debe tener un deployment descriptor, cuyo elemento raiz es <ejb-jar> y contiene un nodo llamado <enterprise-beans> del que cuelgan todos los beans declarados en un aplicación Cada session bean se registra debajo del elemento <session>, pudiendo contener los siguientes subelementos: n n n description, display-name, small-icon, large-icon ejb-name nombre dado al EJB home/local-home el nombre completo del home/local interfaz del bean remote/local el nombre completo del home/local interfaz del bean ejb-class el nombre completo de la clase implementando el bean transaction-type, env-entry, etc.
Deployment descriptor de la aplicación I <? xml version="1. 0" encoding="UTF-8"? > <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc. //DTD Enterprise Java. Beans 2. 0//EN" "http: //java. sun. com/dtd/ejb-jar_2_0. dtd"> <ejb-jar> <description>The Search Bean for Tassie Online Bookstore</description> <display-name>Search Bean</display-name> <enterprise-beans> <session> <ejb-name>Search</ejb-name> <home>es. deusto. bookstore. ejb. Search. Home. Remote</home> <remote>es. deusto. bookstore. ejb. Search. Remote</remote> <ejb-class>es. deusto. bookstore. ejb. Search. Bean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Bean</transaction-type> </session>
Deployment descriptor de la aplicación II <session> <ejb-name>Book. Details</ejb-name> <home>es. deusto. bookstore. ejb. Book. Details. Home. Remote</home> <remote>es. deusto. bookstore. ejb. Book. Details. Remote</remote> <ejb-class>es. deusto. bookstore. ejb. Book. Details. Bean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Bean</transaction-type> </session> <ejb-name>Cart</ejb-name> <home>es. deusto. bookstore. ejb. Cart. Home. Remote</home> <remote>es. deusto. bookstore. ejb. Cart. Remote</remote> <ejb-class>es. deusto. bookstore. ejb. Cart. Bean</ejb-class> <session-type>Stateful</session-type> <transaction-type>Bean</transaction-type> </session> </enterprise-beans> </ejb-jar>
Entity Beans I n n Es un componente de datos que persiste datos permanentemente a almacenamiento secundario, e. j. BD Contiene campos y métodos n n n Guarda datos en los campos Realiza operaciones sobre los campos con los métodos Vista de un registro en una tabla de una base de datos n Aísla el desarrollo de aplicaciones del acceso a BD
Entity Beans II n El contenedor de EJBs lleva a cabo la sincronización y otras tareas para mantener los datos n n Mientras que un session mean modela un proceso de negocio, un entity bean modela datos de negocio n n Además un entity bean sobrevive una caída del servidor Dos tipos: n n n Provee servicios que hacen mucho más sencillo manejar datos representados por entity beans Bean-managed persistence (BMP) Container-managed persistence (CMP) Los ficheros necesarios para construir un entity bean son: n n n Interfaz Remote/Local Interfaz Home. Remote/Home. Local La clase primaria La clase que implementa el entity bean El deployment descriptor
La interfaz remota/local n n Esta interfaz permite a un cliente acceder a la instancia de un bean de entidad Debe extender: n n n El cliente puede usar esta interfaz para hacer: n n javax. ejb. EJBObject para interfaces remotos javax. ejb. EJBLocal. Object para interfaces locales Obtener la interfaz home para el bean de entidad Eliminar el bean de entidad Obtener su clave primaria Ejemplo: public interface Product. Remote extends javax. ejb. EJBObject { double get. Price(int product. Id) throws java. rmi. Remote. Exception; int get. Category(int product. Id) throws java. rmi. Remote. Exception; }
La interfaz home I n Un cliente puede utilizar esta interfaz para: n n n Crear un bean de entidad Eliminarlo Buscar un bean de entidad Esta interfaz provee métodos de creación, eliminación y búsqueda Una interfaz home (javax. ejb. EJBHome o javax. ejb. EJBHome. Local) puede definir varios métodos create: public interface Product. Home. Remote extends javax. ejb. EJBHome { public Product. Remote create(int product. Id, String product. Name, String description, double price) throws java. rmi. Remote. Exception, javax. ejb. Create. Exception; }
La interfaz home II n Como un bean de entidad representa una pieza de datos, una de las operaciones más frecuentes es encontrar un bean de datos: n n Al menos el método find. By. Primary. Key debe definirse n n n Para eso se usan los métodos finder Otros métodos también pueden definirse El método find. All Si un método finder devuelve varios resultados el tipo de datos devuelto deberá ser java. util. Collection public interface Product. Home. Local extends javax. ejb. EJBLocal. Home { public Product. Local find. By. Primary. Key(Integer product. Id) throws javax. ejb. EJBException, javax. ejb. Finder. Exception; public Collection find. By. Product. Name(String product. Name) throws javax. ejb. EJBException, javax. ejb. Finder. Exception; public Collection find. All() throws Finder. Exception, EJBException; } n El método remove permite borrar una bean de entidad: void remove(Object primary. Key) throws jajvax. ejb. EJBException, javax. ejb. Remove. Exception;
El bean de entidad n n Debe implementar el interfaz javax. ejb. Entity. Bean Implementa los métodos de ciclo de vida definidos en la interfaz home n Y métodos callback llamados por el contenedor
La interfaz javax. ejb. Entity. Bean n Los métodos a definir son: n n n n public void ejb. Activate() invocado por el contenedor cuando la entidad es recuperada del pool pasa a asociarse con un objeto entidad específico public void ejb. Passivate() invocado por el contenedor cuando la asociación entre una instancia de un bean con su objeto entidad está a punto de romperse public void ejb. Load() invocado por el contenedor para sincronizar el estado del bean de los datos subyacentes public void ejb. Store() opuesto a ejb. Load public void ejb. Remove() invocado por el contenedor cuando una instancia de un bean está a punto de borrarse public void set. Entity. Context(Entity. Context context) invocado después de que la instancia del bean haya sido creada public void unset. Entity. Context() opuesto a set. Entity. Context Los métodos ejb. Store y ejb. Load permiten la pasivación y activación del estado de un bean de entidad a y desde una BD
Métodos Create/Find/remove en un bean de entidad I n n n Todo método create/find/remove en una interfaz home es transformado en un método ejb. Create/ejb. Find/ejb. Remove en la clase que implementa el bean Los métodos ejb. Create/ejb. Find devuelven un valor correspondiente a la clave primaria de un bean (o colección con claves primarias en métodos find) en vez de una referencia a la interfaz remota/local del bean como en la interfaz home Todos los métodos definidos en el interfaz remote o local tienen los mismos tipos de datos de retorno y listas de argumentos en la implementación del bean
Métodos Create/Find/remove en un bean de entidad II public class Product. Bean implements javax. ejb. Entity. Bean { public Product. PK ejb. Create(int product. Id, String product. Name, String description, double price) throws javax. ejb. EJBException, javax. ejb. Create. Exception { this. product. Id = new Integer(product. Id) this. product. Name = product. Name; this. description = description; this. price = price; return this. product. Id; } public Product. PK ejb. Find. By. Primary. Key(Product. PK primary. Key) throws EJBException, Finder. Exception {. . . } public void ejb. Remove() throws EJBException, Remove. Excepton {. . . } public double get. Price() { return this. price; }. . . }
La interfaz javax. ejb. Entity. Context n Define dos métodos: n javax. ejb. EJBObject get. EJBObject() throws Illegal. State. Exception n n Devuelve una refencia al objecto de empresa asociado con una instancia del bean de entidad Object get. Primary. Key() throws Illegal. State. Exception n Devuelve la clave primaria de un bean
Dos tipos de bean de entidad n Dependiendo del modo en que los datos son hechos persistentes encontramos: n Bean de entidad con bean-managed persistence (BMP) n n El programador tiene que escribir el código SQL necesario para guardar/recuperar estado bean Bean de entidad container-managed persistence (CMP) n La tarea de guardar/recuperar datos es llevada a cabo por el contenedor de EJBs n Instrucciones sobre cómo llevar a cabo esa tarea son dados al contenedor en el deployment descriptor
Escribiendo un bean de entidad BMP Crear un BMP que representa a un producto Pasos a seguir: n n 1. 2. Crear una tabla de una base de datos, con los campos Product. Id (int), Product. Name (string), Description (string) y Price (string) Products. BD Obtener un driver JDBC para tu base de datos. n 3. Configurar JBoss para usar tu base de datos preferida (lo haremos con el ejemplo en CMP) n 4. En nuestro caso usaremos el ODBC-JDBC bridge para acceder a Access Dado que usamos un driver que viene con la distribución estándar de J 2 SE, no es necesario ninguna configuración adicional Preparar la estructura de directorio adecuada para tu proyecto. n es. deusto. ejb en nuestro caso
La interfaz remota Product. Remote package es. deusto. ejb; import javax. ejb. EJBObject; import java. rmi. Remote. Exception; /* this is the remote interface for Product */ public interface Product. Remote extends EJBObject { public Integer get. Product. Id() throws Remote. Exception; public void set. Product. Id(Integer product. Id) throws Remote. Exception; public double get. Price() throws Remote. Exception; public void set. Price(double price) throws Remote. Exception; public String get. Product. Name() throws Remote. Exception; public void set. Product. Name(String product. Name) throws Remote. Exception; public String get. Description() throws Remote. Exception; public void set. Description(String desc) throws Remote. Exception; }
La interfaz home Product. Home. Remote package es. deusto. ejb; import java. rmi. Remote. Exception; import javax. ejb. Finder. Exception; import javax. ejb. Create. Exception; import javax. ejb. EJBHome; import java. util. Collection; public interface Product. Home. Remote extends EJBHome { public Product. Remote create(int product. Id, String product. Name, String description, double price) throws Remote. Exception, Create. Exception; public Product. Remote find. By. Primary. Key(Integer key) throws Remote. Exception, Finder. Exception; public Collection find. By. Name(String name) throws Remote. Exception, Finder. Exception; public Collection find. All() throws Remote. Exception, Finder. Exception; }
El bean de entidad Product. Bean I package es. deusto. ejb; import java. sql. *; import java. util. Properties; import java. util. Collection; import java. util. Vector; import java. rmi. Remote. Exception; import javax. ejb. Entity. Bean; import javax. ejb. Entity. Context; import javax. ejb. Create. Exception; import javax. ejb. Finder. Exception; import javax. ejb. Object. Not. Found. Exception; import javax. naming. Initial. Context; import javax. naming. Naming. Exception; public class Product. Bean implements Entity. Bean { Entity. Context context; Integer product. Id; String product. Name; String description; double price; Connection conn = null; public Integer get. Product. Id() { System. out. println("get. Product. Id: " + this. product. Id); return this. product. Id; } public void set. Product. Id(Integer id) { this. product. Id = id; }
El bean de entidad Product. Bean II public String get. Product. Name() { System. out. println("get. Product. Name " + this. product. Name); return this. product. Name; } public void set. Product. Name(String product. Name) { this. product. Name = product. Name; } public String get. Description() { System. out. println("get. Description: " + this. description); return this. description; } public void set. Description(String description) { this. description = description; } public double get. Price() { System. out. println("get. Price: " + this. price); return this. price; } public void set. Price(double price) { this. price = price; }
El bean de entidad Product. Bean III public Integer ejb. Create(int product. Id, String product. Name, String description, double price) throws Remote. Exception, Create. Exception { System. out. println("ejb. Create"); this. set. Product. Id(new Integer(product. Id)); this. set. Product. Name(product. Name); this. set. Description(description); this. set. Price(price); Connection con = null; Prepared. Statement ps = null; try { String sql = "INSERT INTO Products" + " (Product. Id, Product. Name, Description, Price)" + " VALUES" + " (? , ? , ? )"; con = get. Connection(); ps = con. prepare. Statement(sql); ps. set. Int(1, this. get. Product. Id(). int. Value()); ps. set. String(2, this. get. Product. Name()); ps. set. String(3, this. get. Description()); ps. set. Double(4, this. get. Price()); ps. execute. Update(); } catch (SQLException e) { e. print. Stack. Trace(); } finally { try { if (ps != null) ps. close(); if (con != null)con. close(); } catch (SQLException e) {} } return this. get. Product. Id(); }
El bean de entidad Product. Bean IV public void ejb. Post. Create(int product. Id, String product. Name, String description, double price) throws Remote. Exception, Create. Exception { System. out. println("ejb. Post. Create"); } public Integer ejb. Find. By. Primary. Key(Integer primary. Key) { throws Remote. Exception, Finder. Exception { System. out. println("ejb. Find. By. Primary. Key"); Connection con = null; Prepared. Statement ps = null; Result. Set rs = null; try { String sql = "SELECT Product. Name" + " FROM Products" + " WHERE Product. Id=? "; con = get. Connection(); ps = con. prepare. Statement(sql); ps. set. Int(1, primary. Key. int. Value()); rs = ps. execute. Query(); if (rs. next()) { rs. close(); ps. close(); con. close(); return primary. Key; } } catch (SQLException e) { e. print. Stack. Trace(); } finally { try { if (rs != null) rs. close(); if (ps != null) ps. close(); if (con != null) con. close(); } catch (SQLException e) {} } throw new Object. Not. Found. Exception(); }
El bean de entidad Product. Bean V public Collection ejb. Find. By. Name(String name) throws Remote. Exception, Finder. Exception { System. out. println("ejb. Find. By. Name"); Vector products = new Vector(); Connection con = null; Prepared. Statement ps = null; Result. Set rs = null; try { String sql = "SELECT Product. Id " + " FROM Products" + " WHERE Product. Name=? "; con = get. Connection(); ps = con. prepare. Statement(sql); ps. set. String(1, name); rs = ps. execute. Query(); while (rs. next()) { int product. Id = rs. get. Int(1); System. out. println("Añadiendo producto: " + product. Id); products. add. Element(new Integer(product. Id)); } } catch (SQLException e) { e. print. Stack. Trace(); } finally { try { if (rs != null) rs. close(); if (ps != null) ps. close(); if (con != null) con. close(); } catch (SQLException e) {} } return products; }
El bean de entidad Product. Bean VI public Collection ejb. Find. All() throws Remote. Exception, Finder. Exception { System. out. println("ejb. Find. All"); Vector products = new Vector(); Connection con = null; Prepared. Statement ps = null; Result. Set rs = null; try { String sql = "SELECT * " + " FROM Products"; con = get. Connection(); ps = con. prepare. Statement(sql); rs = ps. execute. Query(); while (rs. next()) { int product. Id = rs. get. Int(1); System. out. println("*****+Product id retrieved: " + product. Id); products. add. Element(new Integer(product. Id)); } } catch (SQLException e) { e. print. Stack. Trace(); } finally { try { if (rs != null) rs. close(); if (ps != null) ps. close(); if (con != null) con. close(); } catch (SQLException e) {} } return products; }
El bean de entidad Product. Bean VII public void ejb. Remove() throws Remote. Exception { System. out. println("ejb. Remove"); Connection con = null; Prepared. Statement ps = null; try { String sql = "DELETE FROM Products" + " WHERE Product. Id=? "; Integer key = (Integer) context. get. Primary. Key(); con = get. Connection(); ps = con. prepare. Statement(sql); ps. set. Int(1, key. int. Value()); ps. execute. Update(); } catch (SQLException e) { e. print. Stack. Trace(); } finally { try { if (ps != null) ps. close(); if (con != null) con. close(); } catch (SQLException e) {} } } public void ejb. Activate() {System. out. println("ejb. Activate"); } public void ejb. Passivate() {System. out. println("ejb. Passivate"); }
El bean de entidad Product. Bean VIII public void ejb. Load() { System. out. println("ejb. Load"); Connection con = null; Prepared. Statement ps = null; Result. Set rs = null; try { String sql = "SELECT Product. Name, Description, Price" +" FROM Products" + " WHERE Product. Id=? "; con = get. Connection(); ps = con. prepare. Statement(sql); Integer primary. Key = (Integer)context. get. Primary. Key (); ps. set. Int(1, primary. Key. int. Value()); rs = ps. execute. Query(); if (rs. next()) { this. product. Name = rs. get. String(1); System. out. println("Product name: " + this. product. Name); this. description = rs. get. String(2); System. out. println("Description: " + this. description); this. price = rs. get. Double(3); System. out. println("Price: " + this. price); } } catch (SQLException e) { e. print. Stack. Trace(); } finally { try { if (rs != null) rs. close(); if (ps != null) ps. close(); if (con != null) con. close(); } catch (SQLException e) {} } }
El bean de entidad Product. Bean IX public void ejb. Store() { System. out. println("ejb. Store"); Connection con = null; Prepared. Statement ps = null; try { String sql = "UPDATE Products" + " SET Product. Name=? , Description=? , Price=? " + " WHERE Product. Id=? "; Integer key = (Integer) context. get. Primary. Key(); con = get. Connection(); ps = con. prepare. Statement(sql); ps. set. String(1, this. product. Name); ps. set. String(2, this. description); ps. set. Double(3, this. price); ps. set. Int(4, key. int. Value()); ps. execute. Update(); } catch (SQLException e) {e. print. Stack. Trace(); } finally { try { if (ps != null) ps. close(); if (con != null) con. close(); } catch (SQLException e) { } } } public void set. Entity. Context(Entity. Context context) { System. out. println("set. Entity. Context"); this. context = context; } public void unset. Entity. Context() { System. out. println("unset. Entity. Context"); context = null; }
El bean de entidad Product. Bean X public Connection get. Connection() { String db. Url = null; String user. Name = null; String password = null; Context initial. Context; Context environment; Connection connection = null; try { initial. Context = new Initial. Context(); environment = (Context) initial. Context. lookup("java: comp/env"); db. Url = (String) environment. lookup("db. Url"); user. Name = ((String) environment. lookup("db. User. Name")). trim(); password = ((String) environment. lookup("db. Password")). trim(); } catch (Naming. Exception e) { e. print. Stack. Trace(); } try { Class. for. Name("sun. jdbc. odbc. Jdbc. Odbc. Driver"); } catch (Class. Not. Found. Exception cnfe) { System. out. println("Class. Not. Found. Exception: " + cnfe. get. Message()); cnfe. print. Stack. Trace(System. out); } try { System. out. println("Obtaining connection: " + db. Url + ": " + user. Name + ": " + password); connection = Driver. Manager. get. Connection(db. Url, user. Name, password); System. out. println("Connection obtained: " + connection); } catch (SQLException e) { e. print. Stack. Trace(); } return connection; } }
El bean de entidad Product. Bean IX n La url, nombre de usuario, contraseña usados para conectarse a la base de datos son recuperados del deployment descriptor n Para ello usamos JNDI, y recuperamos los valores especificados en el fichero XML a través del objeto Initial. Context initial. Context = new Initial. Context() Environment = (Context) initial. Context. lookup(“java: comp/env”); db. Url = (String)environment. lookup(“db. Url”); n Hay tres métodos de búsqueda: find. By. Primary. Key, find. By. Name y find. All n n n ejb. Find. By. Primary. Key devuelve el objeto identificado por la clave primaria pasada o javax. ejb. Object. Not. Found. Exception ejb. Find. By. Name devuelve una colección de productos donde el nombre contiene la clave dada ejb. Find. All devuelve una lista con todos los productos
Deployment Descriptor <? xml version="1. 0" encoding="UTF-8"? > <ejb-jar> <description>Your first EJB application </description> <display-name>Products Application</display-name> <enterprise-beans> <entity> <ejb-name>BMPProduct</ejb-name> <home>es. deusto. ejb. Product. Home. Remote</home> <remote>es. deusto. ejb. Product. Remote</remote> <ejb-class>es. deusto. ejb. Product. Bean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>java. lang. Integer</prim-key-class> <reentrant>false</reentrant> <env-entry-name>db. Url</env-entry-name> <env-entry-type>java. lang. String</env-entry-type> <env-entry-value>jdbc: odbc: Products. BD</env-entry-value> </env-entry> <env-entry-name>db. User. Name</env-entry-name> <env-entry-type>java. lang. String</env-entry-type> <env-entry-value></env-entry-value> </env-entry> <env-entry-name>db. Password</env-entry-name> <env-entry-type>java. lang. String</env-entry-type> <env-entry-value></env-entry-value> </env-entry> </entity> </enterprise-beans> </ejb-jar>
Aplicación cliente I package es. deusto. client; import import javax. naming. *; javax. rmi. Portable. Remote. Object; java. util. Properties; java. util. Collection; java. util. Iterator; es. deusto. ejb. Product. Remote; es. deusto. ejb. Product. Home. Remote; public class BMPProduct. Client { public static void main(String[] args) { // preparing properties for constructing an Initial. Context object Properties properties = new Properties(); properties. put(Context. INITIAL_CONTEXT_FACTORY, "org. jnp. interfaces. Naming. Context. Factory"); properties. put(Context. PROVIDER_URL, "localhost: 1099"); try { // Get an initial context Initial. Context jndi. Context = new Initial. Context(properties); System. out. println("Got context"); // Get a reference to the Bean Object ref = jndi. Context. lookup("BMPProduct"); System. out. println("Got reference");
Aplicación cliente II // Get a reference from this to the Bean's Home interface Product. Home. Remote home = (Product. Home. Remote) Portable. Remote. Object. narrow(ref, Product. Home. Remote. class); // Create an Interest object from the Home interface home. create(11, "Franklin Spring Water", "400 ml", 2. 25); home. create(12, "Franklin Spring Water", "600 ml", 3. 25); home. create(13, "Choco Bar", "Chocolate Bar 200 g", 2. 95); home. create(14, "Timtim Biscuit", "Biscuit w. mint flavor, 300 g", 9. 25); Product. Remote product = home. create(15, "Supermie", "Instant Noodle", 1. 05); product. remove(); Collection products = home. find. By. Name("Franklin Spring Water"); //Collection products = home. find. All(); for (Iterator iter=products. iterator(); iter. has. Next(); ) { product = (Product. Remote) iter. next(); System. out. println("Id: " + product. get. Product. Id()); System. out. println("Product Name: " + product. get. Product. Name()); System. out. println("Description: " + product. get. Description()); System. out. println("Price: " + product. get. Price()); } } catch (Exception e) { System. out. println(e. to. String()); } } }
Compilación/ejecución de EJB Product n n Utilizar ant para generar un product. ear que se copiará a %JBOSS_HOME%serverdefaultdeploy Ejecutar cliente: n n n set CLASSPATH=%JBOSS_HOME%clientjbossallclient. jar; %PRODUCT_APP_HOME%buildproducts-ejb. jar; . java es. deusto. client. BMPProduct. Client Resultado: Got context Got reference Id: 11 Product Name: Franklin Spring Water Description: 400 ml Price: 2. 25 Id: 12 Product Name: Franklin Spring Water Description: 600 ml Price: 3. 25
Escribiendo un bean de entidad CMP n n Son más fáciles de escribir que BMPs Un bean de entidad CMP delega todas las tareas relacionadas con el acceso a una BD al contenedor n n No hay necesidad de implementar métodos ejb. Store y ejb. Load Incluso los métodos finder son implementados por el contenedor en base a los datos especificados en el deployment descriptor Menos trabajo de codificación pero más de declaración Es necesario configurar tu contenedor de EJBs con tu base de datos de preferencia n En este caso vamos a usar hsqldb
Hypersonic SQL (hsqldb) I n n Url: http: //hsqldb. sourceforge. net/ La fuente de datos configurada por defecto con Jboss es hsqldb n Ficheros de configuración de fuentes de datos en JBoss: n n n %JBOSS_HOME%serverdefaultdeployhsqldb-ds. xml %JBOSS_HOME%serverdefaultconfstandardjbosscmpjdbc. xml Para ver contenidos de DB ejecutar: n n java -cp hsqldb. jar org. hsqldb. util. Database. Manager Conectarse como: n n jdbc: hsqldb: %JBOSS_HOME%/server/default/data/hypersonic/local. DB Type: Database engine standalone
Hypersonic SQL (hsqldb) II
Pasos para desarrollar un CMP 1. 2. 3. 4. Declarar interfaz remota/local que define contrato entre los clientes y el EJB Declarar interfaz home remota o local para acceder a los métodos de ciclo de vida del EJB Implementar EJB Actualizar ejb-jar. xml with metadata para el EJB creado
CMPProduct. Remote package es. deusto. ejb; import javax. ejb. EJBObject; import java. rmi. Remote. Exception; /* this is the remote interface for Product */ public interface CMPProduct. Remote extends EJBObject { public void set. Product. Id(Integer product. Id) throws Remote. Exception; public Integer get. Product. Id() throws Remote. Exception; public void set. Price(double price) throws Remote. Exception; public double get. Price() throws Remote. Exception; public void set. Product. Name(String product. Name) throws Remote. Exception; public String get. Product. Name() throws Remote. Exception; public void set. Description(String description) throws Remote. Exception; public String get. Description() throws Remote. Exception; }
CMPProduct. Home. Remote package es. deusto. ejb; import java. rmi. Remote. Exception; import javax. ejb. Finder. Exception; import javax. ejb. Create. Exception; import javax. ejb. EJBHome; import java. util. Collection; public interface CMPProduct. Home. Remote extends EJBHome { public CMPProduct. Remote create(int product. Id, String product. Name, String description, double price) throws Remote. Exception, Create. Exception; public CMPProduct. Remote find. By. Primary. Key(Integer product. Id) throws Remote. Exception, Finder. Exception; public Collection find. By. Product. Name(String product. Name) throws Remote. Exception, Finder. Exception; public Collection find. All() throws Remote. Exception, Finder. Exception; }
CMPProduct. Bean I package es. deusto. ejb; import java. sql. *; import java. util. Properties; import java. util. Enumeration; import java. util. Vector; import java. rmi. Remote. Exception; import javax. ejb. Entity. Bean; import javax. ejb. Entity. Context; import javax. ejb. Create. Exception; import javax. ejb. Finder. Exception; import javax. ejb. Object. Not. Found. Exception; import javax. naming. Initial. Context; import javax. naming. Naming. Exception; public abstract class CMPProduct. Bean implements Entity. Bean { private Entity. Context context; public abstract void set. Product. Id(Integer product. Id); public abstract Integer get. Product. Id(); public abstract void set. Product. Name(String product. Name); public abstract String get. Product. Name(); public abstract void set. Description(String description); public abstract String get. Description(); public abstract void set. Price(double price); public abstract double get. Price();
CMPProduct. Bean II public Integer ejb. Create(int product. Id, String product. Name, String description, double price) throws Remote. Exception, Create. Exception { System. out. println("ejb. Create"); this. set. Product. Id(new Integer(product. Id)); this. set. Product. Name(product. Name); this. set. Description(description); this. set. Price(price); return null; } public void ejb. Post. Create(int product. Id, String product. Name, String description, double price) throws Remote. Exception, Create. Exception { System. out. println("ejb. Post. Create"); } public void ejb. Remove() throws Remote. Exception { System. out. println("ejb. Remove"); }
CMPProduct. Bean III public void ejb. Activate() { System. out. println("ejb. Activate"); } public void ejb. Passivate() { System. out. println("ejb. Passivate"); } public void ejb. Load() { System. out. println("ejb. Load"); } public void ejb. Store() { System. out. println("ejb. Store"); } public void set. Entity. Context(Entity. Context context) { System. out. println("set. Entity. Context"); this. context = context; } public void unset. Entity. Context() { System. out. println("unset. Entity. Context"); context = null; } }
ejb-jar. xml <? xml version="1. 0"? > <!DOCTYPE ejb-jar PUBLIC"-//Sun Microsystems, Inc. //DTD Enterprise Java. Beans 2. 0//EN“ "http: //java. sun. com/dtd/ejb-jar_2_0. dtd"> <ejb-jar> <enterprise-beans> <entity> <ejb-name>CMPProduct. EJB</ejb-name> <home>es. deusto. ejb. CMPProduct. Home. Remote</home> <remote>es. deusto. ejb. CMPProduct. Remote</remote> <ejb-class>es. deusto. ejb. CMPProduct. Bean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java. lang. Integer</prim-key-class> <reentrant>False</reentrant> <cmp-version>2. x</cmp-version> <abstract-schema-name>Product</abstract-schema-name> <cmp-field><field-name>product. Id</field-name></cmp-field> <cmp-field><field-name>product. Name</field-name></cmp-field> <cmp-field><field-name>price</field-name></cmp-field> <cmp-field><field-name>description</field-name></cmp-field> <primkey-field>product. Id</primkey-field> <security-identity><use-caller-identity/></security-identity>
Ejb-jar. xml II <query> <query-method> <method-name>find. By. Product. Name</method-name> <method-params> <method-param>java. lang. String</method-param> </method-params> </query-method> <ejb-ql> SELECT OBJECT( p ) FROM Product AS p WHERE p. product. Name = ? 1 </ejb-ql> </query> </entity> </enterprise-beans> n Para definir consultas a bases de datos complejas EJB 2. 0 define Enterprise Java. Beans Query Language (EJB QL), un lenguaje similar a SQL.
ejb-jar. xml III <assembly-descriptor> <security-role> <description> This role represents everyone who is allowed full access to the beans. </description> <role-name>everyone</role-name> </security-role> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>CMPProduct. EJB</ejb-name> <method-name>*</method-name> </method-permission> <container-transaction> <method> <ejb-name>CMPProduct. EJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar>
Configurando interfaces locales y remotos <session> <ejb-name>Sequence. Session. EJB</ejb-name> <home>com. pelikon. docman. util. ejb. keygenerator. Sequence. Session. Home. Remote </home> <remote>com. pelikon. docman. util. ejb. keygenerator. Sequence. Session. Remote </remote> <local-home>com. pelikon. docman. util. ejb. keygenerator. Sequence. Session. Home. Local </local-home> <local>com. pelikon. docman. util. ejb. keygenerator. Sequence. Session. Local </local> <ejb-class>com. pelikon. docman. util. ejb. keygenerator. Sequence. Session. Bean </ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <env-entry> <description/> <env-entry-name>retry. Count</env-entry-name> <env-entry-type>java. lang. Integer</env-entry-type> <env-entry-value>5</env-entry-value> </env-entry> <security-identity> <use-caller-identity/> </security-identity> </session>
Buscando local/remote homes public static synchronized Object get. Remote. Home(String jndi. Name, Class narrow. To) throws Naming. Exception { Object ref = remote. Homes. get(jndi. Name); if(ref == null) { ref = get. Context(). lookup(jndi. Name); ref = Portable. Remote. Object. narrow(ref, narrow. To); remote. Homes. put(jndi. Name, ref); } return ref; } public static synchronized Object get. Local. Home(String jndi. Name) throws Naming. Exception { Object ref = local. Homes. get(jndi. Name); if(ref == null) { ref = get. Context(). lookup(jndi. Name); local. Homes. put(jndi. Name, ref); } return ref; }
Sintáxis EJB QL I n Una query en EJB QL tiene la forma: n n select_clause from_clause [where_clause] select_clause n select [DISTINCT] {single_valued_path_expression | OBJECT(identification_variable)} n SELECT DISTINCT OBJECT(p) FROM Product AS p
Sintáxis EJB QL II n from_clause n FROM identification_variable_declaration [, identification_variable_declaration]* n identification_variable_declaration: : =collecti on_member_declaration|range_variable_declarati on n n collection_member_declaration: : = IN (collection_valued_path_expression) [AS] identifier range_variable_declaration: : =abstract_schema_name [AS] identifier n SELECT DISTINCT OBJECT(o) FROM Order o, IN(o. line. Items) l WHERE l. product. category=‘book’
Sintáxis EJB QL III n where_clause n n WHERE conditional_expression Donde la expresión condicional puede ser: n n n n n Literal (TRUE, FALSE, 9 E 2) Variable, debe haber sido declarada en la parte FROM Parámetros de entrada, de ? 1 en adelante Operadores: . , *, /, +, -, =, >, >=, <, <=, <>, NOT, AND, OR Expresiones BETWEEN: BETWEEN arithmetic-expr AND arithmetic-expr Expresiones IN Expresiones LIKE Comparaciones IS NULL Funciones: CONCAT, SUBSTRING, LOCATE, LENGTH, ABS, SQRT
Ejemplos EJB QL n SELECT OBJECT(u) FROM User AS u WHERE LCASE(u. user. Name) = LCASE(? 1) n SELECT OBJECT(p) FROM Project AS WHERE p. project. Category. id = ? 1 n En el ejb-jar. xml aparecerían las queries en el siguiente formato: <ejb-ql><![CDATA[SELECT OBJECT(d) FROM Document AS d WHERE d. title = ? 1 and d. description = ? 2 and d. creation. Date >= ? 3 and d. creation. Date <= ? 4 and d. last. Version = ? 5 and d. checked. Out. By. user. Name is NULL]]> </ejb-ql>
Message-driven Beans (MDB) EJB n n El módelo MDB permite a un EJB ser invocado asíncronamente para procesar las peticiones que llegan en la forma de mensajes JMS Para un cliente un MDB EJB es un consumidor asíncrono de mensajes n n n Similar a stateless session bean en que no tiene estado conversacional asociado No tienen interfaces home y remote como los session o entity beans Un cliente de un MDB EJB accede a él enviando mensajes a un destino JMS
Java Message Service (JMS) n Un servicio de mensajería es un servicio que provee comunicación entre aplicaciones o componentes software n En un servicio de mensajería el emisor y el receptor pueden estar desconectados n Emisor envía a un destino y el receptor recoge el mensaje del destino
La API de JMS n Las aplicaciones que usan JMS se denominan JMS n Los servicios de mensajería que manejan el ruteamiento y envío de mensajes son los JMS clients providers n n Un cliente que envía se denomina JMS producer Un cliente que recibe se denomina JMS receiver Dos características: n Asíncrono: un cliente JMS no tiene que requerir un mensaje para recibirlo n Robusto: un sistema JMS puede asegurar que un mensaje se recibe, uno y sólo una vez Objetivo JMS: integrarse con plataformas MOM (Message Oriented Middleware) IBM MQSeries, TIBCO RV
Cuándo usar JMS n n n Los clientes tienen que estar desacoplados unos de otros El intercambio de mensajes se debe permitir incluso cuando alguno de los clientes no está activo El módelo de negocio requiere que los componentes se envíen mensajes sin necesidad de tener que recibir una respuesta inmediatamente
Dominios de JMS n Publish/Subscribe (pub/sub) n n n Un cliente envía un mensaje a un tema y el mensaje es recibido por todos los clientes subscritos a ese tema Modelo one-to-many Point-to-point (PTP) n n n Un emisor envía un mensaje a una cola y el receptor extrae el mensaje de la cola cuando más le convenga La cola mantiene el mensaje hasta que éste sea recogido o expire Modelo one-to-one
Modelo de objetos de JMS I n Los objectos más importantes en JMS son: 1. Connection. Factory sirve para crear una conexión con un proveedor JMS n Tiene dos subinterfaces: n n Topic. Connection. Factory y Queue. Connection. Factory Para conseguir una conexión para un proveedor JMS punto-a-punto: Context jndi. Context = new Initial. Context(); Queue. Connection. Factory factory = (Queue. Connection. Factory) jndi. Context. lookup("Connection. Factory"); 2. Destination encapsula la dirección de un proveedor de JMS Topic en pub/sub n Queue en PTP Queue queue = (Queue) context. lookup(queue. Name); n 3. Connection representa una conexión activa a un proveedor JMS n Dos tipos de conexiones: n n n Queue. Connection Topic. Connection Después de crear una conexión se queda en modo ‘stop’ y es necesario arrancarla cuando se quieren enviar o recibir mensajes
Modelo de objetos de JMS II n Los objectos más importantes en JMS son: 4. Session contexto para producir y recibir mensajes n Dos subinterfaces: n n n Topic. Session Queue. Session Los métodos create. Topic. Session y create. Queue. Session tienen como parámetros: n n transacted indica si hay transacciones en la sesión acknowledge. Mode indica si el consumidor indicará la recepción de un mensaje: n AUTO_ACKNOWLEDGE n CLIENT_ACKNOWLEDGE
Modelo de objetos de JMS III n Los objectos más importantes en JMS son: 5. Message. Consumer objeto usado para recibir mensajes n Dos subtipos: n n 6. Queue. Receiver Topic. Subscriber Message los siguientes tipos de mensajes son soportados: n n n Text. Message Map. Message Bytes. Message Stream. Message Object. Message
MDB EJB API I n Un MDB debe implementar los interfaces: n javax. ejb. Message. Driven. Bean y javax. jms. Message. Listener n n La interfaz javax. ejb. Message. Driven. Context es también utilizada Message. Driven. Bean define los métodos: n n public void set. Message. Driven. Context (Message. Driven. Context context) throws EJBException asocia un contexto al MDB public void ejb. Remove() throws EJBException invocado por el contenedor al destruir un MDB
MDB EJB API II n Message. Driven. Context define los métodos: n n n get. Rollback. Only get. User. Transaction is. Caller. In. Role set. Rollback. Only get. Caller. Principal, get. EJBHome, get. EJBLocal. Home
Acceso a un MDB n Desde un cliente ha de accederse a un javax. jms. Queue: Context context = new Initial. Context(); Queue queue = (Queue)context. lookup( “queue/My. Queue”;
Escribiendo un MDB 1. 2. 3. 4. 5. Crear la clase del message driven bean El ejb-jar. xml Configurar Jboss para explotar un message-driven bean (jboss. xml) Compilar código Ejecutar clientes JMS
My. MDB. java I import import import javax. jms. JMSException; javax. jms. Message. Listener; javax. jms. Queue. Connection; javax. jms. Queue. Connection. Factory; javax. jms. Queue. Sender; javax. jms. Queue. Session; javax. jms. Text. Message; javax. naming. Initial. Context; javax. naming. Naming. Exception; public class My. MDB implements Message. Driven. Bean, Message. Listener { Message. Driven. Context context = null; Queue. Connection connection; Queue. Session session; Queue response. Queue;
My. MDB. java II public void ejb. Create() throws EJBException { System. out. println("ejb. Create"); try { Initial. Context init. Context = new Initial. Context(); String queue. Name = "queue/B"; this. response. Queue = (Queue) init. Context. lookup(queue. Name); Queue. Connection. Factory qcf = (Queue. Connection. Factory) init. Context. lookup("java: comp/env/jms/Queue. Factory"); connection = qcf. create. Queue. Connection(); session = connection. create. Queue. Session(false, Queue. Session. AUTO_ACKNOWLEDGE); connection. start(); } catch (Exception e) { throw new EJBException("Failed to initialize My. MDB", e); } }
My. MDB. java III public My. MDB() { System. out. println("Constructing My. MDB"); } public void set. Message. Driven. Context(Message. Driven. Context context) { this. context = context; System. out. println("set. Message. Driven. Context"); } public void ejb. Remove() { System. out. println("ejb. Remove"); context = null; try { if (session != null) session. close(); if (connection != null) connection. close(); } catch (JMSException e) { e. print. Stack. Trace(); } }
My. MDB. java III public void on. Message(Message msg) { System. out. println("on. Message"); try { Text. Message message = (Text. Message) msg; System. out. println("*******+message received: " + msg); //Queue queue = (Queue) msg. get. JMSReply. To(); //Queue. Sender sender = session. create. Sender(queue); Queue. Sender sender = session. create. Sender(this. response. Queue); Text. Message message 2 = session. create. Text. Message(message. get. Text()); sender. send(message 2); sender. close(); } catch (Exception e) { e. print. Stack. Trace(); } } }
ejb-jar. xml <? xml version="1. 0"? > <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc. //DTD Enterprise Java. Beans 2. 0//EN" "http: //java. sun. com/dtd/ejb-jar_2_0. dtd"> <ejb-jar> <enterprise-beans> <message-driven> <ejb-name>My. MDB</ejb-name> <ejb-class>es. deusto. ejb. My. MDB</ejb-class> <transaction-type>Container</transaction-type> <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode> <message-driven-destination> <destination-type>javax. jms. Queue</destination-type> </message-driven-destination> <resource-ref> <res-ref-name>jms/Queue. Factory</res-ref-name> <res-type>javax. jms. Queue. Connection. Factory</res-type> <res-auth>Container</res-auth> </resource-ref> </message-driven> </enterprise-beans> </ejb-jar>
jboss. xml <? xml version="1. 0" encoding="UTF-8"? > <jboss> <enterprise-beans> <message-driven> <ejb-name>My. MDB</ejb-name> <destination-jndi-name>queue/My. Queue</destination -jndi-name> <resource-ref> <res-ref-name>jms/Queue. Factory</res-ref-name> <jndi-name>java: /Jms. XA</jndi-name> </resource-ref> </message-driven> </enterprise-beans> </jboss>
jbossmq-mymdb-service. xml <? xml version="1. 0" encoding="UTF-8"? > <server> <mbean code="org. jboss. mq. server. jmx. Queue" name="jboss. mq. destination: service=Queue, name=My. Queue"> <depends optional-attributename="Destination. Manager">jboss. mq: service=Dest ination. Manager</depends> </mbean> </server>
Message. Sender. java I package es. deusto. client; import javax. jms. *; import javax. naming. *; public class Message. Sender { public static void main(String[] args) { Queue. Connection queue. Connection = null; try { Context context = new Initial. Context(); Queue. Connection. Factory queue. Connection. Factory = (Queue. Connection. Factory)context. lookup ("Connection. Factory"); String queue. Name = "queue/My. Queue"; Queue queue = (Queue) context. lookup(queue. Name); queue. Connection = queue. Connection. Factory. create. Queue. Connection(); Queue. Session queue. Session = queue. Connection. create. Queue. Session(false, Session. AUTO_ACKNOWLEDGE); Queue. Sender queue. Sender = queue. Session. create. Sender(queue); Text. Message message = queue. Session. create. Text. Message(); message. set. Text("This is a Text. Message"); queue. Sender. send(message); System. out. println("Message sent. ");
Message. Sender. java II } catch (Naming. Exception e) { e. print. Stack. Trace(); System. out. println("Naming Exception"); } catch (JMSException e) { System. out. println("JMS Exception"); } finally { if (queue. Connection != null) { try { queue. Connection. close(); } catch (JMSException e) {} } }
Message. Receiver. java package es. deusto. client; import javax. jms. *; import javax. naming. *; public class Message. Receiver { public static void main(String[] args) { Queue. Connection queue. Connection = null; try { Context context = new Initial. Context(); Queue. Connection. Factory queue. Connection. Factory = ( Queue. Connection. Factory) context. lookup("Connection. Factory"); String queue. Name = "queue/My. Queue"; Queue queue = (Queue) context. lookup(queue. Name); queue. Connection = queue. Connection. Factory. create. Queue. Connection(); Queue. Session queue. Session = queue. Connection. create. Queue. Session(false, Session. AUTO_ACKNOWLEDGE); Queue. Receiver queue. Receiver = queue. Session. create. Receiver(queue); queue. Connection. start(); System. out. println("Receiving message");
Message. Receiver. java Message message = queue. Receiver. receive(0); System. out. println("Message received: " + message); if (message != null) { if (message instanceof Text. Message) { Text. Message text. Message = (Text. Message) message; System. out. println(text. Message. get. Text()); } } } catch (Naming. Exception e) { System. out. println("Naming Exception"); } catch (JMSException e) { System. out. println("JMS Exception"); } finally { if (queue. Connection != null) { try { queue. Connection. close(); } catch (JMSException e) {} } }
Configurando un BMP para que use una fuente de datos I <? xml version="1. 0"? > <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc. //DTD Enterprise Java. Beans 2. 0//EN" "http: //java. sun. com/dtd/ejb-jar_2_0. dtd"> <ejb-jar> <enterprise-beans> <entity> <description> This bean represents a cruise ship. </description> <ejb-name>Ship. EJB</ejb-name> <home>com. titan. ship. Ship. Home. Remote</home> <remote>com. titan. ship. Ship. Remote</remote> <ejb-class>com. titan. ship. Ship. Bean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>java. lang. Integer</prim-key-class> <reentrant>False</reentrant> <security-identity><use-caller-identity/></security-identity> <resource-ref> <description>Data. Source for the Titan database</description> <res-ref-name>jdbc/titan. DB</res-ref-name> <res-type>javax. sql. Data. Source</res-type> <res-auth>Container</res-auth> </resource-ref> </entity> </enterprise-beans>
Configurando un BMP para que use una fuente de datos II <assembly-descriptor> <security-role> <description> This role represents everyone who is allowed full access to the Ship EJB. </description> <role-name>everyone</role-name> </security-role> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>Ship. EJB</ejb-name> <method-name>*</method-name> </method-permission> <container-transaction> <method> <ejb-name>Ship. EJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar>
Configurando un BMP para que use una fuente de datos III <? xml version="1. 0"? > <jboss> <container-configuration> <container-name>Standard BMP Entity. Bean</container-name> <commit-option>A</commit-option> <!--commit-option>C</commit-option--> </container-configurations> <enterprise-beans> <entity> <ejb-name>Ship. EJB</ejb-name> <jndi-name>Ship. Home. Remote</jndi-name> <resource-ref> <res-ref-name>jdbc/titan. DB</res-ref-name> <jndi-name>java: /Default. DS</jndi-name> </resource-ref> <configuration-name>Standard BMP Entity. Bean</configuration-name> </entity> </enterprise-beans> </jboss>
Configurando un BMP para que use una fuente de datos IV private Connection get. Connection () throws SQLException { try { Context jndi. Cntx = new Initial. Context (); Data. Source ds = (Data. Source)jndi. Cntx. lookup("java: comp/env/jdbc/titan. DB"); return ds. get. Connection (); } catch (Naming. Exception ne) { throw new EJBException (ne); } }
Extendiendo una query de EJB QL: jbosscmp-jdbc. xml I <? xml version="1. 0" encoding="UTF-8"? > <!DOCTYPE jbosscmp-jdbc PUBLIC "-//JBoss//DTD JBOSSCMP-JDBC 3. 0//EN" "http: //www. jboss. org/j 2 ee/dtd/jbosscmp-jdbc_3_0. dtd"> <jbosscmp-jdbc> <enterprise-beans> <entity> <ejb-name>Trig. UIEJB</ejb-name> <table-name>TRIGUI</table-name> <cmp-field> <field-name>id</field-name> <column-name>ID</column-name> </cmp-field> <field-name>name</field-name> <column-name>NAME</column-name> </cmp-field> <field-name>version</field-name> <column-name>VERSION</column-name> </cmp-field>
Extendiendo una query de EJB QL: jbosscmp-jdbc. xml II <cmp-field> <field-name>size</field-name> <column-name>SIZE_</column-name> </cmp-field> <field-name>num. Trig. Spaces</field-name> <column-name>NUM_TRIGSPACES</column-name> </cmp-field> <field-name>author</field-name> <column-name>AUTHOR</column-name> </cmp-field> <field-name>company</field-name> <column-name>COMPANY</column-name> </cmp-field> <field-name>description</field-name> <column-name>DESCRIPTION</column-name> </cmp-field>
Extendiendo una query de EJB QL: jbosscmp-jdbc. xml III <cmp-field> <field-name>price</field-name> <column-name>PRICE</column-name> </cmp-field> <field-name>timestamp</field-name> <column-name>TIMESTAMP</column-name> </cmp-field> <field-name>user. Name</field-name> <column-name>USERNAME</column-name> </cmp-field> <field-name>password</field-name> <column-name>PASSWORD</column-name> </cmp-field> <field-name>counter</field-name> <column-name>counter</column-name> </cmp-field>
Extendiendo una query de EJB QL: jbosscmp-jdbc. xml IV <query> <query-method> <method-name>ejb. Select. Ordered. Keys</method-name> <method-params/> </query-method> <jboss-ql><![CDATA[ SELECT t. id FROM Trig. UI t ORDER BY t. id DESC ]]> </jboss-ql> </query>
Extendiendo una query de EJB QL: jbosscmp-jdbc. xml V <query> <query-method> <method-name>find. By. Name. Ordered. By. Version. Desc</method-name> <method-params> <method-param>java. lang. String</method-param> </method-params> </query-method> <jboss-ql><![CDATA[ SELECT OBJECT(t) FROM Trig. UI t WHERE t. name = ? 1 ORDER BY t. version DESC ]]> </jboss-ql> </query> </entity> </enterprise-beans> </jbosscmp-jdbc>
Configurando Jboss con My. SQL I n Instalar My. SQL n n Download My. SQL de: http: //www. mysql. com/downloads/mysql-4. 0. html Para Windows elegir ‘Windows downloads, installer option’ Descomprimir el. zip resultante y hacer doble click en Setup. exe Ejecutar: %MYSQL_INSTALL_DIR%binwinmysqladmin n Hará que el servicio de mysql comience, y un icono con un semáforo aparecerá en la parte derecha barra de tareas
Configurando Jboss con My. SQL II n Añadir soporte para transacciones a My. SQL n Probar si existe soporte para transiciones: n n n Hacer click con el botón derecho del ratón en el semáforo y mostrar el administador de My. SQL Hacer click en pestaña myini setup Asegurarse que el contenido de ese fichero es el que sigue (reemplazando 'c: mysql' por la localización real de My. SQL en tu máquina): #This File was made using the Win. My. SQLAdmin 1. 4 Tool #12/11/2002 15: 06: 27 #Uncomment or Add only the keys that you know how works. #Read the My. SQL Manual for instructions [mysqld] basedir=c: mysql #bind-address=10. 0. 0. 194 datadir=c: mysqldata #language=c: mysqlshareyour language directory #slow query log#= #tmpdir#= #port=3306 #set-variable=key_buffer=16 M default-table-type=Inno. DB max_allowed_packet=12 M
Configurando Jboss con My. SQL III [Win. My. SQLadmin] Server=c: mysqlbinmysqld-max-nt. exe user=starbase 1 password=starbase 1 # Uncomment the following if you are using Innobase tables innodb_data_file_path = ibdata 1: 100 M: autoextend innodb_data_home_dir = c: mysqlibdata innodb_log_group_home_dir = c: mysqliblogs innodb_log_arch_dir = c: mysqliblogs set-variable = innodb_mirrored_log_groups=1 set-variable = innodb_log_files_in_group=3 set-variable = innodb_log_file_size=5 M set-variable = innodb_log_buffer_size=8 M innodb_flush_log_at_trx_commit=1 innodb_log_archive=0 set-variable = innodb_buffer_pool_size=16 M set-variable = innodb_additional_mem_pool_size=2 M set-variable = innodb_file_io_threads=4 set-variable = innodb_lock_wait_timeout=50
Configurando Jboss con My. SQL IV n n Asegurate que el botón de radio mysqld-maxnt es seleccionado Haz click en el botón 'Save Modification' button Para y rearranca el servicio My. SQL yendo a Setting-->Control Panel-->Administrative Tools-->Services http: //www. mhoehme. de/java/ejb. html#jboss -mysql provee detalles sobre cómo configurar JBoss y My. SQL
Configurando Jboss con My. SQL V n Obtener ejemplo de configuración de base de datos de: %JBOSS_HOME%docsexamplesjcamysql-ds. xml n n Copiarlo a %JBOSS_HOME%serverdefaultdeploy Modificar los elementos: <connection-url>jdbc: mysql: //localhost: 3306/deusto</connection -url> <driver-class>com. mysql. jdbc. Driver</driver-class> <user-name>deusto</user-name> <password>deusto</password> n Modificar el fichero %JBOSS_HOME%serverdefaultconfstandardjbosscmpjdbc. xml n n Substituir <datasource>java: /Default. DS</datasource> por <datasource>java: /My. Sql. DS</datasource> Copiar driver de My. SQL downloadsmysql 4. 1mysql-connectorjava-3. 1. 6 -bin. jar a %JBOSS_HOME%serverdefaultlib
Configurando Jboss con My. SQL V n n Para crear la base de datos deusto con username deusto y password deusto Guardar en fichero creardbdeusto. sql el siguiente código: CREATE DATABASE deusto; GRANT ALTER, SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON deusto. * TO deusto@'%' IDENTIFIED BY 'deusto'; GRANT ALTER, SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON deusto. * TO deusto@localhost IDENTIFIED BY 'deusto'; n Ejecutar el comando: mysql –uroot –p < creardbdeusto. sql
EJB 3. 0 n Futuro de EJB: n Orientado a simplificar el desarrollo de aplicaciones escalables n Mejorando aún más su rendimiento
Middlegen n http: //boss. bekk. no/boss/middlegen/
Referencias n La mejor fuente de documentación sobre Jboss es la Jboss Wiki: n n http: //www. jboss. org/wiki/Wiki. jsp http: //benmira. free. fr/en/j 2 ee/ejb. htm
- Slides: 132