Category: Java


Este es el primero de una serie de tutoriales que pretendo publicar acerca de ir creando aplicaciones utilizando JSF 2.1.Intentaré centrarme en cómo se hacen ciertas tareas e ir creando material nuevo que o bien sea difícil de encontrar en nuestro idioma o no se haya publicado nada al respecto en ningún otro idioma. Por ello, pondré enlaces de recomendada lectura que vean que pueden aclarar conceptos, ya que es una tontería volver a publicar algo que esté bien explicado en otro lado.

Cómo primer objetivo, será entender el ciclo de vida y el proceso de creación de una aplicación sencilla en JSF, pero antes de nada, os recomiendo este enlace de adictos al trabajo  en el que se explican todos los conceptos de JSF sin entrar en mucho detalle.

Vamos a comenzar por crear un proyecto, para esta serie de tutoriales usaremos Netbeans 7.2 que ya viene incluido con soporte para JSF 2.1, un contenedor Tomcat 7.0.22 y soporte para Java EE6. Así que creamos un proyecto del tipo web application

Después hacemos las configuraciones del proyecto y pulsando Siguiente, hasta llegar a la elección de que frameworks queremos utilizar, entonces seleccionamos Java Server Faces

Ahora que tenemos el proyecto creado, vamos a crear un pequeño formulario para crear las estadísticas de un personaje de D&D. Necesitaremos  un backend bean, su diseño en facelet (XHTML) y una clase POJO para representar al personaje. Al final del articulo, encontrareis el proyecto que tiene la siguiente estructura:

Podréis observar las impresiones en consola que se hacen en los diferentes métodos, para que veamos y comprendamos mejor el ciclo de vida JSF aplicado al funcionamiento de una aplicación.

Así que vamos a echarle un ojo a la siguiente imagen del ciclo de vida:

ciclo de vida

Cómo podemos ver, tenemos diferentes fases, que en el enlace de adictos al trabajo se explica muy bien:

  1. Restaurar los conponentes de la vista (restore view). En esta etapa el controlador construye en memoria la estructura de componentes de la página.
  2. Aplicar los valores de la petición.. (apply request values). En esta etapa se recuperan los valores de la request y se asignan a los beans de la página.
  3. Procesamiento de las validaciones (process validations). Se verifican los parámetros de entrada según un conjunto de reglas definidas en un fichero de configuración.
  4. Actualizar los valores del modelo (update model values). Los valores leídos y validados son cargados en los beans.
  5. Invocacion a la aplicación (invoke application). Se ejecutan las acciones y eventos solicitados para la página. Si es necesario se realiza la navegación.
  6. Generación de la página (render response). En esta fase se genera la página que será enviada al usuario con todos sus elementos y valores actualizados.

Entonces, cómo afecta esto a nuestro proyecto?  Al  llamar por primera vez a la página, sólo se ejecutan las dos primeras fases, debido a que no hay información de la petición para relllenar el modelo.  Ahora el usuario rellena los datos y pueden ocurrir dos cosas, que haya algún error de validación, en cuyo caso no pasará del paso de validación y no se rellenará el modelo (nunca se ejecuta el setter) ni se ejecutará ninguna acción (no se ejecuta el método save() ). O bien que todo esté correcto, entonces ejecutará el siguiente paso de actualizar los valores del modelo y si no hay ningún error en este paso, se pasarían a ejecutar las invocaciones de los eventos, en este caso, se ejecutaría el método save().

También se puede ver, que el getter en el backend bean, se llama una vez por cada propiedad de la clase POJO, ya que a través de este getter obtenemos la clase POJO, por lo que si lo estuviéramos obteniendo con una consulta a una base de datos o algún otro método costoso, habrá que guardarlo en una variable del bean.

Por supuesto, esto se puede complicar más, porque habrá que tener en cuenta que ocurre cuando hay listeners  de por medio. Pero eso lo dejamos para otra entrada.

El proyecto entero, podeis descargarlo desde aquí.

Desde hace unos meses que comencé a enredar con Icefaces 3, me topé con un problema al utilizar uno de los componentes más potentes para formularios. Se trata del componente  ace:dateTimeEntry,  para tener una bonita presentación a la hora de introducir fechas y horas en los formularios. El componente, dispone de dos formatos de presentación, el primero establece el calendario en bruto en la página y el segundo, y más útil, nos muestra un campo de texto que al recibir el foco, muestra un popup con el calendario para introducir la fecha. Un ejemplo de uso de esta esta segunda forma de renderización es la siguiente:

