miércoles 7 de marzo de 2012

Libro Abierto y Gratuito de Java Persistence API


Quisiera compartir este WikiBook. Esta semana estuve consultándolo, leyendo algunas de las secciones y es en verdad una muy buena referencia para todo lo relacionado con el mundo de JPA.

Java Persistence API es la especificación de un ORM (Object-Relational Mapper) para JavaEE y JavaSE. Actualmente la versión vigente es la 2.0 (JSR 317) y se encuentra en elaboración, en etapa de Early Draft Review, la versión 2.1 (JSR 338). Entre las implementaciones más conocidas podemos encontrar a productos como Hibernate, de JBoss Red Hat, Open JPA, de Apache, TopLink, de Oracle, y la que es hoy la implementación de referencia: EclipseLink, de la Eclipse Foundation.

El libro no sólo trata las incumbencias básicas de la especificación (mapeos, entityManager y JPQL), también trata cuestiones avanzadas como mapeos a Stored Procedures, Views, Eventos, etc. A mí me resultó particularmente útil la sección de Locking and Concurrency, un tema que los programadores descuidamos bastante y a veces debemos tener en cuenta.

Lo bueno es que al ser un WikiBook cualquiera puede colaborar ampliándolo y actualizándolo.

jueves 21 de abril de 2011

Charla: Desarollo de Aplicaciones JavaEE 5 con Productos JBoss


Epidata Consulting los invita a participar de la charla "Desarrollo de aplicaciones JavaEE 5 con productos JBoss", que tendrá lugar el próximo miércoles 4 de mayo, de 18.30 a 20.30hs en la Facultad de Ingeniería de la UBA (FIUBA), sita en Paseo Colón 850.

La actividad tendrá como objetivo presentar la arquitectura de una aplicación JavaEE tipo y mostrar cuáles son algunos de los componentes, especificaciones y tecnologías que se suelen utilizar en cada capa. Puntualmente, al hablar de especificaciones se hará hincapié en JavaEE 5 y en cuanto a implementaciones el foco estará puesto en productos de desarrollo de JBoss. La charla será dictada por Adrián Paredes, arquitecto de software de Epidata Consulting.

La charla está dirigida a Desarrolladores Java y a Programadores interesados en esta tecnología, y será de índole conceptual e introductoria.

Orador

Ing. Adrián Paredes

Adrián Paredes es Ingeniero en Informática (FIUBA) y se desempeña como arquitecto de software y desarrollador en Epidata Consulting. Cuenta con 3 años de experiencia en proyectos JavaEE. Actualmente se encuentra trabajando en un proyecto de migración de aplicaciones internas de Swiss Medical, como partner de Red Hat, utilizando la SOA Platform de JBoss y Seam para las aplicaciones web.

Detalles

Miércoles 4 de mayo de 2011
18.30 a 20.30 hs
Facultad de Ingeniería de la UBA
Paseo Colón 850, Salón del consejo

Capacitación gratuita y abierta al público
Inscripciones: http://bit.ly/e4Bwhl

lunes 1 de febrero de 2010

JSON (JavaScript Object Notation)

JSON (JavaScript Object Notation) es un formato ligero para el intercambio de datos. Es un subconjunto de la notación literal de objetos de JavaScript. JSON es sencillo de escribir, sencillo de leer, sencillo de manipular, y en la mayoría de los casos es mucho más liviano que XML.

JSON maneja dos estructuras básicas:
  • Colección de pares nombre/valor, conocidas en varios lenguajes como Objetos, Registros, Estructuras, Diccionarios, Mapas, Arrays Asociativos
  • Lista ordenada de valores, conocidas en varios lenguajes como Arrays, Vectores, Listas, Secuencias
Para facilitar la comprensión, llamaremos a las primeras Mapas u Objetos, indistintamente, y a las segundas Listas o Arrays. En Java los Mapas se pueden corresponder con instancias concretas de la clase java.util.Map o con simples Java Beans (objetos Java sin métodos y con setters y getters para cada uno de los atributos). A la vez, las Listas se pueden asociar con instancias concretas de la clase java.util.List o meros Arrays que mantienen ordenados sus elementos.

En la notación JSON los Mapas comienzan con llave de apertura "{" y terminan con llave de cierre "}". Tanto los Mapas como las Listas son colecciones de elementos. En el primer caso los elementos son pares nombre/valor; en el segundo, solamente valores. Los dos elementos de un par (nombre y valor) están separados por dos puntos ":" y los nombres, o claves para seguir con la terminología de Mapas, siempre tienen que ser del tipo String.


Las Listas comienzan con corchete de apertura "[" y terminan con corchete de cierre "]" y sus elementos, como mencioné antes, están separados por comas ",".


Por último, los valores pueden ser del tipo string, number, object (Mapa), array (Lista), booleano (true/false) o null.


Veamos algunos ejemplos.

Definamos la clase Persona con sus atributos "nombre", "apellido", "fechaNacimiento", "soltero", "cantHijos":

