1. Un ejemplo sencillo con EJB
Un EJB (Enterprise Java Bean) es un "componente de negocio", es decir, un programa
java que realiza alguna gestión compleja y que aguarda a que otro programa cliente lo
invoque. Para su funcionamiento, se requiere que se instale en un servidor, que es otro
software encargado de controlar el acceso al EJB, la seguridad, las transacciones y otras
tareas. Gracias a esta doble relación, los EJBs ofrecen un alto rendimiento y
disponibilidad a las aplicaciones distribuidas (en varias capas) que pueden ser o no de
tipo WEB. Como ejemplo del uso de la tecnología EJB, vamos a desarrollar la práctica
mínima que detalle el modus operandi para escribir, desplegar y probar una aplicación
J2EE que hace uso de un EJB.
Como se ha comentado, al utilizar EJB, se necesita un servidor de aplicaciones. Para la
práctica, se utilizó como herramienta el jdk (a partir de la versión 1.4 es suficiente), en
[1]; el IDE ECLIPSE 3.1, disponible en [2], (vale cualquier otro editor o entorno) y el
servidor de aplicaciones JBoss 4.0.2, [3], todo legalmente descargable en internet.
Para desarrollar y ejecutar una aplicación con EJB efectuaremos los siguientes pasos:
• Escribir el Bean
• Escribir el descriptor de despliegue
• Desplegar el Bean
• Escribir la aplicación cliente que lo utilice
El ejemplo construirá la aplicación con EJB (HolaMundoEJB), consistente en un único
enterprise java bean que muestre un saludo. Tras una compilación exitosa, escribiremos
su descriptor de despliegue y desplegaremos el bean en el servidor. Por último
crearemos otro programa cliente que llame a nuestro EJB.
Escritura del código fuente del Bean HolaMundo
Con la versión de EJB 2.0, se requieren tres programas Java, los cuales meteremos en
un "paquete", como por ejemplo, com.saludo. Posteriormente deberemos crear una
estructura de directorios que coincida con el nombre de ese paquete, es decir,
com/saludo. Allí es donde introduciremos los tres programas. Esta tarea podemos
hacerla manualmente o mejor con el IDE de Eclipse o similar, que convierte los
nombres de paquetes en directorios tal como los espera encontrar el compilador java.
2. Una vez aclarado dónde se meterán los tres archivos que componen el ejb, echaremos
un vistazo a los fuentes que vamos a introducir. Escribimos primero la "interfaz
remota", archivo que contiene los "métodos de negocio", o sea, lo que haga nuestro ejb,
en este caso el manido mensaje "Hola" acompañado del nombre que añadamos como
parámetro.
package com.saludo;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface HolaMundoRemote extends EJBObject{
public String saludo(String nombre) throws RemoteException;
}
A continuación escribimos la "interfaz Home", que corresponde a los métodos del ciclo
de vida de un bean. Nos limitamos a escribir el código ignorando posibles errores.
package com.saludo;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface HolaMundoHome extends EJBHome{
HolaMundoRemote create() throws RemoteException,
CreateException;
}
Por último escribimos el propio bean que, por fin, es toda una clase java. En ella estará
el código de los métodos antes expuestos en las interfaces previas Home y Remote.
Todo este pastiche se encarga de reunirlo y organizarlo el servidor de aplicaciones del
que pronto hablaremos. Si aún no se ha instalado, los fuentes previos daran errores en
algunas líneas por los "import" de clases que no existen normalmente en el jdk.
package com.saludo;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
3. import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HolaMundoBean implements SessionBean{
private static final long serialVersionUID = 1L;// Quita
warning de serialización del objeto
// Nuestro método "de negocio"
public String saludo(String nombre){
System.out.println("Un cliente me ha invocado");
return "Hola, " + nombre;
}
// Métodos del ciclo de vida del Bean (obligatorios)
public void ejbCreate(){}
public void ejbActivate() throws EJBException, RemoteException
{}
public void ejbPassivate() throws EJBException, RemoteException
{}
public void ejbRemove() throws EJBException, RemoteException {}
public void setSessionContext(SessionContext arg0) throws
EJBException, RemoteException {}
}
Bueno, pues esto es el código fuente de un ejb mínimo. Se trata ahora, de corregir todos
los posibles errores y, el problema a arreglar con más urgencia es añadir la librería de
los ejb que habréis podido observar en los import. En el caso de usar Eclipse y una vez
instalado el servidor JBoss, es más sencillo ya que basta con indicar a la aplicación que
añada el fichero jar correspondiente a los ejb. El jar que tendréis que añadir es el "jboss-
j2ee.jar" y se encuentra en la carpeta "serverdefaultlib" de JBoss. Por supuesto que hay
otras formas de resolver esto, como establecer la variable CLASSPATH para que
apunte allí o trasladar esa librería a nuestro directorio, etc.
Escribiendo el descriptor de despliegue
Una aplicación EJB requiere un descriptor de despliegue que describe cada bean de esa
aplicación. Este fichero descriptor tiene por nombre ejb-jar.xml y debe situarse en un
directorio llamado META-INF (exactamente) y que estará a la misma altura que la
carpeta com (paquete) que habíamos creado desde eclipse. El aspecto de los directorios
y su contenido debe quedar así:
El contenido del descriptor es el siguiente:
4. <?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD
Enterprise JavaBeans 2.0//EN" "http://java.sun.com/j2ee/dtd/ejb-
jar_2_0.dtd">
<ejb-jar>
<description>Ejemplo de EJB Simple</description>
<enterprise-beans>
<session>
<display-name>Bean HolaMundo</display-name>
<ejb-name>HolaMundo</ejb-name>
<home>com.saludo.HolaMundoHome</home>
<remote>com.saludo.HolaMundoRemote</remote>
<ejb-class>com.saludo.HolaMundoBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
Desplegando el Bean ejemplo
Lo que se llama desplegar, es instalar la aplicación y ello conlleva generar un fichero
java comprimido (.jar) que colocaremos en otro directorio concreto del servidor de
aplicaciones. En dicho fichero pondremos las clases (no son necesarias las fuentes que
yo si voy a añadir) e interfaces y el directorio META-INF con el ejb-jar.xml.
Para crear el jar, abrimos una consola de comandos, nos situamos en la carpeta del
proyecto, justo por encima de com y META-INF y allí escribimos:
jar cfv holamundo.jar com/saludo/* META-INF/ejb-jar.xml
Esto causa la creación del fichero comprimido "holamundo.jar". Las opciones cfv sirven
para crear el archivo indicando la ruta donde buscar y que muestre (verbose) todos los
mensajes oportunos durante el proceso. Además, al mencionar dos rutas (la de las clases
y la del ejb-jar), se añaden ambos contenidos. También se añadirá automáticamente el
fichero MANIFEST. Si has llegado hasta aquí, paciencia, ya queda poco. Lo próximo es
terminar el despliegue colocando el fichero "holamundo.jar" en un lugar concreto del
servidor de aplicaciones. Suponiendo que tienes instalado el servidor JBoss, que dicho
sea de paso, es de los contenedores para EJB más sencillos y populares, y no has
cambiado la configuración, debería existir un directorio llamado serverdefaultdeploy
que cuelga del directorio principal. Allí debes "soltar" el resultado "holamundo.jar".
Estamos a punto de comprobar el despliegue...
En este punto y si no ha habido ningún error, el despliegue se ha completado. Bastará
arrancar el servidor JBoss y ejecutar la utilidad run del directorio bin. Se abrirá una
consola donde se mostrarán gran cantidad de mensajes informativos. Debemos estar
atentos de que no salten excepciones java, que pueden ser provocadas por varias razones
pero muy probablemente por algún error en el fichero jar o en las clases o en la fase de
despliegue (a mí me costó tres intentos). En caso de error, hay que revisar todos los
mensajes que se exhiben en la consola de JBoss, como excepciones u otros orientativos
que suelen ser suficientes. Si todo marcha bien, en las últimas líneas tendrá que aparecer
que se ha desplegado nuestro bean "holamundo". En concreto, las últimas líneas que
muestra JBoss son algo así:
5. ...
21:13:52,953 INFO [EjbModule] Deploying HolaMundo
21:13:53,187 INFO [ProxyFactory] Bound EJB Home 'HolaMundo' to jndi
'HolaMundo'
21:13:53,187 INFO [EJBDeployer] Deployed:
file:/D:/jboss-4.0.5.GA/server/defaul
t/deploy/holamundo.jar
21:13:53,593 INFO [TomcatDeployer] deploy, ctxPath=/jmx-console,
warUrl=.../dep
loy/jmx-console.war/
21:13:54,046 INFO [Http11BaseProtocol] Starting Coyote HTTP/1.1 on
http-0.0.0.0
-8080
21:13:54,203 INFO [ChannelSocket] JK: ajp13 listening on /
0.0.0.0:8009
21:13:54,250 INFO [JkMain] Jk running ID=0 time=0/79 config=null
21:13:54,296 INFO [Server] JBoss (MX MicroKernel) [4.0.5.GA (build:
CVSTag=Bran
ch_4_0 date=200610162339)] Started in 24s:93ms
Escribiendo una aplicación cliente
Una vez desplegado nuestro Bean en el servidor, se hace necesario un programa cliente
que lo invoque. Hay que aclarar que este es un ejemplo mínimo y que los EJB se
utilizan para grandes aplicaciones distribuidas con mucho tráfico como el comercio
electrónico, la banca y similares. En cualquier caso es un buen modo de acercarnos a
esta nueva modalidad de programación y para probarla finalmente, necesitamos otro
programa que requiera sus servicios y probar que todo marcha según lo previsto.
Un bean enterprise es un componente del lado del servidor que espera llamadas de
clientes. Escribir un cliente EJB puede ser a veces una tarea compleja. Sin un cliente, no
podremos probar dichos beans. El conocer cómo los clientes acceden y llaman a
nuestros beans es una importante fase de una aplicación con EJB.
Para esta práctica creamos un nuevo programa separado del anterior donde crearemos
una clase que aquí he llamado BeanCliente. Para este nuevo proyecto, es importante
señalar que deberemos añadirle de nuevo las librerías necesarias para resolver todas las
referencias con J2EE y la relación con el proyecto anterior. Para arreglarlo,
vincularemos las librerías e incluiremos los correspondientes imports.
Para vincular las librerías al proyecto, en Eclipse haríamos: Project -> Properties
(pestaña Libraries) y ahí pulsamos el botón "Add external jar" para incluir los siguientes
ficheros: jboss-j2ee.jar, jboss-client.jar,jbossall-client.jar y nuestro holamundo.jar. Los
tres primeros se encuentran en las carpetas de jboss (normalmente la "client"), y el
último era el resultado del paso 3.
Tras estos pasos preparatorios ya podemos escribir el fuente que tiene el siguiente
aspecto:
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
6. import javax.rmi.PortableRemoteObject;
import com.saludo.HolaMundoHome;
import com.saludo.HolaMundoRemote;
public class BeanCliente {
public static void main(String[] args) {
// Preparación de propiedades para construir el
contexto inicial de JNDI
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory
");
prop.put(Context.PROVIDER_URL, "localhost:1099");
try{
// Intentamos obtener el contexto inicial antes
preparado
InitialContext jndiContext = new
InitialContext(prop);
System.out.println(" >> Obtenido un contexto
JNDI");
// Obtener una referencia al Bean
Object ref = jndiContext.lookup("HolaMundo");
// Nombre del ejb en el ejb-jar.xml
System.out.println(" >> Obtenida una referencia
al Bean "HolaMundo"");
// Obtener una referencia desde aquí a la
interfaz Home del Bean
HolaMundoHome home = (HolaMundoHome)
PortableRemoteObject.narrow(ref,
HolaMundoHome.class);
// Creación de la interfaz remota a partir de
la interfaz Home:
HolaMundoRemote h = home.create();
// Invocamos finalmente al Bean
System.out.println("Invocando el EJB: " +
h.saludo("Pedro"));
}catch(Exception ex){
System.out.println(ex.toString());
}
}
}
Pues esta era la última pieza del ejemplo. Si no nos hemos encontrado problemas (o
mejor, si ya los hemos resuelto) tenemos lista la infraestructura para poner en marcha la
práctica.
Si no está ya arrancado el servidor JBoss, lo iniciamos y estamos atentos a que el Bean
HolaMundo se ha desplegado satisfactoriamente. Hecho eso, sólo queda invocar el
cliente, ejecutando el programa sin más, como aplicación de consola. Se puede hacer
desde el propio Eclipse o desde una interfaz de comandos como prueba de
funcionamiento.
7. >> Obtenido un contexto JNDI
>> Obtenida una referencia al Bean "HolaMundo"
Invocando el EJB: Hola, Pedro
A partir de aquí ya podríamos avanzar con ejemplos más complejos, como un entorno
WEB con JSP, Servlets y EJB o un EJB de Entidad, que por cierto, lo tengo pendiente
probar.
Pero eso lo dejamos para el próximo día, esperando que todos los pasos anteriores os
hayan salido satisfactoriamente y os sirvan para empezar a comprender el complejo
mundo de las aplicaciones con J2EE.
Bueno, pues hasta aquí hemos llegado por hoy.
8. JNDI
Publicado por velorek en Junio 6, 2007
¿Qué es JNDI?
Es el sistema de nombrado en Java.
Un sistema de nombrado significa qué nombres están asociados con objetos y cómo se
localizan los objetos basándose en sus nombres.
Cuando usamos cualquier programa o sistema, estamos nombrando un objeto u otro.
Por ejemplo, cuando usamos un sistema de correo electrónico, debemos proporcionar el
nombre del recipiente al que queremos enviar el correo. Para acceder a un fichero en el
pc, debemos suministrar su nombre, entonces,un servicio de nombres nos permite
localizar un objeto dando su nombre.
Arquitectura
JNDI es un API genérico, de tal manera que es necesario integrarlo con otros sistemas
de nombres. Para ellos, la arquitectura JNDI incluye una interfaz de provision del
servicio (SPI), que sirve como intermediario con otros proveedores de servicios de
nombres.
Estos proveedores traducen las llamadas al API JNDI en llamadas reales sobre un servidor de
nombres o directorios.
Necesitamos las clases del proveedor de servicio que hayamos elegido. Desde la versión 1.4.1
el JDK cuenta con los siguientes proveedores de servicio de nombres:
* Lightweight Directory Access Protocol (LDAP)
* DNS
* Common Object Request Broker Architecture (CORBA)
* Remote Method Invocation (RMI)
Cuando usamos como proveedor de servicios el sistema de ficheros, no necesitamos
configurar el servidor, ya que el sistema de ficheros actúa como servidor.
Para bajar un proveedor de servicios pincha aquí.
9. Primeros Pasos con JNDI
Buscaremos un objeto,
Vamos a pedir al usuario un nombre de archivo o de directorio por teclado. La finalidad es que
podamos obtener el objeto asociado. El inicio es utilizar un stream de teclado para pedir el
nombre:
import java.util.*;
import javax.naming.*;
import java.io.*;
public class primeros_pasos01 {
public static void main(String[] args) {
BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in));
try {
/* Pedir por teclado un nombre
System.out.print( “Indique un nombre:”);
String name = entrada.readLine();
…
Lo siguiente es definir una Hashtable en la que indicamos que usaremos un sistema de
archivos. A continuación usamos el método lookup() para buscar el nombre. Este método
devuelve el objeto asociado o produce una excepción:
/* Indicamos que el contexto tiene como proveedor de servicio el sistema de archivos
(fsContext)*/
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
“com.sun.jndi.fscontext.RefFSContextFactory”);
Context ctx = new InitialContext(env);
/*Buscar el objeto*/
Object obj = ctx.lookup(name);
/ *Mostrar
System.out.println(“El nombre ” + name + ” esta ligado al objeto ” + obj.toString());
Evidentemente debemos manejar las excepciones:
catch ( NamingException e ) {
System.out.println( e.toString() ); }
catch ( IOException e ) {
System.out.println( e.toString() ); }
10. Si hemos indicado el directorio /Temporal obtenemos:
El nombre /Temporal esta ligado al objeto com.sun.jndi.fscontext.RefFSContext@15b7986
Si hemos indicado el archivo /Temporal/Presentacion.ppt obtenemos:
El nombre /Temporal/Presentacion.ppt esta ligado al objeto TemporalPresentacion.ppt
JNDI: acceso a propiedades de entorno (web.xml)
Ahora vamos a mostrar un servlet que usa de JNDI para acceder a propiedades definidas en el
archivo web.xml. Supongamos que tenemos una serie de propiedades definidas en web.xml
con la forma env-entry:
ejemplos/server
http://chunda.com
java.lang.String
El servlet en doGet() toma un contexto inicial vacio o “por defecto”, es decir, el conjunto de
nombres definido en web.xml. Observar que llamamos a InitialContext() sin argumentos; esto
implica pedir un contexto vacio de propiedades o, lo que es lo mismo, el contexto de nombres
definido en web.xml.
Con lookup() buscamos la entrada “ejemplos/server”:
try {
contexto = new InitialContext(); /*Equivalente: new InitialContext(null).*/
/* Busca propiedad de entorno*/
servidorHttp = (String) contexto.lookup(“java:comp/env/ejemplos/server”);}
catch(NamingException e) {
resultado = new String(e.toString()); } /*Ha fallado lookup()*/
Invocar al servlet de ejemplo.
Código fuente.
Enumerar los vínculos
Podemos enumerar todos los vinculos (bindings):
NamingEnumeration bindings = contexto.listBindings(“java:comp/env/ejemplos”);
while (bindings.hasMore()) {
Binding bd = (Binding) bindings.next();
out.println( ” ” + bd.getName() + “: ” + bd.getObject() + “, ” +
bd.getObject().getClass().getName() +”");