<ace:dateTimeEntry id="cal"
value="#{bean.persona.fechaNacimiento}"
timeZone="Canada/Mountain"
pattern="dd/MM/yyyy"
navigator="true"
yearRange="1940:1980"
renderAsPopup="true"/>

Con el atributo renderAsPopup , conseguimos que se renderice como un popup. Para el resto de opciones, aunque se explican por sí solas, podeis ver cómo funcionan en el showcase de icefaces. Una vez presentado el componente, vamos a ver el problema que me surgía y hasta la semana pasada no he conseguido dar con el origen. Cuando renderizaba el componente de esta forma,  el popup se mostraba, pero al realizar una navegación y volver al formulario, el popup con el calendario dejaba de mostrarse.  Después de mucho darle vueltas al porqué y preguntar en el foro de icesoft(sin mucho éxito), conseguí dar con el problema. Lo primero que hay que tener en cuenta, esque en Icefaces trabaja completamente con Ajax, con lo cual, cuando realizamos una actualización de sección, solo refresca la partede la página que ha cambiado. Para ello, el servidor envia como respuesta un XML con los cambios que ha habido. Entonces, en algunas páginas, esta respuesta, hacía cambiar el body entero, con una respuesta parecida a esta:

<div><partial-response></div>
<div><changes></div>
<update id="javax.faces.ViewBody"><body title=""><div id="header">...

En el momento que aparece <update id=”javax.faces.ViewBody”>, se nos fastidia el popup de dateTimeEntry, debería de aparecer un id de los que hayamos puesto nosotros. Tras revisar y revisar el código, el fallo está en algo bastante tonto, pero no trivial. Resulta que en las plantillas, para organizar las secciones de cada página, lo tenía dividido utilizando divs y algún que otro table, sin asignarle un id.  Y JSF, para detectar los cambios que hay en el DOM cuando cambiamos de navegación, utiliza los id para localizar que parte tiene que sustituir, así que asignandole un id, debería bastar, pero aún así, se dan comportamientos extraños. Por lo que lo mejor, es utilizar las etiquetas propias de JSF para organizar el código, que luego se sustituira en el cliente por un div.

De forma que para arreglar este error y evitar otros posibles funcionamientos erráticos, habría que sustituir (si tenemos algo parecido) esto:


<div id="content">

<ui:insert name="content"></ui:insert>

</div>

&nbsp;

&nbsp;

Por esto otro:


<h:panelGroup layout="block" id="content">

<ui:insert name="content"></ui:insert>

</h:panelGroup>

Y siempre, siempre, siempre, asignarle un id a los panelGroup, para que JSF encuentre correctamente donde tiene que sustituir al hacer cambios en el DOM.

Antes de nada, Feliz año a mis escasos lectores. Últimamente ando enfangado de nuevo con Java y por supuesto, con JasperReport para sacar informes. Con la aplicación que estoy desarrollando actualmente, estoy utilizando Icefaces 3 para JSF, que da muchas facilidades para crear aplicaciones rápidamente y atractivas, tanto visual como funcionalmente. Una de las cualidades de esta versión del framework, esque todo va por AJAX. Esto, al llegar al momento de presentar al usuario los informes creados con JasperReport, supone un problema. Ya que normalmente, nos inmiscuimos en el ciclo de vida de JSF para capturar la respuesta e imprimir nuestro informe, y esto no le sienta muy bien al framework.  Así que tras unas cuantas peleas y cabezazos contra el teclado, he conseguido llegar a una solución. Dicha solución consiste en sacar el informe en otra ventana, fuera del flujo de Icefaces. Y para presentar el informe, utilizo directamente un Servlet para generar la respuesta. Paso a explicar paso a paso.

Lo primero, tenemos en nuestro archivo .xhtml lo siguiente:

<h:commandButton id="pushButtonId12"  actionListener="#{informes.pdfEdificios}"  />

Utilizamos actionListener, para que no haga ningún refresco del contenido. Ahora, el código del método encargado de manejar dicho evento es el siguiente:


public String pdfEdificios(ActionEvent event) throws IOException{
try {
Connection con = edificiosDao.getConnection();
HashMap parameters = new HashMap();
String reportsDirectory = "c:/Users/Usuario/Documents/NetBeansProjects/Proyecto/build/web/";
String jasperName = "report1.jasper";
JasperPrint jasperPrint = UtilReports.generateReport(con, reportsDirectory, jasperName, parameters);
getSessionMap().put("report",jasperPrint);
JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), "window.open('Report', 'myWindow');");
} catch (NamingException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (SQLException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (JRException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}

En este método, simplemente generamos el informe mediante UtilReports.generateReport(con, reportsDirectory, jasperName, parameters), una vez hecho, guardamosel objeto JasperPrint a la sesión, para poder recuperarlo después en el Servlet.
Hasta este punto, ya tenemos el informe creado y guardado en la sesión, ahora necesitamos abrir el Servlet automáticamente para llevar el informe al usuario. Para ello, Icefaces nos proporciona JavascriptContext, desde el cual podemos añadir código javascript enviado desde el servidor.
Así que mediante JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), “window.open(‘Report’, ‘myWindow’);”); enviamos el código para que se abra una ventana nueva que lleva al servlet Report. Que habremos creado previamente con el siguiente código para manejar la respuesta:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
JasperPrint report =(JasperPrint) request.getSession().getAttribute("report");
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + "report" + ".pdf;");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, report);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, response.getOutputStream());
exporter.exportReport();
} catch (JRException ex) {
Logger.getLogger(Report.class.getName()).log(Level.SEVERE, null, ex);
}finally{
request.getSession().removeAttribute("report");
}

}

Aquí, recuperamos de la sesión el objeto JasperPrint de nuestro informes y preparamos las cabeceras de la respuesta para indicar el tipo, y utilizamos un exporter para exportar el informe en el formato deseado (normalmente pdf). Por último, es importante poner en el bloque finally, la liberación del objeto de la sesión, para no sobrecargar la memoria del servidor.
Esta es la única forma que he encontrado, de momento, para poder mostrar los informes sin entrar en conflicto con Icefaces. Si alguién tiene una solución mejor, que no dude en comentarlo.

Después del parón de los exámenes volvemos a la carga. En este caso, voy a publicar la práctica de una asignatura de la carrera. El enunciado de la práctica consiste en resolver el problema del Trimino, que se trata de un tablero de X*X tamaño, siendo X una potencia de 2, y teniendo una casilla marcada. Entonces debemos recubrir el tablero utilizando triminos (un angulo de tres casillas). Una descripción más detallada del problema con su solución teórica la podemos encontrar en el siguiente enlace : http://www.dma.fi.upm.es/docencia/primerciclo/matrecreativa/juegos/poliominos/triminos/triminos.htm

Para la solución del problema se puede utilizar uno de los esquemas algorítmicos básicos, en este caso el esquema de Divide y Venceras. Que comentado por encima, consiste en coger un problema y dividirlo en partes iguales y más sencillas, hasta llegar a una solución trivial. Una vez llegada a esta solución, se va juntando las soluciones parciales para llegar a una solución completa.
La adaptación de mi esquema para este problema es el siguiente:


public static void divide(Rectangulo r){
boolean b=buscarLleno(r);
if(b) // comprobacion de si es el caso trivial
return;
// Division en subproblemas
ArrayList<Rectangulo> rects = r.dividirEnCuatro();
for(Rectangulo r1  : rects){
divide(r1); // Llamada recursiva para resolver el subproblema
}
 }

Gracias a la mayoría de las funciones auxiliares, el algoritmo  resultante es muy reducido. Pinchando aquí os podeis descargar la práctica resuelta. Podesi descargarlo de github: https://github.com/smaug1985/Trimino

Preparando una vista de detalles de una aplicación de Android, me he encontradoo con un curioso funcionamiento. La idea de la vista es tener un LinearLayout e ir metiendole en tiempo de ejecución pequeñas vistas que constan de un LinearLayout con un TextView para el titulo y un ListView con unos pasos. Puesto que la vista puede quedar más grande que la pantalla, todo encerrado dentro de un ScrollView. Pero por lo visto hay un pequeño bug, que cuando se encierran un ListView dentro de un ScrollView, en vez de hacer Scroll a toda el conjunto, “obliga” a los ListView a hacer scroll haciendo que su alto sea más pequeño.
Buscando por la red sobre este problema, en todos los foros y listas de desarrollares dicen que es un problema de Google que aún no han solucionado y la mejor “solución” es no poner un ListView dentro de un ScrollView. Aunque por suerte, dí con la siguiente solución , que llamando esta función:

<pre>  public static void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = 0;
        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            listItem.measure(0, 0);
            totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
    }
</pre>

Y lo que hace es poner el tamaño del listview justo para que se muestren todos los hijos. Aunque tiene el problema que si tiene muchos hijos con bastante contenido que renderizar, puede tardar bastante.
Aún así, para ir saliendo del paso, va bastante bien.

