domingo, julio 08, 2007

Creación de un EJB(I): Session Bean

Bueno, después de este pequeño traspiés con la nueva versión de J2EE, voy a ir contándoos lo que voy haciendo para crear un bean de sesión desde donde lo dejé. Recordemos que en el primer post hablamos sobre la tecnología J2EE y en el segundo post habíamos instalao nuestro servidor de aplicaciones de Sun. Por lo que me comentó Manolo, existe también otro servidor de aplicaciones llamado JBOSS que parece ser que es más eficiente, pero como yo ya había trabajao con una versión previa de éste, os voy a contar cómo se hace ésto. También he visto por ahí que hay un proyecto open source llamado GlashFish que creo que también es un servidor de aplicaciones y tiene buena pinta pero eso es arena de otro costal que ya urgaremos cuando proceda. Yo voy a lo mío.

Después de instalar el servidor de aplicaciones, en la nueva versión, necesitamos para desplegar nuestras aplicaciones un pack para Netbeans o un plugin para eclipse que se ofrecen junto con el servidor de aplicaciones en la página de Sun. Ésas eran las herramientas que se descargaban si en vez de pulsar en Download with JDK pulsábamos en Download with Tools. Como por entonces no tenía ni idea de que ya no existía la herramienta de despliegue deploytool, pues yo ya tengo mi servidor de aplicaciones instalao en mi máquina. No todo está perdido. Yo he escogido hacerlo con NetBeans como reto personal porque ya conocía Eclipse y tenía ganas de ver qué tal era NetBeans, pero vosotros podéis hacerlo con lo que elijáis.

Bien, para empezar nos vamos a la página de descarga de Sun y pinchamos en un link que hay a la derecha en un div azul titulado Related Resources y que se llama NetBeans. Ahí podemos descargarnos tanto el propio IDE NetBeans como el Enterprise Pack. Descargad agusto. Luego la instalación es bastante simple. Hacemos un chmod +x fichero_binario y luego un ./fichero_binario. Con ésto se nos abre un instalador gráfico bastante intuitivo al que la máxima información que tendremos que aportarle será el sitio donde queremos que lo instale cuando se trate del IDE o del sitio donde hemos instalao el IDE cuando se trate del Enterprise Pack. Habrá un momento de la instalación que nos preguntará si queremos instalar un servidor de aplicaciones o si queremos usar una instalación preexistente. Obviamente aquí usaremos el servidor que habíamos instalao en el post anterior y para ello tendremos que darle el nombre de usuario y la contraseña de administrador que nos creamos en su momento.

Aparte de ésto, lo demás no os creará problemas. Y bien, ahora tenemos nuestro servidor de aplicaciones perfectamente sincronizao con nuestro IDE NetBeans que hemos instalao para el desarrollo de nuestros beans y sólo nos queda desarrollar las aplicaciones.


Creación de un Bean de Sesión - El meollo.

Vamos a crearnos un Bean de Sesión que a partir de un día, mes y año de nacimiento calcule la edad que debe tener la persona. Además posteriormente y para ver la funcionalidad de ésto, nos vamos a crear una página JSP que haga uso de este Bean para recoger la información introducida por un usuario, mandársela al bean, recoger el resultado y mostrarlo por pantalla. Al lío pues.

Arrancamos NetBeans si no lo hemos hecho ya y pinchamos en Archivo -> Nuevo Proyecto -> Nueva Aplicación Empresarial. Rellenamos los campos necesarios. En nuestro caso le vamos a poner de nombre CalculadorEdad. Ponemos la carpeta donde queremos que se guarde y seleccionamos la versión de J2EE que queremos usar. En este primer post vamos a usar J2EE 1.4 que es la versión que yo había trabajado, pero a ver si más adelante os puedo escribir cómo hacerlo con J2EE 5 para que veáis la diferencia. Aseguráos antes de darle a "Terminar" de que marcáis las casillas de "Crear módulo EJB" y "Crear módulo de aplicación Web" y dejad sin marcar la de "Crear módulo de aplicación Web" de abajo que se refiere a un cliente de aplicación web que nosotros no necesitaremos, sólo los dos primeros. Debería quedar parecido a lo que sigue:

Pinchad en "Terminar".

Ahora creamos el nuevo Session Bean. Para ello le damos al botón derecho en CalculadorEdad-ejb -> Nuevo -> Bean de sesión.
Introducimos el nombre que será CalculadorEdad, ponemos el nombre de un paquete, que será por ejemplo "calculadoredad". Marcamos "sin estado" puesto que vamos a hacer un Stateless Session Bean, ponemos el tick en las dos interfaces, remota y local y pinchamos en Terminar. En realidad podríamos valernos sólo con la Remota, ya que la local es para optimizar accesos desde local pero vamos a ponerlo todo y así vemos todas las interfaces en pleno rendimiento. El resultado será que nos creará automáticamente las clases que necesitamos e implementará él sólo el código aburrido de los métodos de ciclo de vida y demás asuntos escabrosos. A nosotros nos tocará entonces implementar los métodos de negocio que son los específicos de nuestro bean. La intención es crear una aplicación que introduciendo una fecha de nacimiento calcule los años que tiene la persona dada. Así pues, nuestro método de negocio lo llamaremos edad y recibirá como parámetros 3 int representando el día, mes y año de nacimiento. Además le diremos que lanza una excepción que llamaremos PeroQueInventoEsEsteException y que extiende de Exception para indicar cuando alguien ha introducido una fecha no válida (ya se que aún no la hemos creado, lo haremos a continuación).

Si le damos al botón derecho en la clase CalculadorEdadBean , y seleccionamos métodos EJB -> Agregar método Business podremos añadir nuestro método de negocio que os describía arriba y el código se implementa sólo. Jurch, estos IDEs...dentro de poco no habrá que saber programar para programar :).

Justo después de haber puesto todo lo que arriba decíamos vamos a crearnos nuestra excepción especial. Para eso nos vamos dentro de CalculadorEdad-ejb al apartado de "Paquetes de orígen". Le damos al botón derecho encima y decimos Nuevo -> Clase java. Como nombre le damos PeroQueInventoEsEsteException y le decimos que la cree. A continuación la abrimos haciendo doble click en ella y añadimos a su declaración de clase el código "extends Exception" para que tenga las propiedades de una excepción normal y corriente. Luego en el constructor por defecto metemos como código "super();" y nos creamos un constructor con un String como parámetro que tenga por código "super(motivo);" siendo motivo el nombre del String del parámetro. Ya tenemos nuestra excepción.

Ahora vamos a implementar el código de nuestro método de negocio. Para ello nos vamos a la clase CalculadorEdadBean que es quien tiene que implementar el código de nuestro bean y picamos código. Mi implementación to perrillera del método que calcula la edad es la que os pongo aquí.

public int edad(int dia, int mes, int anho) throws PeroQueInventoEsEsteException{
//Obtenemos un objeto java.util.Date con la fecha actual y un objeto java.sql.Date con la fecha que ha introducido el usuario
java.sql.Date fecha = java.sql.Date.valueOf(new Integer(anho).toString()+"-"+new Integer(mes).toString()+"-"+new Integer(dia).toString());
hoy = new Date();
//Para compararlos obtengo el número de milisegundos desde "The Epoch"
long nacimiento = fecha.getTime();
long actual = hoy.getTime();

//Si se ha introducido una fecha futura...
if(nacimiento>actual){
throw new PeroQueInventoEsEsteException("¿Tu tas creído que somos unos pardillos o qué? ¿usease que aún no has nacío no?");
}

//Calculamos el tiempo en milisegundos que la persona lleva viviendo y hayamos su edad averiguando cuántos años son esos milisegundos. Hay un error obvio debido a los años bisiestos pero no lo contemplaremos aquí.
long tiempoDeVida = actual-nacimiento;
int edad = (int)(((tiempoDeVida/1000)/3600)/24)/365;

return edad;
}