public class Persona {

private String nombre;

private String apellido;

private Date fechaNacimiento;

private Boolean soltero;

private Integer cantHijos;

// ... Los setters y getters para cada atributo ...
}

Carguemos un objeto Persona:

   Persona persona = new Persona();
persona.setNombre("Adrian");
persona.setApellido("Paredes");
persona.setFechaNacimiento(parseDate("03/06/82", "dd/MM/yy"));
persona.setSoltero(false);
persona.setCantHijos(0);

Una forma equivalente de guardar este objeto sería en un HashMap:

   Map persona = new HashMap();
persona.put("nombre", "Adrian");
persona.put("apellido", "Paredes");
persona.put("fechaNacimiento", parseDate("03/06/82", "dd/MM/yy"));
persona.put("soltero", false);
persona.put("cantHijos", 0);

Dejemos de lado por un momento el campo "fechaNacimiento" y veamos cómo se representaría este Mapa en formato JSON, repito: sin la fecha de nacimiento:

{
"nombre" : "Adrian",
"apellido" : "Paredes",
"cantHijos" : 0,
"soltero" : false
}

Ahora agreguemos el atributo "fechaNacimiento":

{
"nombre" : "Adrian",
"apellido" : "Paredes",
"cantHijos" : 0,
"soltero" : false,
"fechaNacimiento" : {
"year" : 82
"month" : 5,
"day" : 4,
"date" : 3,
"hours" : 0,
"minutes" : 0,
"seconds" : 0,
"time" : 391921200000,
"timezoneOffset" : 180,
}
}

El atributo "fechaNacimiento" es del tipo java.util.Date, por lo tanto, lo que estamos viendo en el JSON es un Mapa dentro del Mapa principal que es Persona. Los campos "year", "month", "day", "date", "hours", "minutes", "seconds", "time" y "timezoneOffset" son los atributos de la clase java.util.Date.

Hay algunas bibliotecas de JSON que serializan los objetos java.util.Date de formas más compacta. Estas formas escapan al estándar de JSON. Por ejemplo, jabsorb, un broker que sirve como framework liviano para aplicaciones Ajax/Web 2.0, escribiría el JSON de Persona de esta manera:

{
"nombre" : "Adrian",
"apellido" : "Paredes",
"cantHijos" : 0,
"soltero" : false,
"fechaNacimiento" : {
"javaClass" : "java.util.Date",
"time" : 391921200000
}
}

Como jabsorb es un broker diseñado para transportar datos a tavés de una red, esta forma de serializar los objetos Date es beneficiosa, ya que suprime la información redundante.

Para hacer un poco más interesante esta introducción a JSON, vamos a agregarle dos atributos a la clase Persona:

   private Direccion direccion;

private List nombreHijos;

// Y agregamos los setters y getters...

La clase Direccion está definida de esta forma:

public class Direccion {

private String calle;

private Integer numero;

// ... Los setters y getters para cada atributo ...
}

Supongamos que la Persona "Adrián Paredes" esta vez sí tiene hijos y tiene tres: "Marco", "Polo" y "Lolita":

   Persona persona = new Persona();
persona.setNombre("Adrian");
persona.setApellido("Paredes");
persona.setFechaNacimiento(parseDate("03/06/82", "dd/MM/yy"));
persona.setSoltero(false);
persona.setCantHijos(3);

List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.setNombreHijos(nombres);

Direccion direccion = new Direccion();
direccion.setCalle("Av. Siempre Viva");
direccion.setNumero(2012);
persona.setDireccion(direccion);

Lo que en el lenguaje de Mapas y Listas sería:

   Map persona = new HashMap();
persona.put("nombre", "Adrian");
persona.put("apellido", "Paredes");
persona.put("fechaNacimiento", parseDate("03/06/82", "dd/MM/yy"));
persona.put("soltero", false);
persona.put("cantHijos", 3);

List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.put("nombreHijos", nombres);

Map direccion = new HashMap();
direccion.put("calle", "Av. Siempre Viva");
direccion.put("numero", 2012);
persona.put("direccion", direccion);

Serializado a JSON este objeto quedaría:

{
"nombre" : "Adrian",
"apellido" : "Paredes",
"fechaNacimiento" : {
"year" : 82
"month" : 5,
"day" : 4,
"date" : 3,
"hours" : 0,
"minutes" : 0,
"seconds" : 0,
"time" : 391921200000,
"timezoneOffset" : 180,
},
"cantHijos" : 3,
"nombreHijos" : ["Marco", "Polo", "Lolita"],
"soltero" : false,
"direccion" : {
"calle" : "Av. Siempre Viva",
"numero" : 2012
}
}

JSONLint (Validador de JSON)