Si nos vamos a dedicar a hacer aplicaciones para iOs y Android con grandes contenidos locales, posiblemente nos vendría bien una forma de poder pasar rápidamente los datos de un sitio a otro. En iOs los contenidos locales se suelen guardar en un fichero pList, mientras que en Android los xml son la base. O también puede ocurrir que necesitemos consumir desde un servicio web que nos envie los datos en un xml formateado como un plist. En este caso, lo más sencillo es tener un plist (sobre todo si primero hemos desarrollado sobre iOs) y poder leer desde este plist en ambas plataformas. Para ello he encontrado un parser basado en SAX para leer estos plist:


https://github.com/tenaciousRas/android-plist-parser

Aunque es muy comodo, tiene las siguientes desventajas ya que parece que está preparado para leer configuraciones, no utilizar plist como almacen de datos:

  • La raiz ha de ser obligatoriamente un diccionario, no podemos tener un array como raiz.
  • Utiliza tipos propios para representar el plist y los diccionarios
  • El contenido de los array siempre los guarda como Object, con lo cual un casting a String de algo que sabemos que es un String puede causarnos una excepción.
  • Aunque está pensado para Android, no se puede crear con un Resource id de un xml propio

Por el resto, es más o menos bastante cómodo de utilizar. Vamos a poner un ejemplo de como utilizar esta clase:


PListXMLParser parser = new PListXMLParser();
String xml = Utils.getStringFromAsset(ctx,"myData.plist"); //Función que nos devuelve el contenido del xml como un string
PListXMLHandler handler = new PListXMLHandler();

parser.setHandler(handler);
parser.parse(xml);
PList plist =handler.getPlist(); // Objeto Plist que representa al fichero.
Dict d=plist.getRootDictionary();
parseData( d.getConfigurationArray("root"));

Este es un ejemplo de como obtener el contenido, primero obtenemos el contenido del plist en forma de string. Y luego creando un parser y un handler, parseamos el fichero. Ahora solo hay que obtener el objeto del tipo PList y luego el diccionario que es la raiz del arbol de datos. En mi caso, el raiz era un array, pero tuve que envolverlo en un diccionario con un solo elemento llamado root, que es el array. Así que obtengo ese array y se lo paso a un método parseData que se encarga de parsear el contenido del array a mi modelo de datos. A continuación vamos a enseñar el contenido de la función getStringFromAsset que obtiene el contenido de un fichero. Este fichero ha de estar dentro de la carpeta asset de nuestro proyecto.

public static String getStringFromAsset(Context ctx,String filename){
    try{
        AssetManager manager = ctx.getAssets();
        InputStream input= manager.open(filename);
        InputStreamReader in = new InputStreamReader(input, "utf-8");
        StringBuffer xml = new StringBuffer();
        int c =0;
        while( (c = in.read()) != -1){
           xml.append((char)c);
        }
        in.close();
        return xml.toString();
     }catch(Exception e){
         e.printStackTrace();
         return null;
     }
 }

Creo que el código se autoexplica bastante bien. Simplemente obtenemos un AssetManager, que nos permite crear flujos a los ficheros de la carpeta asset y después creamos un InputStreamReader para leer de este fichero. Es importante utilizar un tipo de flujo como este que nos permita establecer la codificación, ya que si no, nos encontraremos con carácteres extraños. Luego solo es ir leyendo caracter por caracter y guardarlos en un stringBuffer. Sinceramente, esta no es la mejor opción, ya que es bastante lenta (en una app mia, cerca de 15 segundos para un plist no muy grande) así que cuando encuentre una forma más eficiente, actualizaré.

Y aún estoy de exámenes, hasta el viernes, que estaré por fin libre de este gran peso, y esperemos, que con resultados satisfactorios.

Mientras tanto, para no tener abandonado el blog, ahi va mi pequeña aportación semanal.
Normalmente, en toda base de datos de una aplicación que se precie, suele tener algún campo del tipo Date. Pues Mysql, por norma general, si el campo no puede ser NULL, le asigna como valor por defecto ‘0000-00-00’, un valor que puede parecer inocente en un principio, pero si ya tenemos una aplicación Java funcionando o de una nueva, al intentar recuperarla del ResultSet, obtendremos una bonita excepción del tipo :


java.sql.SQLException: Value '0000-00-00' can not be represented as java.sql.Date