Hago uso de java.util.Date (que está importada en el inicio de la clase con un simple "import java.util.Date") y de java.sql.Date por la facilidad de uno para crear objetos Date a partir de Strings y la facilidad del otro para crear objetos Date representando la fecha actual. Como ambos tienen un método getTime() que devuelve el número de milisegundos desde "The Epoch" que es como se conoce a las 00:00h del 1 de enero de 1970, mediante una simple resta puedo tener los milisegundos de vida de la persona que ha introducido su fecha de nacimiento. Paso los milisegundos a años y voilà, ya tenemos su edad. Está claro que no estoy contemplando los bisiestos y ésto sería un problema, pero como la propia implementación es bastante perrillera no me voy a centrar en que el código sea bonito o que cumpla bien con su cometido, sino en que veáis una implementación de un EJB. Además la aproximación es bastante cercana, y sólo fallará en días próximos a nuestro cumpleaños así que lo podemos considerar aceptable para un ejemplo :). De toas formas sí, ya se que es un código un tanto perrillero, pero weno, qué le vamos a hacer.

Lo único que nos queda por hacer con el Bean es darle un nombre de referencia en el servidor para que pueda ser encontrado por nuestra página web (que crearemos a continuación) cuando vaya a buscarlo. Ésto es fácil. Nos vamos al menú de la izquierda, donde está nuestro CalculadorEdad-ejb. Desplegamos el submenú "Archivos de configuración" y hacemos doble click encima de "sun-ejb-jar.xml". Ese fichero es en realidad un xml, pero nuestro querido IDE NetBeans nos lo mostrará como cajitas y menús tó wapos (si es que vivimos en una puta metáfora con las interfaces gráficas ;) ). Veréis que debajo de "Configuración Sun" tenéis un submenú llamado CalculadorEdadBean, pues pinchad en él, y veréis algo como ésto:


Rellenadlo como véis en la imágen, poniendo "ejb/CalculadorEdadBean" en el campo de "Nombre JNDI". Ya tenemos nuestro bean programado y reconocido por el servidor para que pueda ser llamado por otras aplicaciones, servlets, beans o JSPs.

Ahora nos vamos a crear una página JSP. En realidad esta página ya está creada. Se creó automáticamente cuando marcamos la casilla de "Crear módulo de aplicación Web" cuando creamos el proyecto. Para verla, id en el menú de la izquierda a "CalculadorEdad-war" y desplegad su submenú. De ahí nos vamos a páginas web y veréis que hay una que se llama "index.jsp". Ésta es la que nuestro servidor de aplicaciones mostrará cuando ejecutemos la aplicación y entremos en localhost:8080/CalculadorEdad-war. Pinchad dos veces sobre ella y se os abrirá a la derecha. Ahora la vamos a editar. Os voy a enseñar el código en dos trozos pa ir explicándooslo poco a poco. Primer cacho:


De lo que veis, llamaros la atención sobre unas pocas cosas:
  • No os olvidéis de importar las clases necesarias para el funcionamiento de vuestra página JSP, además de las clases de las interfaces del Bean que váis a usar.
  • Cread un contexto inicial como se ve en el código.
  • Fijáos en la forma de obtener una referencia a nuestro Bean. Primero se obtiene una referencia al interfaz de inicio o interfaz Home de nuestro Bean sobre la que llamaremos a su método create(). Lo hacemos con el método lookup(String) sobre nuestro contexto inicial y le preguntaremos por "java:comp/env/" + el nombre que le dimos a la referencia del EJB. ¿Os acordáis? Lo que sí recordaréis seguro es que en los Session Beans el bean nace y muere con el cliente, así que aquí no tendría mucho sentido obtener la referencia de otra forma. La creamos cada vez y punto.