JSONLint es un validador de JSON on-line. Muy útil para cuando queremos verificar si un JSON está bien formado o no. Cuando un JSON es muy grande, la población de llaves y corchetes puede crecer bastante. Si nos olvidamos algún corchete de cierre, alguna llave de apertura, algunas comillas, JSONLint nos indicará en qué línea está el problema, simplemente copiando y pegando la cadena JSON y presionando el botón Validate.

JSON-lib

Lo más práctico de JSON es que es muy fácil de parsear. Al final de la página oficial de JSON hay una lista de bibliotecas útiles para usar en distintos lenguajes (Java, C#, Delphi, Lisp, Phyton, Ruby, etc). Siendo Java tan popular, hay muchas herramientas que sirven para este lenguaje. Una de ellas es JSON-lib.

JSON-lib es una poderosa biblioteca que permite transformar Java Beans, Mapas, Colecciones, Arrays y XML a JSON y viceversa. Hasta convierte de JSON a DynaBeans de Apache Commons, si no necesitamos crear una clase. También soporta algunas funcionalidades que no entran dentro del estándar como la serialización y deserialización de funciones JavaScript.

Usar esta librería es muy fácil. Si estás usando Maven2, la dependencia a incluir es:

<dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.3</version>
    <classifier>jdk15</classifier>
<dependency>

Sino, debes incluir en el classpath estas librerías:
  • jakarta commons-lang 2.4
  • jakarta commons-beanutils 1.7.0
  • jakarta commons-collections 3.2
  • jakarta commons-logging 1.1.1
  • ezmorph 1.0.6
Además del mismo jar de JSON-lib, por supuesto.

A la hora de parsear una cadena JSON, JSON-lib lo que hace es almacenar los Objetos y los Arrays en Mapas y Listas. Un objeto JSON es volcado en una instancia de la clase JSONObject y un array JSON a una instancia de la clase JSONArray. Ambas clases implementan las interfaces java.util.Map y java.util.List respectivamente.

Pasar de Java Bean, Mapa o Lista a JSONObject es casi trivial. Simplemente usamos el método estático de fromObject de JSONObject o JSONArray según corresponda.

Por ejemplo, si queremos pasar un objeto Persona a JSON:

   Persona persona = new Persona();
persona.setNombre("Adrian");
persona.setApellido("Paredes");
persona.setFechaNacimiento(parseDate("03/06/82", "dd/MM/yy"));
persona.setSoltero(false);
persona.setCantHijos(3);

List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.setNombreHijos(nombres);

Direccion direccion = new Direccion();
direccion.setCalle("Av. Siempre Viva");
direccion.setNumero(2012);
persona.setDireccion(direccion);

JSONObject jsonObj = JSONObject.fromObject(persona);
System.out.println(jsonObj);

La salida del System.out.println de este código va a ser sencillamente el string JSON:

{
"nombre" : "Adrian",
"apellido" : "Paredes",
"fechaNacimiento" : {
"year" : 82
"month" : 5,
"day" : 4,
"date" : 3,
"hours" : 0,
"minutes" : 0,
"seconds" : 0,
"time" : 391921200000,
"timezoneOffset" : 180,
},
"cantHijos" : 3,
"nombreHijos" : ["Marco", "Polo", "Lolita"],
"soltero" : false,
"direccion" : {
"calle" : "Av. Siempre Viva",
"numero" : 2012
}
}

Esto es así, ya que el objeto JSONObject tiene sobrecargado el toString() para que escupa el mapa en formato JSON. Así de sencillo.

Si queremos pasar de HashMap a JSON, el código es exactamente igual:

   Map persona = new HashMap();
persona.put("nombre", "Adrian");
persona.put("apellido", "Paredes");
persona.put("fechaNacimiento", parseDate("03/06/82", "dd/MM/yy"));
persona.put("soltero", false);
persona.put("cantHijos", 3);

List nombres = new ArrayList();
nombres.add("Marco");
nombres.add("Polo");
nombres.add("Lolita");
persona.put("nombreHijos", nombres);

Map direccion = new HashMap();
direccion.put("calle", "Av. Siempre Viva");
direccion.put("numero", 2012);
persona.put("direccion", direccion);

JSONObject jsonObj = JSONObject.fromObject(persona);
System.out.println(jsonObj);

El fromObject puede recibir prácticamente cualquier cosa. Ya vimos que podemos mandarle un Bean y un HashMap, cualquier cosa que pueda convertirse en Mapa de JSON. Por supuesto, también puede recibir un String JSON. De esta forma, podemos deserealizar objetos:

String jsonStringPersona = "{" +
"\"apellido\":\"Paredes\"," +
"\"nombre\":\"Adrian\"," +
"\"direccion\":{" +
"\"calle\":\"Av. Siempre Viva\"," +
"\"numero\":2012" +
"}," +
"\"soltero\":false," +
"\"nombreHijos\":[\"Marco\",\"Polo\",\"Lolita\"]," +
"\"cantHijos\":3," +
"\"fechaNacimiento\":{" +
"\"date\":3," +
"\"day\":4," +
"\"hours\":0," +
"\"minutes\":0," +
"\"month\":5," +
"\"seconds\":0," +
"\"time\":391921200000," +
"\"timezoneOffset\":180," +
"\"year\":82" +
"}" +
"}";

JSONObject jsonPersona = JSONObject.fromObject(jsonStringPersona);
String nombre = jsonPersona.getString("nombre");
String apellido = jsonPersona.getString("apellido");
JSONObject jsonDireccion = jsonPersona.getJSONObject("direccion");
String calle = jsonDireccion.getString("calle");
Integer numero = jsonDireccion.getInt("numero");
JSONArray jsonArray = jsonPersona.getJSONArray("nombreHijos");
String hijo1 = jsonArray.getString(0);
String hijo2 = jsonArray.getString(1);
String hijo3 = jsonArray.getString(2);

System.out.println(nombre + " " + apellido);
System.out.println("Vive en: " + calle + " " + numero);
System.out.println("Sus hijos se llaman: " + hijo1 + ", " + hijo2 + " y " + hijo3);

Recordemos que un JSONObject implementa la interfaz java.util.Map, por lo que al levantar el string JSON en un objeto JSONObject ya tenemos un Mapa con el que podemos manipular los datos.

Si quisiéramos volcar el JSONObject en el Java Bean Persona podríamos hacer:

Persona persona = (Persona) JSONObject.toBean(jsonPersona, Persona.class);

Si miramos lo que tiene persona, vemos que la deserealización se ha realizado con éxito:

System.out.println(ReflectionToStringBuilder.toString(persona));

Otra funcionalidad muy interesante de JSON-lib es que permite pasar de JSON a XML y de XML a JSON. Por ejemplo, si quisiéramos serializar el jsonPersona a XML, tendríamos que hacer:

XMLSerializer xmlSerializer = new XMLSerializer();
String xml = xmlSerializer.write(jsonPersona);
System.out.println(xml);

Para pasar de XML a JSON es un poco más complicado (tampoco tanto). A quien le interese explorar esta funcionalidad, la documentación de la biblioteca nos recomienda este tutorial.

Cabe aclarar que para que funcione el XMLSerializer en tiempo de ejecución se necesita una dependencia no mencionada en la documentación y no incluida en el pom.xml de JSON-lib:

<dependency>
    <groupId>xom</groupId>
    <artifactId>xom</artifactId>
    <version>1.1</version>
</dependency>

JSON-RPC

JSON-RPC es un protocolo de llamadas a procedimientos remotos similar a XML-RPC, pero más liviano. Como dice la especificación, fue diseñado para ser simple. Se trata de un protocolo para un mecanismo típico de RPC en donde hay un cliente y un servidor que establecen una conexión. El cliente invoca métodos remotos publicados por el servidor enviando un request con el nombre del método y sus parámetros. El servidor contesta con un response que contiene los datos de respuesta. Tanto los request como los response son paquetes HTTP cuyo recurso (información transmitida) son objetos JSON.

El objeto request contiene tres propiedades:
  • method: un String que contiene el nombre del método a invocar
  • params: una Lista de Objetos para pasar como argumento del método
  • id: un identificador que se usa para matchear el request con el response
El objeto response contiene tres propiedades:
  • result: el Objeto resultado que retorna la invocación del método (debe ser nulo si hubo un error en la invocación)
  • error: un Objeto error que será nulo si no hubo error en la invocación del método
  • id: el mismo identificador del request
Las notificaciones son peticiones especiales que no reciben una respuesta. El objeto de notificación tiene las mismas dos primeras propiedades que un objeto request (method y params) y la propiedad id debe ser nula.

Un ejemplo de request podría ser:

{ "method" : "echo", "params" : ["Hello JSON-RPC"], "id" : 1 }

Lo que equivaldría a la invocación remota:

service.echo("Hello JSON-RPC")

Un ejemplo de response sin error para esta invocación podría ser:

{ "result" : "Hello JSON-RPC", "error" : null, "id" : 1 }

Un response con error:

{ "error" : { "message" : "Se ha producido un error de comunicación.", "code" : 15}, "id" : 1 }

jabsorb (Java to JavaScript Object Request Broker)

jabsorb es un framework Ajax/Web 2.0 simple y liviano. Permite realizar llamadas a métodos de objetos Java alojados en un servidor de aplicaciones web desde código JavaScript de forma transparente, como si el código de los objetos residiera en el browser cliente. Encapsula por completo la serialización y deserealización de los objetos y usa como protocolo de transporte el recién mencionado JSON-RPC.

Lamentablemente parece que la gente que desarrolla este broker nunca subió una distribución al repositorio público de Maven. De todas formas, yo lo he encontrado para bajar en un repositorio de Apache exclusivo del proyecto Apache Tuscany. Esto sirve para "zafar", pero bajo ningún punto de vista es una solución buena, porque dependemos de que la gente de Apache Tuscany mantenga este servidor y de que si aparece una nueva versión la actualicen. Por otra parte, no veo a la gente de jabsorb con intención de subir la biblioteca a un repositorio de Maven en un futuro cercano, considerando que esto está reportado como bug de prioridad media y continúa abierto desde el 16 de enero de 2008 (Ver Bug #40). Así que, para los que usamos Maven, tenemos que conformarnos con descargar la dependencia de algún repositorio de un proyecto público que encontremos o bajar el jar desde la página de jabsorb, a la antigua, y subirlo a un repositorio local que tengamos en nuestra empresa o usarlo directamente del repositorio local de nuestra máquina.

Si quieren descargar la dependencia desde el repositorio del proyecto Apache Tuscany, pueden agregar el servidor en el settings.xml de Maven:

<repository>
    <id>tuscany.apache.org</id>
    <name>Apache Tuscany</name>
    <url>http://svn.apache.org/repos/asf/tuscany/maven</url>
    <layout>default</layout>
</repository>

Y luego agregar la siguiente dependencia en pom.xml del proyecto:

<dependency>
    <groupId>org.jabsorb</groupId>
    <artifactId>jabsorb</artifactId>
    <version>1.3.1</version>
</dependency>

Veamos un ejemplo de uso muy sencillo, así entendemos la potencia de este mecanismo de RPC.

Dado que nuestro objetivo es comunicar código JavaScript que corre del lado del cliente con código Java que corre del lado del servidor, para probar jabsorb tendremos que armar una aplicación web. Si desean crear un proyecto de aplicación web para Eclipse y Tomcat usando un arquetipo de Maven2 y no saben cómo, pueden consultar los post HelloServlet Web Application con Maven2 y Eclipse y Plugin de Maven2 para Sysdeo de este mismo blog.

La biblioteca expone sólo dos componentes: JSONRPCBridge y JSONRPCServlet. Para registrar el JSONRPCServlet, quien maneja las peticiones HTTP, tendremos que agregar el mapeo en el web.xml de la aplicación:

<servlet>
    <servlet-name>JSONRPCServlet</servlet-name>
    <servlet-class>org.jabsorb.JSONRPCServlet</servlet-class>
    <init-param>
        <param-name>gzip_threshold</param-name>
        <param-value>200</param-value>
    </init-param>
</servlet>
<servlet-mapping> 
    <servlet-name>JSONRPCServlet</servlet-name>
    <url-pattern>/JSON-RPC</url-pattern>
</servlet-mapping>

El JSONRPCBridge mantiene las referencias a los objetos exportados, los objetos a los que se le pueden ejecutar invocaciones remotas. Tiene como responsabilidad decodificar las llamadas a los métodos, localizar el objeto y realizar la ejecución. Para que una única instancia de JSONRPCBridge esté disponible durante la conexión entre el cliente y el servidor, debemos crearla y guardarla en la sesión del servidor.

Por ejemplo, si estamos usando JSP para escribir los servlets podemos hacer:

<jsp:useBean id="JSONRPCBridge" scope="session" class="org.jabsorb.JSONRPCBridge" />

Luego, creamos una clase Java bien simple con el código que queremos ejecutar desde JavaScript. Por ejemplo:

package com.blogspot.tecnologiasjava.jarsorb.pruebas;

public
class Hello {
public String sayHello(String who) {
return "hello " + who;
}
}

Luego tendremos que registrar una instancia de la clase Hello en el bridge:

<% JSONRPCBridge.registerObject("hello", hello); %>

Listo. De esta forma tan simple hemos publicado todos los métodos públicos de la clase Hello para que puedan ser ejecutados de forma remota desde un navegador.

Veamos ahora el código JavaScript necesario para el lado del cliente:

var jsonrpc;

function
onLoad() {
jsonrpc = new JSONRpcClient("/JSON-RPC");
}

function clickHello() {
var whoNode = document.getElementById("who");
var result = jsonrpc.hello.sayHello(whoNode.value);
alert("The server replied: " + result);
}

Veamos el jsp hello.jsp completo:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<jsp:useBean id="JSONRPCBridge" scope="session"
   class="org.jabsorb.JSONRPCBridge"/>

<jsp:useBean id="hello" scope="session"
   class="com.blogspot.tecnologiasjava.jarbsorb.pruebas.Hello"/>

<%JSONRPCBridge.registerObject("hello", hello);%>

<html>
   <head>
      <script type="text/javascript" src="jsonrpc.js"></script>
      <script type="text/javascript" src="hello.js"></script>
      <title>jabsorb Hello</title>
   </head>

   <body bgcolor="#ffffff" onLoad="onLoad()">
      <h2>jabsorb Hello</h2>
      <p>The jabsorb <em>Hello World</em> application.</p>
      <p>        
      <strong>Who:</strong>
       <input type="text" id="who" size="30" value="Adrian"/>&nbsp;        
       <input type="button" value="Say Hello" onclick="clickHello()"/>
      </p>
   </body>
</html>

Si queremos ejecutar este ejemplo, debemos bajarnos el ZIP de la página de descarga de jabsorb. Este ejemplo de Hello viene dentro. Pero no sólo por eso es importante bajarlo, además necesitamos el cliente JSON-RPC de jabsorb escrito en JavaScript, el archivo jsonrpc.js, que podemos copiar de la carpeta webapps/jsonrpc directo a la carpeta webapp de nuestro proyecto.

Con los archivos hello.js, hello.jsp y jsonrpc.js en nuestra carpeta webapp y la clase Hello.java en el paquete com.blogspot.tecnologiasjava.jarbsorb.pruebas, estamos listos para levantar el Tomcat (o el servidor de aplicaciones web que tengamos) y, si todo salió bien, cuando hagamos click en el botón de Say Hello del formulario de prueba de http://localhost:8080/jabsorb_pruebas/hello.jsp, el alert nos saludará con el texto The server replied: hello Adrian, o el nombre que hayamos escrito en el input text.


La línea más importante para analizar en el código es ésta:

var result = jsonrpc.hello.sayHello(whoNode.value);

Aquí es donde toda la magia de jabsorb y JSON-RPC muestra su esplendor. Una línea de código JavaScript que ejecuta un método de una clase Java ubicada en el servidor. Los paquetes JSON-RPC van por debajo, son invisibles para el programador.

Si nos ponemos a pensar un poco, este mecanismo de RPC transparente que proporciona jabsorb tiene bastantes similitudes con los mecanismos de RPC que proveen otros frameworks bastante más pesados, como por ejemplo EJB3. Claro que EJB3 es una plataforma y provee muchas más funcionalidades que obviamente JSON-RPC no provee, ni intenta proveer, pero lo asombroso es que, con tan poco y con tanta simpleza, las funcionalidades provistas por jabsorb y otras tecnologías livianas basadas en JSON resultan muy poderosas.

Referencias

lunes 9 de noviembre de 2009

JSF 1.2: Los Values de los InputText Number y Boolean que Deberían ser Null

Cuando uno define un input-text en JSF (que no es requerido) para un valor que debería ser un Number (Integer, Double, etc) o un Boolean, y al correrlo el usuario lo deja vacío, podemos ver, con desagrado, que el valor de esa variable se ha convertido en cero o false, respectivamente. En la mayoría de los casos, este comportamiento no es el deseado cuando estamos trabajando con objetos y no con primitivas. Queremos que el valor permanezca en null y no que se le asigne un valor incorrecto.

Aunque cueste creerlo, éste no es realmente un bug de JavaServer Faces directamente, sino del servidor de aplicaciones que se esté usando. A partir de la versión 1.2, JSF utilizará la implementación del ELParser provista por el servidor web para evaluar las expresiones JSF. Por lo tanto, dependiendo del servidor que estén usando puede que nunca se hayan topado con este bug (de hecho el bug es totalmente dependiente de la plataforma en la que se esté corriendo). Este problema es muy común en los Apache Tomcat de la versión 6.0.16 para arriba. (Yo uso la 6.0.20 y me pasa.)

Para solucionarlo, no se rompan la cabeza destrozando el código, agregando converters horribles que sólo lo van a hacer sentir peores programadores a ustedes, o lo que sea. Si usan Tomcat, basta con agregar este argumento a la Java Virtual Machine (JVM) cuando vayan a ejecutar el startup:

-Dorg.apache.el.parser.COERCE_TO_ZERO=false

(No sé a quién se le pudo ocurrir que por defecto fuera true.)

Si están levantando el Tomcat desde una consola de Linux, deben agregarle el argumento a la variable JAVA_OPTS en el catalina.sh de la carpeta lib. Por ejemplo, yo tengo:

if [ -z "$LOGGING_MANAGER" ]; then
    JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dorg.apache.el.parser.COERCE_TO_ZERO=false"
else
    JAVA_OPTS="$JAVA_OPTS $LOGGING_MANAGER"
fi

Si usan Windows deberán hacer lo mismo con la variable de entorno JAVA_OPTS, pero en el catalina.bat.

IMPORTANTE: Si levantan el Tomcat desde el Eclipse (usando el plugin Sysdeo) tienen que agregarlo como JVM Parameter. Para eso van a Window --> Preferences --> Tomcat --> JVM Settings --> Append to JVM Parameters y escriben -Dorg.apache.el.parser.COERCE_TO_ZERO=false como indiqué más arriba.

¡Ya está! Si todo salió bien, la próxima vez que levanten el Tomcat y corran la aplicación, los values de los input-text no agregarán más ceros en los Numbers, ni false en los Booleans.

------------------------------------------
http://forums.sun.com/thread.jspa?threadID=5359447
https://jsp-spec-public.dev.java.net/issues/show_bug.cgi?id=183

domingo 9 de agosto de 2009

Construir una aplicación Web (JSF, RichFaces y Facelets) con Maven

En este post nos proponemos construir una aplicacion web JSF utilizando el arquetipo maven-archetype-jsfwebapp disponible para Maven. En el sitio oficial de RichFaces existe un tutorial para utilizar este arquetipo. Nuestra contribución es realizar algunas modificaciones a la aplicacion resultante para integrar Facelets.

Versiones Utilizadas
Maven2
Eclipse Ganymede

Que es un arquetipo? Un arquetipo es la plantilla que se utilizara para crear la estructura y archivos base necesarios para obtener un proyecto funcional. En este caso una aplicacion jsf.

Paso 1) Agregar repositorios al archivo settings.xml de Maven
Agregamos al archivo DIRECTORIO_INSTALACION_MAVEN/conf/settings.xml el siguiente profile que contiene los repositorios de RichFaces.



<profile>
    <id>jsf-app-profile</id>
    <repositories>
        <repository>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
            </snapshots>
            <id>snapshots.jboss.org</id>
            <name>Snapshot Jboss Repository for Maven</name>
            <url>http://snapshots.jboss.org/maven2/</url>
            <layout>default</layout>
        </repository>
        <repository>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
            </snapshots>
            <id>repository.jboss.com</id>
            <name>Jboss Repository for Maven</name>
            <url>http://repository.jboss.com/maven2/</url>
            <layout>default</layout>
        </repository>
    </repositories>
</profile>



Paso 2) Agregar el perfil a los perfiles activos
Para que el perfil que agregamos sea visible para Maven, el tag activeProfiles del settings.xml debe quedar asi:


<activeProfiles>
  <activeProfile>jsf-app-profile</activeProfile>
</activeProfiles>



Paso 3) Crear el Proyecto

En una consola tipeamos:


mvn archetype:generate -DarchetypeGroupId=org.richfaces.cdk -DarchetypeArtifactId=maven-archetype-jsfwebapp -DarchetypeVersion=3.3.1-SNAPSHOT -DgroupId=com.blogspot.tecnologiasjava.controller -DartifactId=AppWeb



Donde DgroupId es el package de los Managed Beans y DartifactId el nombre del proyecto.

Paso 4) Agregar Dependencias

Con el comando anterior se creo una carpeta llamada AppWeb que contiene la estructura del proyecto. Ahora debemos agregar unas dependencias al pom.xml del proyecto para que compile.
Dentro del pom.xml buscamos el tag dependencies, este deberia quedar como:



  <dependencies>
      <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
      </dependency>
      <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.4</version>
    <scope>provided</scope>
      </dependency>
      <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
      </dependency>
      <dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.1.2</version>
      </dependency>
      <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
      </dependency>
      <dependency>
    <groupId>javax.faces</groupId>
    <artifactId>jsf-api</artifactId>
    <version>1.2_12</version>
      </dependency>
      <dependency>
    <groupId>javax.faces</groupId>
    <artifactId>jsf-impl</artifactId>
    <version>1.2_12</version>
      </dependency>
      <dependency>
    <groupId>javax.el</groupId>
    <artifactId>el-api</artifactId>
    <version>1.0</version>
    <scope>provided</scope>
      </dependency>
      <dependency>
    <groupId>el-impl</groupId>
    <artifactId>el-impl</artifactId>
    <version>1.0</version>
    <scope>provided</scope>
      </dependency>
      <dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>jsr250-api</artifactId>
    <version>1.0</version>
      </dependency>
      <!-- RichFaces libraries -->
      <dependency>
    <groupId>org.richfaces.framework</groupId>
    <artifactId>richfaces-api</artifactId>
    <version>3.3.1-SNAPSHOT</version>
      </dependency>
      <dependency>
    <groupId>org.richfaces.framework</groupId>
    <artifactId>richfaces-impl</artifactId>
    <version>3.3.1-SNAPSHOT</version>
      </dependency>
      <dependency>
    <groupId>org.richfaces.ui</groupId>
    <artifactId>richfaces-ui</artifactId>
    <version>3.3.1-SNAPSHOT</version>
      </dependency>
      <!-- Facelets -->
      <dependency>  
       <groupId>com.sun.facelets</groupId>  
       <artifactId>jsf-facelets</artifactId>  
       <version>1.1.14</version>  
     </dependency>  
  </dependencies>



