Introducción a EJB 3
Es un API que forma parte del estándar de construcción de aplicaciones JEE.
Los EJB proporcionan un modelo de componentes distribuido estándar del lado del servidor. El objetivo de los EJB es dotar al programador de un modelo que le permita abstraerse de los problemas generales de una aplicación empresarial (concurrencia, transacciones, persistencia, seguridad, etc.) para centrarse en el desarrollo de la lógica de negocio en sí. El hecho de estar basado en componentes permite que éstos sean flexibles y sobre todo reutilizables. Mas información aqui: https://es.wikipedia.org/wiki/Enterprise_JavaBeans
- El cliente que puede ser un servlet, cliente local, remoto o inclusive otro EJB se comunica por medio de JNDI para poder comunicarse con un EJB ya sea local o remoto.
- EL EJB de session que puede implementar un EJB remoto, local o incluso ambos se encarga de realizar la lógica basada en la petición del cliente comunicándose con la base de datos por medio de JPA para luego devolver la respuesta al cliente.
- Estos componentes se deben encontrar dentro de un contenedor de aplicaciones empresariales. Que por ejemplo no puede ser un contenedor WEB como es el tomcat pero si un contenedor empresarial que por ejemplo puede ser JBOSS.
Esto es una descripción resumida. Durante la construcción del proyecto iremos explicando a detalle los tipos de EJB y forma de comunicación que podemos realizar para poder entenderlo a detalle.
Proyecto en EJB 3
Como indiqué anteriormente no explicaré como se realiza la instalación del Eclipse con su respectivo JDK, ni la Base de datos MySQL por lo que se asume que ya tenéis el entorno instalado. Lo unico que explicaré es la instalación del JBoss en eclipse.
Antes que nada crearemos en MySQL nuestra base de datos así como la tabla necesaria para poder trabajar con ella:
CREATE DATABASE BD_TUTORIAL; USE BD_TUTORIAL; grant all on BD_TUTORIAL.* to 'admin'@'localhost' identified by 'test'; CREATE TABLE COMPONENTE ( id INT PRIMARY KEY AUTO_INCREMENT, nombre VARCHAR(30), version VARCHAR(30), tipo VARCHAR(15), extension VARCHAR(30), creado TIMESTAMP DEFAULT NOW() ); CREATE TABLE usuario ( id INT PRIMARY KEY AUTO_INCREMENT, nombre VARCHAR(30), clave VARCHAR(30), creado TIMESTAMP DEFAULT NOW() ); insert into usuario (nombre, clave) values ('victor', 'elliott');
Luego obtenemos el servidor y nuestro conector a partir de las siguientes URL's:
Una vez hayamos descomprimido el archivo del jboss nos vamos a la carpeta:
\jboss-as-7.1.1.Final\modules\com y creamos la siguiente estructura de carpetas: mysql\main
al final quedará asi:
\jboss-as-7.1.1.Final\modules\com\mysql\main
dentro de main ponemos el conector: mysql-connector-java-5.1.25
Creamos el fichero xml: module.xml y dentro escribimos y guardamos:
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.1" name="com.mysql"> <resources> <resource-root path="mysql-connector-java-5.1.25.jar"/> <!-- Insert resources here --> </resources> <dependencies> <module name="javax.api"/> <module name="javax.transaction.api"/> <module name="javax.servlet.api" optional="true"/> </dependencies> </module>
Luego nos vamos a la carpeta: \jboss-as-7.1.1.Final\standalone\configuration y abrimos el archivo: standalone.xml y dentro del tag <datasources> copiamos lo siguiente:
<datasource jta="false" jndi-name="java:/mysql_ds" pool-name="mysql_ds" enabled="true" use-ccm="false"> <connection-url>jdbc:mysql://localhost:3306/BD_TUTORIAL</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <driver>mysql</driver> <security> <user-name>admin</user-name> <password>test</password> </security> <validation> <validate-on-match>false</validate-on-match> <background-validation>false</background-validation> </validation> <statement> <share-prepared-statements>false</share-prepared-statements> </statement> </datasource>
Y dentro del tag <drivers> lo siguiente:
<driver name="mysql" module="com.mysql"/>
Guardamos y ya tenemos nuestro Servidor JBoss configurado para poder conectarnos a la Base de datos.
Ahora instalamos el plugin de JBoss en nuestro eclipse: En el menú de eclipse Help -> Eclipse Marketplace en search buscamos JBoss e instalamos JBoss Tools (Kepler). Una vez instalado ya tenemos el JBoss plugin instalado en el Eclipse.
Ahora creamos un proyecto EJB: New -> EJB Project , le ponemos el nombre: ProyectoEJBTutorial-EJB
hacemos click en New Runtime, elegimos JBoss 7.1 Runtime -> Next Le ponemos un nombre adecuado yo le puse: JBoss 7.1 Runtime 1, en Home Directory elegimos la carpeta donde descomprimimos nuestro servidor JBoss en JRE puse jr7 y luego finish. En EJB Module elegimos la 3.1en Configuration lo dejamos a Default Configuration for JBoss 7.1 Runtime 1. Tendríais que tenerlo mas o menos asi:
Ahora nos vamos a la vista del servidor damos click en create new server, elegimos JBoss AS 7.1 en server name le pones el nombre que quieras yo lo deje el que salia por defecto: JBoss 7.1 Runtime 1 Server y en runtime enviroment elegimos el que acabamos de crear JBoss 7.1 Runtime 1. Quedaría asi:
Damos click a Finish y ya tenemos nuestro servidor configurado.
Ahora vamos a nuestro proyecto EJB damos click derecho en ejbModule -> New -> Other -> EJB -> Session Bean (EJB 3.x) -> Next y escribimos lo siguiente:
- Java package: com.victor.elliott.humala.logica
- Class name: ComponenteSessionBean
- State type: Stateless
- Hacemos check en Remote: com.victor.elliott.humala.negocio.ComponenteInterfazRemota
- Quitamos el check de No-interface View y quedaría asi:
Se nos crearan las siguientes clases:
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/logica/ComponenteSessionBean.java
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/negocio/ComponenteInterfazRemota.java
Ahora creamos nuestra entidad Componente:
ejbModule -> New -> Class
- Package: com.victor.elliott.humala.entidades
- Name: ComponenteEntity
Ahora creamos las clases para el Usuario:
Vamos a nuestro proyecto EJB damos click derecho en ejbModule -> New -> Other -> EJB -> Session Bean (EJB 3.x) -> Next y escribimos lo siguiente:
- Java package: com.victor.elliott.humala.logica
- Class name: UsuarioSessionBean
- State type: Stateless
- Hacemos check en Local: com.victor.elliott.humala.negocio.UsuarioInterfazLocal
- Quitamos el check de No-interface View y quedaría asi:
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/logica/UsuarioSessionBean.java
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/negocio/UsuarioInterfazLocal.java
Ahora creamos nuestra entidad Usuario:
ejbModule -> New -> Class
- Package: com.victor.elliott.humala.entidades
- Name: UsuarioEntity
ejbModule -> New -> Class
- Package: com.victor.elliott.humala.cliente
- Name: ClienteProbador
Por ultimo una clase que nos ayudará a comunicar el cliente con nuestros EJB's a través de JNDI
ejbModule -> New -> Class
- Package: com.victor.elliott.humala.clienteutility
- Name: JNDILookupClass
Luego insertamos el siguiente código dentro de esa clase:
package com.victor.elliott.humala.clienteutility; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; public class JNDILookupClass { private static Context initialContext; private static final String PKG_INTERFACES = "org.jboss.ejb.client.naming"; public static Context getInitialContext() throws NamingException { if (initialContext == null) { Properties properties = new Properties(); properties.put(Context.URL_PKG_PREFIXES, PKG_INTERFACES); properties.put("jboss.naming.client.ejb.context", true); initialContext = new InitialContext(properties); } return initialContext; } }
Aquí lo único que estamos haciendo es poner las propiedades necesarias a un contexto para poder realizar las llamadas JNDI correctamente.
Ahora que ya tenemos nuestras clases necesarias creadas crearemos nuestros ficheros de configuración:
Creamos dentro de la carpeta META-INF -> New -> Other -> XML -> XML File -> persistence.xml
y colocamos el siguiente código:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="ProyectoPersistencia" transaction-type="JTA"> <jta-data-source>mysql_ds</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" /> <property name="hibernate.hbm2ddl.auto" value="validate" /> </properties> </persistence-unit> </persistence>
Usamos el tipo de persistencia de Hibernate puesto que actualmente es el proveedor para persistencia en JBoss y la configuración no es nada complicada como pueden ver. Tener en cuenta que estamos usando la conección del driver que definimos algunos pasos atras en el JBoss "mysql_ds"
Ahora vamos a añadir a nuestro proyecto EJB las librerias necesarias para poder ejecutar el cliente y hacer algunas pruebas para esto vamos a crear un fichero de configuración para luego importarlo y evitar buscar los jar necesarios dentro de nuestro servidor de aplicaciones. para esto creamos un fichero en c:\ por ejemplo que se llame: clientelibrerias.userlibraries
y ponemos lo siguiente:
Donde reemplazamos <JBOSS_HOME> por la carpeta donde se ha descomprimido el servidor JBoss.
Ahora vamos a nuestro proyecto Click Derecho ejbModule -> Properties -> Java Build Path -> Add Library -> User Library -> Next -> User Libraries -> Import -> Browser -> Elegimos el clientelibrerias.userlibraries que hemos creado y ya estará listo nuestras librerías para el cliente. Finalmente lo seleccionamos para agregarlo al proyecto y Finish.
Luego para tener configurado correctamente al cliente creamos un archivo de propiedades que es necesario cuando creamos un cliente en JBoss:
Click derecho en el proyecto -> New -> Others -> JBoss Tools Web -> Properties File -> Next:
Y Finish. Dentro del archivo de propiedades ponemos lo siguiente:
Ahora ya tenemos todo el entorno EJB listo para empezar a desarrollar. EL entorno de trabajo quedará de la siguiente forma:
Primero que todo configuramos las entidades que serán mapeadas prácticamente como nuestras tablas creadas inicialmente en la Base de datos:
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/entidades/UsuarioEntity.java
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/entidades/ComponenteEntity.javaAhora vamos a añadir a nuestro proyecto EJB las librerias necesarias para poder ejecutar el cliente y hacer algunas pruebas para esto vamos a crear un fichero de configuración para luego importarlo y evitar buscar los jar necesarios dentro de nuestro servidor de aplicaciones. para esto creamos un fichero en c:\ por ejemplo que se llame: clientelibrerias.userlibraries
y ponemos lo siguiente:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <eclipse-userlibraries version="2"> <library name="EJBClientLibrerias" systemlibrary="false"> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/javax/transaction/api/main/jboss-transaction-api_1.1_spec-1.0.0.Final.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/javax/ejb/api/main/jboss-ejb-api_3.1_spec-1.0.1.Final.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/ejb-client/main/jboss-ejb-client-1.0.5.Final.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/marshalling/main/jboss-marshalling-1.3.11.GA.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/xnio/main/xnio-api-3.0.3.GA.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/remoting3/main/jboss-remoting-3.2.3.GA.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/logging/main/jboss-logging-3.1.0.GA.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/xnio/nio/main/xnio-nio-3.0.3.GA.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/sasl/main/jboss-sasl-1.0.0.Final.jar"/> <archive path="<JBOSS_HOME>/jboss-as-7.1.1.Final/modules/org/jboss/marshalling/river/main/jboss-marshalling-river-1.3.11.GA.jar"/> </library> </eclipse-userlibraries>
Donde reemplazamos <JBOSS_HOME> por la carpeta donde se ha descomprimido el servidor JBoss.
Ahora vamos a nuestro proyecto Click Derecho ejbModule -> Properties -> Java Build Path -> Add Library -> User Library -> Next -> User Libraries -> Import -> Browser -> Elegimos el clientelibrerias.userlibraries que hemos creado y ya estará listo nuestras librerías para el cliente. Finalmente lo seleccionamos para agregarlo al proyecto y Finish.
Luego para tener configurado correctamente al cliente creamos un archivo de propiedades que es necesario cuando creamos un cliente en JBoss:
Click derecho en el proyecto -> New -> Others -> JBoss Tools Web -> Properties File -> Next:
- Folder:* /ProyectoEJBTutorial-EJB/ejbModule
- Name:* jboss-ejb-client.properties
Y Finish. Dentro del archivo de propiedades ponemos lo siguiente:
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=default remote.connection.default.host=localhost remote.connection.default.port = 4447 remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
Ahora ya tenemos todo el entorno EJB listo para empezar a desarrollar. EL entorno de trabajo quedará de la siguiente forma:
Primero que todo configuramos las entidades que serán mapeadas prácticamente como nuestras tablas creadas inicialmente en la Base de datos:
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/entidades/UsuarioEntity.java
package com.victor.elliott.humala.entidades; import java.io.Serializable; import javax.persistence.*; @Entity @Table(name = "usuario") public class UsuarioEntity implements Serializable{ private static final long serialVersionUID = 1L; @Id @Column(name="id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; @Column(name="nombre") private String nombre; @Column(name="clave") private String clave; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getClave() { return clave; } public void setClave(String clave) { this.clave = clave; } public String toString(){ return "Usuario: "+this.nombre; } }
package com.victor.elliott.humala.entidades; import java.io.Serializable; import javax.persistence.*; @Entity @Table(name="componente") public class ComponenteEntity implements Serializable{ private static final long serialVersionUID = 1L; @Id @Column(name="id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; @Column(name="nombre") private String nombre; @Column(name="version") private String version; @Column(name="tipo") private String tipo; @Column(name="extension") private String extension; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getTipo() { return tipo; } public void setTipo(String tipo) { this.tipo = tipo; } public String getExtension() { return extension; } public void setExtension(String extension) { this.extension = extension; } public String toString(){ return "Componente: "+this.id+", "+this.nombre+", "+this.version+", "+this.extension; } }Si os percatáis ambas entidades implementan Serializable, esto es debido a que son objetos que viajarán via red para ser guardadas en la session Http cuando usemos el cliente servlet (posteriormente).
Ahora ya tenemos mapeadas nuestras tablas con estas dos entidades. Ahora desarrollemos las llamadas respectivas en los sessionBean:
Antes que nada indicar que la diferencia principal entre una interfaz local y remota es que una interfaz local necesita un cliente que tenga el mismo JVM (Java Virtual Machine) y una remota no. Por ejemplo el cliente que vamos a implementar en un momento es standalone lo cual quiere decir que no se va a ejecutar con nuestro proyecto. Este cliente vamos a ejecutar como una aplicación Java que se encuentra fuera del contexto de nuestro proyecto y por lo tanto tiene una diferente JVM por lo que las pruebas solo podremos hacerla con la interfaz remota. (Podéis probar con la local si deseáis pero os saldrá un error de compatibilidad)
Ahora desarrollemos nuestras interfaces y sus respectivas implementaciones.
Primero modifiquemos:
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/negocio/UsuarioInterfazLocal.java
package com.victor.elliott.humala.negocio; import javax.ejb.Local; import com.victor.elliott.humala.entidades.UsuarioEntity; @Local public interface UsuarioInterfazLocal { public boolean buscarUsuario(UsuarioEntity usuario); }
Aqui estamos indicando que la clase es una interfaz de tipo Local declarando un método a ser implementado.
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/logica/UsuarioSessionBean.java
package com.victor.elliott.humala.logica; import com.victor.elliott.humala.entidades.UsuarioEntity; import com.victor.elliott.humala.negocio.UsuarioInterfazLocal; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; /** * Session Bean implementation class UsuarioSessionBean */ @Stateless public class UsuarioSessionBean implements UsuarioInterfazLocal { @PersistenceContext private EntityManager entityManager; @Override public boolean buscarUsuario(UsuarioEntity usuario) { boolean existe=false; String queryString = "select u from UsuarioEntity u where u.nombre = :nombre and u.clave = :clave"; Query query = this.entityManager.createQuery(queryString); query.setParameter("nombre", usuario.getNombre()); query.setParameter("clave", usuario.getClave()); existe = !query.getResultList().isEmpty(); return existe; } }
- Aquí creamos nuestra clase UsuarioSessionBean que implementa UsuarioInterfazLocal.
- La clase está declarada como Stateless lo cual indica que el estado del SessionBean no estará dedicado a un cliente específico lo cual quiere decir que muchos cliente podrían interactuar con el y modificar su estado y/o datos. Si fuera Statefull indicaría que el estado del sessionBean solo estará reservado para el cliente que lo invoque por tanto no se perdería la data especifica para ese cliente. Y finalmente si se tratará de un Singleton su estado será compartido por toda la aplicación es decir que se instancia una sola vez al sessionBean al inicio de la aplicación y esta instancia perdurará hasta que se apague el servidor.
- Creamos nuestro EntityManager que es el encargado de interactuar con la Base de datos a través del Entity UsuarioEntity que definimos anteriormente.
- Implementamos el método buscarUsuario que me retornará true en caso que lo encuentre.
- Hay que percatarnos que la query se hace contra la entidad y no contra la tabla en si, me refiero que no hacemos "select u from usuario u" si no "select u from UsuarioEntity u".
Modificamos
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/negocio/ComponenteInterfazRemota.java
package com.victor.elliott.humala.negocio; import java.util.List; import javax.ejb.Remote; import com.victor.elliott.humala.entidades.ComponenteEntity; @Remote public interface ComponenteInterfazRemota { public void agregarComponente(ComponenteEntity componente); public List<ComponenteEntity> mostrarComponentes(); public void eliminarComponente(Integer id); public void actualizarComponente(ComponenteEntity componente); public ComponenteEntity mostrarComponente(Integer id); public boolean existeComponente(ComponenteEntity componente); }
Aqui estamos indicando que la clase es una interfaz de tipo Remota declarando seis métodos a ser implementados.
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/logica/ComponenteSessionBean.java
package com.victor.elliott.humala.logica; import java.util.List; import com.victor.elliott.humala.entidades.ComponenteEntity; import com.victor.elliott.humala.negocio.ComponenteInterfazRemota; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import javax.persistence.TypedQuery; /** * Session Bean implementation class ComponenteSessionBean */ @Stateless public class ComponenteSessionBean implements ComponenteInterfazRemota { @PersistenceContext private EntityManager entityManager; @Override public void agregarComponente(ComponenteEntity componente) { entityManager.persist(componente); } @Override public List<ComponenteEntity> mostrarComponentes() { String queryString = "select c from ComponenteEntity c"; TypedQuery<ComponenteEntity> query = this.entityManager.createQuery(queryString,ComponenteEntity.class); return query.getResultList(); } @Override public void eliminarComponente(Integer id) { ComponenteEntity entity=entityManager.find(ComponenteEntity.class, id); entityManager.remove(entity); } @Override public void actualizarComponente(ComponenteEntity componente) { entityManager.merge(componente); } @Override public ComponenteEntity mostrarComponente(Integer id) { return entityManager.find(ComponenteEntity.class, id); } public boolean existeComponente(ComponenteEntity componente) { boolean existe=false; String queryString = "select c from ComponenteEntity c where c.nombre = :nombre"; Query query = this.entityManager.createQuery(queryString); query.setParameter("nombre", componente.getNombre()); existe = !query.getResultList().isEmpty(); return existe; } }
- Aquí creamos nuestra clase ComponenteSessionBean que implementa ComponenteInterfazRemota.
- La clase está declarada como Stateless lo cual indica que el estado del SessionBean no estará dedicado a un cliente específico lo cual quiere decir que muchos cliente podrían interactuar con el y modificar su estado y/o datos. Si fuera Statefull indicaría que el estado del sessionBean solo estará reservado para el cliente que lo invoque por tanto no se perdería la data especifica para ese cliente. Y finalmente si se tratará de un Singleton su estado será compartido por toda la aplicación es decir que se instancia una sola vez al sessionBean al inicio de la aplicación y esta instancia perdurará hasta que se apague el servidor.
- Creamos nuestro EntityManager que es el encargado de interactuar con la Base de datos a través del Entity ComponenteEntity que definimos anteriormente.
- Implementamos los métodos agregarComponente, mostrarComponentes, eliminarComponente, actualizarComponente, mostrarComponente, existeComponente que me retornará true en caso que lo encuentre.
- Hay que percatarnos que la query se hace contra la entidad y no contra la tabla en si, me refiero que no hacemos "select c from componente c" si no "select c from ComponenteEntity c".
Ahora codificamos a nuestro cliente:
/ProyectoEJBTutorial-EJB/ejbModule/com/victor/elliott/humala/cliente/ClienteProbador.java
package com.victor.elliott.humala.cliente; import java.util.List; import javax.naming.Context; import javax.naming.NamingException; import com.victor.elliott.humala.clienteutility.JNDILookupClass; import com.victor.elliott.humala.entidades.ComponenteEntity; import com.victor.elliott.humala.logica.ComponenteSessionBean; import com.victor.elliott.humala.negocio.ComponenteInterfazRemota; public class ClienteProbador { public static void main(String[] args) { ComponenteInterfazRemota componenteIR=doLookup(); List<ComponenteEntity> componentesLista; ComponenteEntity componente=new ComponenteEntity(); boolean existe=false; componente.setNombre("Eclipse"); componente.setVersion("4.3.0"); componente.setTipo("IDE"); componente.setExtension("exe"); componenteIR.agregarComponente(componente);//id=1 componente.setNombre("Hibernate"); componente.setVersion("4.2.7"); componente.setTipo("Framework"); componente.setExtension("zip"); componenteIR.agregarComponente(componente);//id=2 componente.setNombre("EJB"); componente.setVersion("3.1.0"); componente.setTipo("Framework"); componente.setExtension("zip"); componenteIR.agregarComponente(componente);//id=3 componente.setNombre("MySQL"); componente.setVersion("5.0"); componente.setTipo("BD"); componente.setExtension("msi"); componenteIR.agregarComponente(componente);//id=4 componentesLista = componenteIR.mostrarComponentes(); System.out.println(componentesLista.toString()); componente.setId(1); componente.setNombre("Eclipse"); componente.setVersion("4.3.1"); componente.setTipo("IDE"); componente.setExtension("exe"); componenteIR.actualizarComponente(componente); System.out.println(componenteIR.mostrarComponente(1).toString()); componenteIR.eliminarComponente(4); componentesLista = componenteIR.mostrarComponentes(); System.out.println(componentesLista.toString()); componente.setNombre("Eclipse"); existe=componenteIR.existeComponente(componente); System.out.println("Componente Eclipse existe es:"+existe); componente.setNombre("MySQL"); existe=componenteIR.existeComponente(componente); System.out.println("Componente MySQL existe es:"+existe); } private static ComponenteInterfazRemota doLookup() { Context context = null; ComponenteInterfazRemota cIR = null; try { // 1. Obtaining Context context = JNDILookupClass.getInitialContext(); // 2. Generate JNDI Lookup name String lookupName = getLookupName(); // 3. Lookup and cast System.out.println("EL lookupName es: "+lookupName); cIR = (ComponenteInterfazRemota) context.lookup(lookupName); } catch (NamingException e) { e.printStackTrace(); } return cIR; } private static String getLookupName() { /*The app name is the EAR name of the deployed EJB without .ear suffix. Since we haven't deployed the application as a .ear, the app name for us will be an empty string */ String appName = ""; /* The module name is the JAR name of the deployed EJB without the .jar suffix.*/ String moduleName = "ProyectoEJBTutorial-EJB"; /* AS7 allows each deployment to have an (optional) distinct name. This can be an empty string if distinct name is not specified.*/ String distinctName = ""; // The EJB bean implementation class name String beanName = ComponenteSessionBean.class.getSimpleName(); // Fully qualified remote interface name final String interfaceName = ComponenteInterfazRemota.class.getName(); // Create a look up string name String name = "ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + interfaceName; return name; } }
Que realizará las siguientes tareas:
- Crear cuatro componentes en la base de datos con id auto-generables: 1,2,3 y 4 respectivamente.
- Mostrar los cuatro componentes mediante una lista.
- Modificar el primer componente id=1
- Mostrar el primer componente
- Eliminar el cuarto componente id=4
- Mostrar los 3 componentes de la base de datos que quedan.
- Pregunta si existe el componentes Eclipse
- Pregunta si exise el componente MySQL
- Creamos el método doLookup y getLookupName para invocar a nuestro EJB remotoa través de JNDI. Hay que percatarnos que doLookup hace la llamada JNDILookupClass para obtener el nombre del contexto con sus respectivas propiedades y getLookupName construye el nombre como será invocado el EJB remoto.
Ahora y tenemos lista nuestra aplicación EJB para poder ser testeada. Antes que nada asegurémonos que tenemos nuestra base de datos vacía para que no haya errores al invocar los métodos. Para esto ejecutemos los siguientes comandos en la Base de datos:
DELETE FROM COMPONENTE; ALTER TABLE COMPONENTE AUTO_INCREMENT=1;
Ahora Iniciemos el servidor Click derecho en JBoss Runtime Server -> Start
Una vez tengamos el servidor levantado hacemos click derecho -> Add and Remove
seleccionamos nuestra aplicación ProyectoEJBTutorial-EJB y presionamos ADD pafra agregarlo a nuestro servidor y poder deployarlo. Presionamos Finish y tendremos que ver lo siguiente en el log de la consola sin ningún error:
18:55:22,016 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named UsuarioSessionBean in deployment unit deployment "ProyectoEJBTutorial-EJB.jar" are as follows: java:global/ProyectoEJBTutorial-EJB/UsuarioSessionBean!com.victor.elliott.humala.negocio.UsuarioInterfazLocal java:app/ProyectoEJBTutorial-EJB/UsuarioSessionBean!com.victor.elliott.humala.negocio.UsuarioInterfazLocal java:module/UsuarioSessionBean!com.victor.elliott.humala.negocio.UsuarioInterfazLocal java:global/ProyectoEJBTutorial-EJB/UsuarioSessionBean java:app/ProyectoEJBTutorial-EJB/UsuarioSessionBean java:module/UsuarioSessionBean 18:55:22,024 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named ComponenteSessionBean in deployment unit deployment "ProyectoEJBTutorial-EJB.jar" are as follows: java:global/ProyectoEJBTutorial-EJB/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:app/ProyectoEJBTutorial-EJB/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:module/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:jboss/exported/ProyectoEJBTutorial-EJB/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:global/ProyectoEJBTutorial-EJB/ComponenteSessionBean java:app/ProyectoEJBTutorial-EJB/ComponenteSessionBean java:module/ComponenteSessionBean
Esto quiere decir que nuestros SessionBean; UsuarioSessionBean y ComponenteSessionBean se han inicializado correctamente. Tenemos que estar atentos a cualquier cambio que hagamos en los EJB tenemos que volver a deployar es decir hacer click derecho en el servidor -> Add and Remove seleccionamos el proyecto y presionamos Remove modificamos nuestro EJB y luego lo volvemos a añadir ADD para que se vuelva a deployar con los cambios realizados. Ojo no es necesario reiniciar ni parar el servidor para esto. Cuando hagamos un cambio a nuestro cliente ClienteProbador no es necesario volver a deployar.
Ahora que ya tenemos nuestro proyecto deployado nos disponemos a ejecutar al cliente:
Click derecho en ClienteProbador -> Run as -> Java Application
y veremos el siguiente log si es que no ocurre ningún error:
EL lookupName es: ejb:/ProyectoEJBTutorial-EJB//ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota [Componente: 1, Eclipse, 4.3.0, exe, Componente: 2, Hibernate, 4.2.7, zip, Componente: 3, EJB, 3.1.0, zip, Componente: 4, MySQL, 5.0, msi] Componente: 1, Eclipse, 4.3.1, exe [Componente: 1, Eclipse, 4.3.1, exe, Componente: 2, Hibernate, 4.2.7, zip, Componente: 3, EJB, 3.1.0, zip] Componente Eclipse existe es:true Componente MySQL existe es:false
Lo cual lo podréis corroborar haciendo un select en la base de datos sobre la tabla componente
Ahora comenzamos son la segunda parte del tutorial.
Creamos un proyecto WEB File -> New -> Dynamic Web Project
- Project Name: ProjectoEJBTutorial-WEB
- Target Runtime: JBoss 7.1 Runtime 1
- Dynamic Web Module version: 3.0
- Configuration: Default Configuration ....
Asociamos al Proyecto Web el Proyecto EJB que ya creamos en la primera parte.
Click Derecho al proyecto WEB -> Properties -> Java Build Path -> Projects -> Add Seleccionamos ProyectoEJBTutorial-EJB -> OK -> OK Y ya tenemos nuestro proyecto WEB asociado al EJB
Creamos nuestros servlets:
Click Derecho en el projecto -> New -> Other -> Web -> Servlet
- Source Folder: \ProjectoEJBTutorial-WEB\src
- Java Package: com.victor.elliott.humala.servlets
- Class Name: UsuarioServlet
- SuperClass: javax.servlet.http.HttpServlet
Click Derecho en el projecto -> New -> Other -> Web -> Servlet
- Source Folder: \ProjectoEJBTutorial-WEB\src
- Java Package: com.victor.elliott.humala.servlets
- Class Name: ComponenteServlet
- SuperClass: javax.servlet.http.HttpServlet
Ahora creamos las paginas que necesitamos:
Primero creamos la carpeta jsp dentro de WEB-INF
- index.jsp en /ProjectoEJBTutorial-WEB/WebContent
- login.jsp en /ProjectoEJBTutorial-WEB/WebContent/WEB-INF/jsp
- agregarComponentes.jsp en /ProjectoEJBTutorial-WEB/WebContent/WEB-INF/jsp
- mostrarComponentes.jsp en /ProjectoEJBTutorial-WEB/WebContent/WEB-INF/jsp
Finalmente creamos nuestro descriptor de despliegue
- web.xml en /ProjectoEJBTutorial-WEB/WebContent/WEB-INF
Al final la estructura del proyecto quedará de la siguiente forma:
Para evitar complejidad en nuestro descriptor de despliegue (web.xml) vamos usar la anotaciones que nos proporciona la versión 3.0 de los servlets y así solo ponemos nuestra página de inicio en el descriptor.
Primero que todo modificamos los servlet:
/ProyectoEJBTutorial-WEB/src/com/victor/elliott/humala/servlets/UsuarioServlet.java
package com.victor.elliott.humala.servlets; import java.io.IOException; import javax.ejb.EJB; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.victor.elliott.humala.entidades.UsuarioEntity; import com.victor.elliott.humala.negocio.UsuarioInterfazLocal; @WebServlet("/UsuarioServlet") public class UsuarioServlet extends HttpServlet { private static final long serialVersionUID = 1L; @EJB UsuarioInterfazLocal usuarioLocal; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String forward=""; forward="/jsp/login.jsp"; RequestDispatcher rd = request.getRequestDispatcher(forward); rd.forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String message=""; String forward=""; String nombre=request.getParameter("nombre"); String clave=request.getParameter("clave"); UsuarioEntity usuario=new UsuarioEntity(); usuario.setNombre(nombre); usuario.setClave(clave); if(datosVacios(usuario)){ message="El nombre y/o la clave no pueden estar vacios"; forward="/jsp/login.jsp"; } else{ if(!existeUsuario(usuario)){ message="El usuario no pertenece al sistema"; forward="/jsp/login.jsp"; } else{ message="Usuario Correcto: "+usuario.getNombre(); request.setAttribute("accion", "agregarComponente"); forward="/jsp/agregarComponentes.jsp"; request.getSession().setAttribute("usuario", usuario); } } request.setAttribute("message", message); RequestDispatcher rd = request.getRequestDispatcher(forward); rd.forward(request, response); } public boolean existeUsuario(UsuarioEntity usuario){ return usuarioLocal.buscarUsuario(usuario); } public boolean datosVacios(UsuarioEntity usuario){ return ("".equals(usuario.getNombre())||"".equals(usuario.getClave())); } }
- Todos los import son necesarios para codificar este servlet.
- Podemos percatarnos que estamos usando la anotación @WebServlet, esto es para indicar a quienes quieran invocar a este servlet que no es necesario que lo busque en el descriptor de despliegue.
- @EJB es una anotacion que nos permitirá obtener la referencia al EJB UsuarioInterfazLocal donde tenéis que recordar que hacemos referencia a la interfaz y no al implementador UsuarioSessionBean.
- El método doGet lo usarán todas aquellas llamadas que no se pase un formulario, para este caso especifico nos servirá solo para que el index.jsp nos redirija a nuestra pagina inicial login.jsp
- En el método doPost lo usarán todas aquellas llamadas donde se pase un formulario, para este caso nos servirá para pasar los datos del formulario del Login.
- Validamos los datos del Login con el método datosVacio() y una vez validado preguntamos si el usuario existe con el método existeUsuario()
- El método existeUsuario simplemente pregunta al EJB si el usuario que pasamos como parámetro existe en el sistema.
/ProyectoEJBTutorial-WEB/src/com/victor/elliott/humala/servlets/ComponenteServlet.java
package com.victor.elliott.humala.servlets; import java.io.IOException; import javax.ejb.EJB; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.victor.elliott.humala.entidades.ComponenteEntity; import com.victor.elliott.humala.entidades.UsuarioEntity; import com.victor.elliott.humala.negocio.ComponenteInterfazRemota; @WebServlet("/ComponenteServlet") public class ComponenteServlet extends HttpServlet { private static final long serialVersionUID = 1L; @EJB ComponenteInterfazRemota componenteRemoto; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { UsuarioEntity usuario=(UsuarioEntity)request.getSession().getAttribute("usuario"); String message=""; String forward=""; String accion=request.getParameter("accion"); if("verListaComponentes".equals(accion)){ request.setAttribute("listaComponentes", componenteRemoto.mostrarComponentes()); forward="/jsp/mostrarComponentes.jsp"; } else if("volverAgregarComponente".equals(accion)){ request.setAttribute("accion", "agregarComponente"); forward="/jsp/agregarComponentes.jsp"; } else if("volverModificarComponente".equals(accion)){ Integer id=Integer.parseInt(request.getParameter("id")); ComponenteEntity componenteModificar=componenteRemoto.mostrarComponente(id); request.setAttribute("nombre", componenteModificar.getNombre()); request.setAttribute("tipo", componenteModificar.getTipo()); request.setAttribute("version", componenteModificar.getVersion()); request.setAttribute("extension", componenteModificar.getExtension()); request.setAttribute("id", componenteModificar.getId()); request.setAttribute("accion", "modificarComponente"); forward="/jsp/agregarComponentes.jsp"; } else if("eliminarComponente".equals(accion)){ Integer id=Integer.parseInt(request.getParameter("id")); componenteRemoto.eliminarComponente(id); request.setAttribute("listaComponentes", componenteRemoto.mostrarComponentes()); forward="/jsp/mostrarComponentes.jsp"; } message="Usuario Correcto: "+usuario.getNombre(); request.setAttribute("message", message); RequestDispatcher rd = request.getRequestDispatcher(forward); rd.forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { UsuarioEntity usuario=(UsuarioEntity)request.getSession().getAttribute("usuario"); String message=""; String messageError=""; String forward=""; String nombre=request.getParameter("nombre"); String version=request.getParameter("version"); String tipo=request.getParameter("tipo"); String extension=request.getParameter("extension"); String accion=request.getParameter("accion"); ComponenteEntity componente=new ComponenteEntity(); componente.setNombre(nombre); componente.setVersion(version); componente.setTipo(tipo); componente.setExtension(extension); if("Agregar".equals(accion)){ accion="agregarComponente"; if(datosVacios(componente)){ messageError="Ningun dato del componente puede estar vacío"; forward="/jsp/agregarComponentes.jsp"; } else{ if(existeComponente(componente)){ messageError="El componente: "+componente.getNombre()+" ya existe en el sistema"; forward="/jsp/agregarComponentes.jsp"; } else{ componenteRemoto.agregarComponente(componente); request.setAttribute("listaComponentes", componenteRemoto.mostrarComponentes()); forward="/jsp/mostrarComponentes.jsp"; } } } else if("Modificar".equals(accion)){ accion="modificarComponente"; if(datosVacios(componente)){ messageError="Ningun dato del componente puede estar vacío"; forward="/jsp/agregarComponentes.jsp"; } else{ Integer id=Integer.parseInt(request.getParameter("id")); componente.setId(id); componenteRemoto.actualizarComponente(componente); request.setAttribute("listaComponentes", componenteRemoto.mostrarComponentes()); forward="/jsp/mostrarComponentes.jsp"; } } message="Usuario Correcto: "+usuario.getNombre(); request.setAttribute("accion", accion); request.setAttribute("message", message); request.setAttribute("messageError", messageError); RequestDispatcher rd = request.getRequestDispatcher(forward); rd.forward(request, response); } public boolean datosVacios(ComponenteEntity componente){ return ("".equals(componente.getNombre())||"".equals(componente.getVersion())||"".equals(componente.getTipo())||"".equals(componente.getExtension())); } public boolean existeComponente(ComponenteEntity componente){ return componenteRemoto.existeComponente(componente); } }
- Todos los import son necesarios para codificar este servlet.
- Podemos percatarnos que estamos usando la anotación @WebServlet, esto es para indicar a quienes quieran invocar a este servlet que no es necesario que lo busque en el descriptor de despliegue.
- @EJB es una anotacion que nos permitirá obtener la referencia al EJB ComponenteInterfazRemota donde tenéis que recordar que hacemos referencia a la interfaz y no al implementador ComponenteSessionBean.
- El método doGet lo usarán todas aquellas llamadas que no se pase un formulario, para este caso especifico nos servirá para las siguientes acciones: verListaComponentes, volverAgregarComponente, volverModificarComponente y eliminarComponente
- En el método doPost lo usarán todas aquellas llamadas donde se pase un formulario, para este caso nos servirá para pasar las siguientes acciones: Agregar y Modificar a un componente.
- Validamos en ambos casos los datos del Componente con el método datosVacio().
- El método existeComponente simplemente pregunta al EJB si el componente que pasamos como parámetro existe en el sistema.
/ProyectoEJBTutorial-WEB/WebContent/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>ProyectoEJBTutorial-WEB</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
Ahora que ya tenemos listo nuestro proyecto WEB si queréis podéis probar en deployarlo haciendo RUN con el servidor JBOSS pero darán algunos problemas de error con los EJB ya que las anotaciones @EJB solo sirven siempre y cuando se encuentren dentro de un proyecto de empresa (Enterprise Application Project). Para esto crearemos nuestro proyecto que contendra el proyecto EJB y el proyecto WEB.
File -> New -> Enterprise Application Project
- Project Name: ProyectoEJBTutorial-Enterprise
- Target runtime: JBoss 7.1 Runtime
- EAR version: 6.0
- Configuration: Default
Tal como podemos ver en la imagen:
Damos Next y en la siguiente pantalla seleccionamos nuestros proyectos ProyectoEJBTutorial-EJB y ProyectoEJBTutorial-WEB y damos click en Finish.
Ahora ya estamos listos para deployar nuestro proyecto. Vamos al servidor: click derecho -> Add and Remove -> Nos aseguramos que los proyectos EJB no WEB se encuentren agregados y agregamos el proyecto ProyectoEJBTutorial-Enterprise y Finish.
Si hemos hecho todo bien no debería salir ningun error en el log de la consola solo que se han generado correctamente nuestros EJB:
17:38:26,555 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named UsuarioSessionBean in deployment unit subdeployment "ProyectoEJBTutorial-EJB.jar" of deployment "ProyectoEJBTutorial-Enterprise.ear" are as follows: java:global/ProyectoEJBTutorial-Enterprise/ProyectoEJBTutorial-EJB/UsuarioSessionBean!com.victor.elliott.humala.negocio.UsuarioInterfazLocal java:app/ProyectoEJBTutorial-EJB/UsuarioSessionBean!com.victor.elliott.humala.negocio.UsuarioInterfazLocal java:module/UsuarioSessionBean!com.victor.elliott.humala.negocio.UsuarioInterfazLocal java:global/ProyectoEJBTutorial-Enterprise/ProyectoEJBTutorial-EJB/UsuarioSessionBean java:app/ProyectoEJBTutorial-EJB/UsuarioSessionBean java:module/UsuarioSessionBean 17:38:26,564 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named ComponenteSessionBean in deployment unit subdeployment "ProyectoEJBTutorial-EJB.jar" of deployment "ProyectoEJBTutorial-Enterprise.ear" are as follows: java:global/ProyectoEJBTutorial-Enterprise/ProyectoEJBTutorial-EJB/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:app/ProyectoEJBTutorial-EJB/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:module/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:jboss/exported/ProyectoEJBTutorial-Enterprise/ProyectoEJBTutorial-EJB/ComponenteSessionBean!com.victor.elliott.humala.negocio.ComponenteInterfazRemota java:global/ProyectoEJBTutorial-Enterprise/ProyectoEJBTutorial-EJB/ComponenteSessionBean java:app/ProyectoEJBTutorial-EJB/ComponenteSessionBean java:module/ComponenteSessionBean
Finalmente hacemos click derecho en ProyectoEJBTutorial-Enterprise -> Run As -> Run on server y veremos las siguientes pantallas:
Y una vez agregado varios componentes al sistema tendremos el resultado respectivo:
Finalmente podemos exportar nuestro proyecto como un EAR para independizarnos del eclipse y poderlo deployar a traves del ear. Click derecho sobre ProyectoEJBTutorial-Enterprise -> export -> EAR File le indicamos el destino y se habrá creado ProyectoEJBTutorial-Enterprise.ear
El código delos proyectos os lo podéis bajar de los siguientes links