Aunque la primera solución que se nos puede pasar por la cabeza, después de las ganas de querer apalear la máquina virtual de Java, sería modificar la base de datos y cambiar todos los registros cuyo valor sea ‘0000-00-00’ por un NULL o si no podemos, por la fecha más antigua posible (Esa de 1970 que bla bla bla).  Ahora bién, esto puede ser bastante engorroso, aparte de que posiblemente nos pueda causar problemas posteriores con otras zonas de la aplicación. Pero por suerte, tenemos una solución bastante más asequible, rápida y escalable. Es tan sencillo, como pasarle como parámentro al conector de Mysql para java lo siguiente : “zeroDateTimeBehavior=convertToNull”, quedando por ejemplo, la línea de la conexión algo así:

"jdbc:mysql://localhost:3306/pagos?user=root&password=root&zeroDateTimeBehavior=convertToNull";

De esta forma, automaticamente, el conector, cada vez que recupere un registro del tipo fecha que sea cero, lo que hará será darle al ResultSet un null, solucionando así gran parte de nuestros problemas.

Si quereis aprender más sobre toda la funcionalidad del conector de Mysql, siempre podeis dirigiros a la documentación oficial del conector.

Cuando estamos tratando con consultas a bases de datos en Java, sobre todo en un proyecto Enterprise, podemos llegar a hablar de consultas kilométricas que acceden a varias tablas con una cantidad ingente de campos. Y si pretendemos hacer las cosas bien, utilizando los PreparedStatement, por temas de seguridad , legibilidad y reutilización, seguramente, si tenemos que hacer algún cambio en una sentencia, añadiendo o eliminando algún parametro, nos podemos encontrar con que deberemos cambiar bastantes líneas de código, en vez de la que debemos añadir/quitar solamente.

Vamos a poner un ejemplo práctico, si tenemos la siguiente sentencia de consulta a una base de datos:


SELECT usuarios.*, roles.descripcion, posts.entrada, posts.titulo FROM usuarios,descripcion,posts
WHERE usuarios.rolId=roles.id AND posts.userId=usuarios.id AND posts.fecha BETWEEN ? AND ?
AND usuarios.userId = ? AND posts.id = ? AND posts.isVisible = ? AND posts.visitas > ?

Esta consulta no es muy extensa, pero nos valdrá para ilustrar el supuesto práctico.  Ahora tendríamos el siguiente código Java para realizar la consulta:


String consulta = //la consulta de arriba;
PreparedStatement stmt = conexion.prepareStatement(consulta);
stmt.setDate(1,desde);
stmt.setDate(2,hasta);
stmt.setInt(3,userId);
stmt.setInt(4,postId);
stmt.setBoolean(5,isVisible);
stmt.setInt(6,numVisitas);

ResultSet result = stmt.executeQuery();

Esta sería la forma normal de trabajar, pero si en un futuro tenemos que realizar alguna modificación a la consulta,
por ejemplo, quitamos la necesidad de las dos fechas en la condición. Nos encontramos conque deberemos eliminar dos lineas de código
y modificar las otras 4, para que cuadre el número del parámetro. Esto no nos llevaría mucho tiempo en una sola consulta, pero si hubiera que modificar más consultas (porque se haya eliminado un campo que se utilizaba en todas las consultas como condición),  o si tratamos de una consulta con 20 parámetros por ejemplo, ya requeriría más tiempo empleado en el mantenimiento.

Así, que para ahorrar en tiempo de mantenimiento futuro, lo que podemos hacer, es insertar una variable que vaya aumentando con cada inserción de un nuevo parámetro, de esta forma,  no  tenemos que estar trabajando con el número de parámetro que se le pasa a los métodos set… y nos ahorraremos más de un dolor de cabeza. El código resultante, sería algo así:

int parameter=1;
String consulta = //la consulta de arriba;
PreparedStatement stmt = conexion.prepareStatement(consulta);
stmt.setDate(parameter++,desde);
stmt.setDate(parameter++,hasta);
stmt.setInt(parameter++,userId);
stmt.setInt(parameter++,postId);
stmt.setBoolean(parameter++,isVisible);
stmt.setInt(parameter++,numVisitas);

ResultSet result = stmt.executeQuery();

De esta forma, ya nos podemos despreocupar, de que el número de un parámetro coincida
con el de la consulta, y si hacemos alguna modificación en la consulta, no tendremos que realizar demasiado
cambio en el código y por último, nos facilita el copy & paste que tanto gusta a algunos.
Para finalizar, solo hay que recordar que el número de parametros, empieza con 1, no con 0 como los arrays, y que el número
de parametros que requiere la consulta, debe ser igual al número de parámetros que le pasamos, uno más o uno menos
resultará en una excepción.