Ahora pasamos al segundo cacho:


Aquí está la chicha de nuestra página. Quitando el código que es meramente HTML, lo más importante quizás sea lo que está a partir de la línea 47. Tened en cuenta que cuando llamemos a nuestra página JSP lo podremos hacer pasándole parámetros en la propia URL del tipo dia=4&mes=5&anho=1984. Ésta es la forma de funcionar de una petición "GET". Para recuperar esos parámetros en nuestro JSP tendremos siempre el objeto request siempre inicializado y que indica la petición, y si llamamos a su método getParameter("parametro"), éste nos podrá devolver o el valor del parámetro, o bien null, y debemos contemplar esa posibilidad. También contemplamos la posibilidad de que el usuario pinche sobre el botón sin haber introducido nada (se hace en las comprobaciones noseque.length()>0) por lo que se estará enviando una cadena vacía. Una vez capturados los parámetros con los que se ha llamado a nuestra página JSP mediante nuestro formulario que generaba peticiones "GET" sobre la propia página, los transformamos en el tipo de dato que necesitábamos, para ello apoyándonos en la clase java.lang.Integer y llamamos al método de negocio edad(int,int,int) que nos habíamos creado usando la referencia que obtuvimos en el primer cacho de código. Al realizar la llamada tenemos que encapsularla por un try-catch que recoja las excepciones que se puedan producir, que en nuestro caso será la excepción especial cachonda que nos habíamos creao. Y finalmente, si la excepción no se ha producido sólo tenemos que mostrar el mensaje con el resultado de la llamada a nuestro bean. ¿A que no es complicao?

Por último, vamos a crear una referencia al EJB en nuestro módulo de aplicación web. Cosa sencillica. Nos vamos al menú de la izquierda de NetBeans. Desplegamos el menú de "CalculadorEdad-war" y dentro de él el submenú de "Archivos de configuración". Hacemos doble click sobre web.xml. Aquí pasa como antes, que pese a que es un fichero xml, nuestro IDE nos lo muestra de forma bonica (si queréis ver su forma real podéis pinchar en el botón "XML" que os lo enseñará perfectamente). Ya trastearéis un poquillo por este fichero para descubrir que se pueden configurar muchísimas cosas, como por ejemplo el tiempo que dura una sesión o temas de seguridad y demás, pero lo que a nosotros nos importa lo conseguimos pinchando en el botón de "Referencias". Ahí desplegaremos "Referencias EJB" y pincharemos sobre "Agregar". Ahí se os mostrará algo como:


Rellenadlo como ahí pongo. Aquí fijáos que en el nombre de la referencia tiene que haber una coherencia con el nombre que configuramos antes en el servidor al EJB. Luego, dado que hemos metido las distintas clases de nuestro EJB en un paquete llamado calculadoredad, fijáos en que al indicar las interfaces de inicio y remota hemos puesto delante calculadoredad.*, es importante. Una vez que hayáis hecho ésto, pinchamos en guardar y tocad la madera más cercana porque se supone que ya lo tenemos todo listo para desplegar nuestra aplicación. Lo que sigue es fácil. Le damos al botón derecho encima de "CalculadorEdad", que es la raíz de nuestro menú de la izquierda y seleccionamos "Ejecutar Proyecto". Nos saldrán un chorro de letras por abajo, os pedirá el nombre de usuario y contraseña de administrador y parecerá que nuestra máquina esté intentando calcular todos los números de pi, pero cuando lleve un rato leeréis algo así como "GENERACIÓN CORRECTA" y sabréis que todo este tiempo habrá merecido la pena :). Que no os lleve a engaños, este mensaje aparece también cuando la mayoría de las cosas fallan. Depende del tipo de fallo que se produzca. Si las referencias las habéis puesto mal o el código no rula como debería de rular, el mensaje os aparecerá, pero cuando intentéis entrar en vuestra página lo que salga o cómo funcione la aplicación...eso ya será otro rollo.