Paso 5) Compilar, empaquetar e instalar

Dentro de la carpeta del proyecto ejecutamos el comando mvn install para que el proyecto compile, se empaquete y sea instalado en el repositorio local.



Paso 6) Convertir el proyecto en un proyecto eclipse

Ejecutamos el comando mvn eclipse:eclipse

Paso 7) Convertir el proyecto en un proyecto tomcat

Ejecutamos el comando mvn sysdeo-tomcat:generate

Paso 8) Incluyendo los jars

Ejecutamos el comando mvn war:inplace

Para desplegar la aplicación vamos a utilizar el pluging de Sysdeo Eclipse Tomcat . Lo podes descargar desde http://www.eclipsetotale.com/tomcatPlugin.html#A3. Se descomprime el zip y se copia la carpeta com.sysdeo.eclipse.tomcat_3.2.1 a la carpeta plugins del Eclipse. Reiniciamos el IDE para que tome los cambios. Ultimo Paso, configurar el tomcat que vamos a utilizar. En el menu Windows/Preferences/Tomcat configuramos el path y la versión de Tomcat



Paso 8) Importar el proyecto en eclipse
Importamos el proyecto haciendo click en la opción Import... del menú File.



Hacemos click en Existing Projects into Workspace y seleccionamos la carpeta del proyecto.





Importante:
Si te falta la variable M2_REPO el proyecto no va a compilar. Para definirla hace click sobre el menu Windows/Preferences/Java/Build Path/Classpath Variables.



Paso 9) Modificar el web.xml
Reemplazamos el contenido del web.xml por el siguiente que tiene configurado RichFaces y Facelets



<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 <display-name>Greeter</display-name>
 <!-- AJAX View handlers -->
  <context-param>
    <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
    <param-value>com.sun.facelets.FaceletViewHandler</param-value>
  </context-param>

  <!-- Rich Faces -->
    <context-param>
        <param-name>org.richfaces.SKIN</param-name>
        <param-value>blueSky</param-value>
    </context-param>
    
    
  <!-- Making the RichFaces skin spread to standard HTML controls -->
  <context-param>
      <param-name>org.richfaces.CONTROL_SKINNING</param-name>
      <param-value>enable</param-value>
  </context-param>
    
    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    
      <!-- Faces Servlet -->
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    
    <filter> 
        <display-name>RichFaces Filter</display-name> 
        <filter-name>richfaces</filter-name> 
        <filter-class>org.ajax4jsf.Filter</filter-class
         <init-param>
            <param-name>createTempFiles</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>maxRequestSize</param-name>
            <param-value>1000000</param-value>
        </init-param>
    </filter> 
    <filter-mapping> 
        <filter-name>richfaces</filter-name> 
        <servlet-name>Faces Servlet</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>
    <listener>
        <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Faces Servlet Mapping -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
        
  <welcome-file-list>
    <welcome-file>index.xhtml</welcome-file>
  </welcome-file-list>

 </web-app>