Para comprobar que la cosa rula abrimos un navegador y metemos la dirección
http://localhost:8080/CalculadorEdad-war/
Si todo ha ido medianamente bien debería de mostrársenos algo como ésto...


Ahora introducimos un día de nacimiento, mes y año correctos, por ejemplo Día: 4, Mes: 5, Año: 1984, y pinchamos en "Submit". Nos debería salir algo como...

Es correcto. Pero, ¿qué pasa si introducimos una fecha en el futuro? ¿cómo manejará nuestra página aquella PeroQueInventoEsEsteException que nos inventamos? ¡Probémoslo! El resultado debería parecerse a éste...


Wapo ¿verdad?. En realidad, seguramente no queráis hacer una aplicación de un servidor de aplicaciones que maneje una página que te calcula tu edad con un cierto márgen de error de días según bisiestos...es mu probable que no, pero hacer un calculador de edad es lo mismo que hacer cualquier otra cosa, sólo cambiad el código y número de los métodos de negocio y fenómeno. Se que es un poco tedioso, pero también hay que tener en cuenta que en este ejemplo nosotros hemos hecho todo el trabajo. En un servidor de aplicaciones real seguramente no tuviéramos que hacerlo. Ahí es donde entra el reparto de roles que contempla este tipo de programación. El desarrollador no tiene por qué ser el mismo que el desplegador, que a su vez no hace falta ni que conozca a quien montó el servidor de aplicaciones. Por eso os enseñé en el anterior post cómo arrancar y parar el servidor desde línea de comandos, pues puede que os toque símplemente hacer ésto y no desarrollar nada encima. Una vez montado el sistema, ya es sólo cuestión de hacer correr distintas aplicaciones sobre él y si somos listos al cliente le cambiaremos muy poco la interfaz para no hacerle la picha un lío, como al fin y al cabo lo que va por debajo no le importa, sólo tenemos que añadir métodos de negocio y añadir nuevas funcionalidades en nuestra interfaz que nunca cambia :). Todo ventajas. Inconveniente: ésto es java, así que ya se sabe, el procesador echando humo y la memoria derrochándose. Pero weno, dejemos eso para otro momento.

El desarrollo de un EJB de sesión con estado es bastante similar al del sin estado que hemos desarrollado así que el siguiente post lo dedicaré a crear un EJB de entidad (con una bonita base de datos derby). Espero no haberos aburrido mucho y que ésto os pueda servir para introduciros en cómo hacer lo que ya sabéis que queréis hacer.

3 comentarios:

Manuel Cardenas Thorlund dijo...

Muy bueno, wapo, wapo. Podrías ahora intentar meterle un poco de Ajax al cálculo de la edad, no? Estaría guay separar completamente la lógica de negocio de la de presentación y cuando el usuario pulse sobre el botón de submit que lanzase una llamada asíncrona al ejb de sesión pasándole los parámetros y que devolviese el valor o la excepción. Una opción inicial podría ser crear una página con el html y otra con el código que tienes en esta (la página jsp). Al pulsar sobre el botón se llama a la página jsp que devuelve el valor y lo pintamos en pantalla.
Otra opción, más elegante, sería llamar directamente a un servicio web que calculase la edad y devolviese el valor o el error.

¿Qué te parece?

Nacho Foche dijo...

Me mola tu idea. Después de poner el post del Bean de Entidad, voy a intentar ser buen aprendiz y crear un post que haga lo que me cuentas. Seguramente te pida que le eches un ojo pa echarme una mano. ¿cómo lo ves?

Manuel Cardenas Thorlund dijo...

Ok. Como no sé muy bien manejarme con Java (aunque todos los lenguajes se parecen), voy a enterarme un poco de cómo se puede llamar a servicios web desde páginas jsp con Ajax.