Paso 10) Crear template.xhtml

Este archivo sera la base para todas las páginas de la aplicación web. Incluimos un peque;o menu y definimos el tag ui:include que nos permitira incluir dentro de esta plantilla lo que definamos dentro del campo ui:define name="body" de los otros archivos.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:a4j="http://richfaces.org/a4j"
  xmlns:rich="http://richfaces.org/rich">
<head>
<title><h:outputText value="aplicacion JSF" /></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<f:view>
  <a4j:form>
    <rich:toolBar>
      <rich:dropDownMenu value="Productos">
        <rich:menuItem submitMode="none">
          <h:outputLink value="listar-productos.xhtml">
            <h:outputText value="Listar Productos" />
          </h:outputLink>
        </rich:menuItem>
      </rich:dropDownMenu>
    </rich:toolBar>
  </a4j:form>
  <rich:spacer height="20" />
</f:view>
<p>
  <ui:insert name="body" />
</p>
</body>
</html>



Paso 11) Reescribimos el index.xhtml

  
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:s="http://jboss.com/products/seam/taglib"
  xmlns:rich="http://richfaces.org/rich">
<body>
<ui:composition template="/template.xhtml">
  <ui:define name="body">
  </ui:define>
</ui:composition>
</body>
</html>


Paso 12) Más Páginas

Agregamos otra página que es utilizada por el menú definido en el template.xhtml llamada listar-productos.xhmtl



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:s="http://jboss.com/products/seam/taglib"
  xmlns:rich="http://richfaces.org/rich">
<body>
<ui:composition template="/template.xhtml">
  <ui:define name="body">
    <h:outputText value="Productos" />
  </ui:define>
</ui:composition>
</body>
</html>


Paso 13) Actualizar el contexto del tomcat


Para que el tomcat despliegue la aplicación, debemos actualizar el contexto del mismo. Abrimos el menú contextual del proyecto y hacemos click sobre la opción Tomcat Project/Update context definition



Importante: Para no tener problemas de librerías tenemos que deshabilitar el DevLoader, en otro post hablaremos de esta funcionalidad. En el menú contextual del proyecto buscamos la opción Properties. Dentro de la ventana Properties hacemos click en Tomcat.
Una de las tres pestañas se llama DevLoader Classpath. En esta pestaña desactivar el checkbox.




Paso 14) Desplegar la aplicación
Hacemos click sobre el icono del tomcat para levantarlo.

Abrimos un navegador y tipeamos http:localhost:8008/AppWeb, si todo salio bien deberíamos ver nuesta aplicación, ahora un poco vacia.