Archive for February, 2008

Seam Generator (seam-gen)

La distribución de Seam incluye una aplicación de linea de comandos para crear y mantener aplicaciones Seam.
Esta herramienta es capaz de generar un proyecto Seam que contenga el esqueleto de una aplicación, incluyendo configuración y librerías. Esta es la forma más rápida de empezar un desarrollo con Seam. Genera una aplicación en blanco con facelets, drools (seguridad), página de login y bienvenida, configuración de la base de datos… así como scripts para su compilación, empaquetación y despliegue.
Las opciones que ofrece esta herramienta son las siguientes:

  • setup: Sirve para configurar el proyecto. Te pregunta entre otras cosas el directorio de instalación de JBoss, si quieres desplegar un archivo war (sin EJBs) o un ear (con EJBs), el nombre del paquete donde irán las clases, y parámetros de la conexión con la base de datos.
  • new-project: Crea un proyecto para eclipse con todas las dependencias y la configuración básica a partir de los datos proporcionados a través del setup.
  • update-project: Actualiza el proyecto con las últimas dependencias.
  • delete-project: borra el proyecto
  • deploy: Despliega el proyecto y el datasource en JBoss
  • undeploy: Replega el proyecto y el datasource.
  • explode: Desplega el proyecto y el datasource en JBoss como una estructura de ficheros, sin empaquetar.
  • restart: Resetea el proyectodesplegado con ”explode”
  • unexplode: Replega el proyecto y el datasource desplegado con ”explode”
  • new-action: Crea un nuevo Stateless Session Bean con su correspondiente interface y sus anotaciones.
  • new-form: Crea un nuevo Stateful Session Bean con su correspondiente interface y sus anotaciones.
  • new-conversation: Crea un nuevo Stateful Session Bean con su correspondiente interface y sus anotaciones. Añade anotaciones y esqueletos de métodos para trabajar con Tasks. También crea una clase de Test que puede usarse para simular el ciclo de petición/respuesta de JSF.
  • new-entity: Crea un nuevo Entity Bean con sus anotaciones.
  • generate-entities: Genera Entity Beans a partir de un modelo existente en la base de datos

Aunque seamgen está pensado para trabajar principalmente con eclipse y JBoss AS, también puede configurarse para crear aplicaciones para netbeans y glassfish, que tiene diferentes archivos de configuración.
Además puedes modificarla facilmente, ya que no deja de ser un simple script que trabaja con ant.

Al usar la opción new-project se crea un nuevo proyecto con una estructura de directorios que a primera vista parece algo compleja. Sin embargo vamos a ver como cada carpeta tiene una función o
definida.

  • bootstrap: Contiene el JBoss Embeddable EJB 3.0 container, para poder desplegar EJBs fuera del servidor de aplicaciones.
  • classes: Contienen los archivos de bytecodes (.class) de los componentes definidos por el desarrollador.
  • dist: Contiene los archivos empaquetados (ear, war, jar) que serán desplegados en el servidor de aplicaciones.
  • exploded-archives: Contiene la estructura de directorios que se puede desplegar directamente en el servidor de aplicaciones.
  • lib: Contiene todas las librerías(.jar) con las dependencias necesarias para la compilación y ejecución de la apliación.
  • nbproject: Contiene los archivos necesarios para trabajar en el proyecto con netbeans.
  • resources: Contiene todos los archivos de configuración.
  • src: Contiene el código fuente de los componentes definidos por el desarrollador, separados en tres bloques: entidades, acciones y test.
  • test-build: Esta carpeta está destinada a almacenar los test de integración.
  • view: Contiene todos los elementos relativos a la vista, como archivos css, imágenes, archivos xhtml…

Leave a Comment

Archivos de configuración de Seam

JBoss Seam intenta minimizar todo lo posible la cantidad de xml necesario para configurar la aplica-
ción.
Sin embargo, parte de la configuración es preferible tenerla en archivos xml, como reglas de navegación o configuración de componentes del framework, para así aislarla del código java y poderla
cambiar sin recompilar. En otros casos, como los descriptores, es obligatorio tener los archivos xml para cumplir las especificaciones.

Vamos a ver los archivos de configuración que podemos encontrarnos en un proyecto Seam, su localización y cometido.

  • *-ds.xml: Contiene los par ́metros de conexión a la base de datos. Este archivo debe desplegarse en el directorio “deploy” de JBoss
  • seam.properties: Este archivo debe estar presente obligatoriamente (aunque esté vacío) en la carpeta raíz del archivo jar que va a desplegarse o en la carpeta classes del archivo war.
    Este archivo se usa para configurar las propiedades de los componentes.
    components.xml: Puede aparecer en tres lugares; el directorio “META-INF” del archivo jar, el directorio “WEB-INF” del archivo war, o cualquier directorio que contenga clases anotadas con @Name. En él se declaran y configuran componentes.
  • components.properties: Especifica el valor de variables que pueden usarse en el archivo components.xml, como @debug@. Estas variables se reemplazan cuando se despliega la aplicación.
  • persistence.xml: Se sitú en el directorio “META-INF”" del archivo jar y en él se declaran las unidades de persistencia.
  • security.drl: En él se declaran las reglas de autorización del subsistema Drools. Se coloca en la raíz del archivo ear o en el directorio “classes” en el caso de desplegar en un archivo war.
  • application.xml: Es el descriptor de despliegue de Enterprise Application Archives y es un archivo estándar en aplicaciones java 2 EE. Se sitúa en el directorio “META-INF” del archivo ear y en él se declaran los módulos de la aplicación y las dependencias.
  • ejb-jar.xml: Es el descriptor de despliegue est ́ndar de la especificació EJB. Se coloca en el directorio META-INF del archivo jar, y en Seam contiene la declaración del SeamInterceptor.
  • jboss-app.xml: Es el descriptor de aplicación específico de JBoss y se sitúa en el directorio “META-INF” del archivo ear.
  • faces-config.xml: Es el archivo de configuración central de JavaServer Faces. Se encuentra en el directorio “WEB-INF” del archivo war.
  • pages.xml: Especifica las reglas de navegación. Se encuentra en el directorio “WEB-INF” del archivo war.
  • web.xml: Es el descriptor Web estádar especificado en J2EE. Se encuentra en el directorio “WEB-INF” del archivo war.
  • import-*.sql: Se coloca en la raíz del archivo jar. Puede contener código sql que será ejecutado cada vez que se despliegue la aplicación

Una aplicación Seam típica tendrá la siguiente estructura de archivos

my-application.ear/
    jboss-seam.jar
    jboss-el.jar
    jboss-el-api.jar
    META-INF/
        MANIFEST.MF
        application.xml
    my-application.war/
        META-INF/
            MANIFEST.MF
        WEB-INF/
            web.xml
            components.xml
            faces-config.xml
            lib/
                jsf-facelets.jar
                jboss-seam-ui.jar
        login.jsp
        register.jsp
        ...
    my-application.jar/
        META-INF/
            MANIFEST.MF
            persistence.xml
        seam.properties
        mi/
            paquete/
                myapplication/
                    Clase1.class
                    Clase2.class
                    Clase3.class

Leave a Comment

Cuándo se crean y destruyen las conversaciones

En Seam podríamos definir una conversación como la unidad de trabajo desde el punto de vista del usuario.
La noción de Seam de conversació es el conjunto de tres ideas:

  • La idea de workspace. Cada usuario de la aplicación debe tener su propio espacio de trabajo, sin interferir en la actividad del resto de usuarios.
  • La idea de transacción. La mayoría de los frameworks están construidos siguiendo una arquitectura sin estado, incapaz de manejar correctamente los contextos de persistencia extendidos, dando lugar a fallos como la famosa LazyLoadException.
  • La idea de tarea en un workflow.

Uniendo y respetando estos tres conceptos, obtenemos el modelo conversacional de Seam, que nos
permite construir aplicaciones eficientes de más ráidamente.
Este modelo sigue las siguientes reglas para crear o destruir el contexto de conversación:

  • Siempre hay una conversación activa durante la petición, el proceso de validación, la actualización de los modelos de datos, la invocación de la aplicación o la respuesta del ciclo de vida de JSF.
  • Con cada petición JSF, Seam intenta restaurar una ”conversación larga” (longrunning conversation) que existiese previamente. Si no la encuentra crea una conversación nueva.
  • Cuando se encuentra una anotacion @Begin en un méodo que se ejecute, la conversación activa se promociona a conversación larga.
  • Cuando se encuentra una anotacion @End en un método que se ejecute, cualquier conversación larga se degrada a conversación normal.
  • Tras cada respuesta JSF, Seam almacena los contenidos de la conversación larga si existe, y si no destruye el contenido de la conversación normal.
  • Cualquier petición JSF propaga el contexto de conversación, y por defecto las peticiones normales no lo propagan.
  • Si una petición JSF es redirigida, Seam almacena y restaura posteriormente la conversación para preservar su contenido.

Comments (2)

Bijection

Para entender el funcionamiento básico de JBoss Seam debes familiarizarte con dos conceptos: Contexto y Componente.

Un componente es un objeto con estado (normalmente stateful session beans). Una instancia de un componente vive en un contexto y se le asigna un nombre en dicho contexto.
Para asociar la instancia de un componente al nombre de dicho componente en un contexto Seam proporciona el mecanismo de Bijection

          Bijection = Injection + Outjection

El mecanismo de injection permite a un componente A obtener de un contexto una referencia a una
instancia de un componente B, haciendo que el contenedor de aplicaciones ”inyecte” el componente B en una variable del componente A.
El mecanismo de outjection permite que un componente B esté disponible en un contexto para poder ser inyectado en un componente A.
Es decir, mediante outjection se toma una instancia de un componente y se deposita en un contexto
y mediante injection se toma una instancia de un componente de un contexto y se asocia a una
variable de otro componente.

A diferencia del mecanismo de Injection, el mecanismo de Bijection es:

  • contextual: Es usada para asociar componentes con estado desde diferentes contextos.
  • bidireccional: Los componentes pueden ser depositados en el contexto o tomados de él.
  • dinámico: Como los valores de las variables de los contextos cambian con el tiempo y los
    componentes tienen un estado definido, la bijection sucede cada vez que el componente es
    invocado.

Comments (1)

Qué es JBoss Seam ?

JBoss Seam es un framework que integra y unifica los distintos standars de la plataforma Java EE 5.0, pudiendo trabajar con todos ellos siguiendo el mismo modelo de programación.
Ha sido diseñado intentado simplificar al máimo el desarrollo de aplicaciones, basando el diseño en Plain Old Java Objects (POJOs) con anotaciones. Estos componentes se usan desde la capa de persistencia hasta la de presentación, poniendo todas las capas en comunicación directa.

El núcleo principal de Seam está formado por las especificaciones Enterprise JavaBeans 3 (EJB3) y JavaServer Faces (JSF).
A grandes rasgos podemos definir EJB3 como una arquitectura para un sistema transaccional (como bases de datos) de objetos distribuidos basado en componentes que permite construir aplicaciones portables, reusables y escalables.
JSF es un framework de la capa de presentación que define componentes para el interfaz gráfico y “managed beans” para la lógica de la aplicación que interactúan a travé de un sistema de eventos.

Sin embargo, estos frameworks tienen algunas limitaciones y no han sido concebidos para trabajar juntos (esto pretende resolverse con la futura especificación web beans ); tienen distinto tipo de configuraciones (JSF usa archivos XML mientras que EJB3 usa anotaciones), distinto ciclo de vida y no pueden comunicarse directamente a nivel de framework.
Para hacerlos cooperar necesitaríamos escribir “clases fachada” y multitud de código de relleno que se encargase de pasar las llamadas de una capa de la aplicación a otra. Ahí es donde entra en juego Seam.
Seam elimina la barrera existente entre estas tecnologías, permitiendo usar EJBs directamente como “backing beans” de JSF y lanzar o escuchar eventos web.

Leave a Comment

eclipse tips

Para aquellos que, como yo, utilizáis eclipse (o cualquiera de sus variantes) como IDE de desarrollo, aquí os dejo una serie de truquillos, explicaciones y atajos de teclado interesantes. Seguro que ya conocéis la mayoría, pero allá van.

  • Toggle Comment: (ctrl+shift+C) Comenta/Descomenta la linea actual o lineas seleccionadas.
  • Generate Element Comment: (shift+alt+J) Genera las lineas de comentario del elemento sobre el que está el cursor.
  • Organize Imports: (ctrl+shift+O) Importa las clases que necesitas y elimina las que no.
    Es aplicable a una clase, paquete o a todo el proyecto.
  • Correct Indentation: (ctrl+I) corrige la identación (tabulación de lineas) de la linea actual o lineas seleccionadas.
  • Format: (ctrl+shift+F) Esto te deja el código bonito. Deja los espacios correspondientes, saltos de línea, identación… según las convenciones de java.
    Pero lo mejor, es que puedes definir todas esas cosas a tu gusto. Ve al menu de Windows->Preferences->Java->Code Style->Formatter. Seleccionando “Edit” accedes a un menu donde puedes cambiar el aspecto del código sobre los comentarios, saltos de linea, sentencias de control, declaración de variables…
    Es aplicable a una clase, paquete o a todo el proyecto.
  • Clean Up: Esta herramienta es realmente poderosa. Es parecida a “Format”. Permite eliminar/añadir el modificador this, paréntesis, llaves, anotaciones automáticas y otras cosillas.
    Es aplicable a una clase, paquete o a todo el proyecto.
  • Generate Getters/Setters: Genera métodos get/set estilo JavaBean para los campos que queramos.
  • Generate Constructor using Fields: Genera el constructor de la clase con los campos definidos, en plan JavaBean.
  • Surround With: (shift+alt+Z) Mete las lineas seleccionadas en un bloque if, while, try-catch, etc.

Leave a Comment

Cómo recuperar archivos borrados con eclipse

Si metes archivos en la carpeta WEB-INF/classes de tu aplicación J2EE que no estos quedan añadidos al classpath de la aplicación, por lo que la gente es muy propensa a meter directamente en esa carpeta los archivos de mensajes (x ej: messages.properties).

El inconveniente de hacer esto es que (por lo menos en eclipse) al recargar el proyecto, el IDE tiende a eliminar todos los archivos de la carpeta WEB-INF/classes incluyendo nuestros preciados archivos de mensajes.

Para evitar esto, una buena práctica es poner dichos archivos en la carpeta de código fuente (normalmente llamada ’src’). Si por algún motivo quieres tenerlos en la carpeta WEB-INF/classes, siempre puedes marcar la opción ‘Allow output folders for source folders’ en la pestaña Project -> Java Build Path ->Source.

Si este consejo llega tarde para tí, o simplemente quieres recuperar un archivo eliminado, no te desesperes. Eclipse guarda internamente una copia de todos los archivos (.java, .properties, .xml, imágenes…) y carpetas de tu proyecto.

Haz click derecho en la carpeta contenedora de tus archivos eliminados y elige la opción ‘Restore from Local History‘. Con un poco de suerte aparecerán los archivos que buscabas. Selecciona los que quieras y pulsa el botón ‘Restore’

En alguna ocasión se me han borrado accidentalmente

Leave a Comment

Cómo limitar el numero de caracteres que muestra un h:outputText

En alguna ocasión, trabajando con JSF, he necesitado limitar el número de caracteres que se muestran en un elemento de salida de texto, como un outuputText.Una forma fácil de hacerlo es implementar un Converter personalizado.

En primer lugar necesitas declarar una clase que implemente la interfaz javax.faces.convert.Converter e implementar los métodos getAsObject y getAsString.
Aquí tienes la implementación del Converter: StringLimiterConverter

package es.neodoo.control.jsf;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

public class StringLimiterConverter implements Converter {
    private static final String LIMIT_PARAMETER_NAME = "limit";
    private static final int DEFAULT_LIMIT = 5;

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        return limit(value, getLimitAttribute(component));
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (! (value instanceof String))
            return null;
        else {
            return limit(value.toString(), getLimitAttribute(component));
        }
    }

    private int getLimitAttribute(UIComponent component) {
        Object att = component.getAttributes().get(LIMIT_PARAMETER_NAME);
        if (att == null)
            return DEFAULT_LIMIT;
        else
            return Integer.parseInt((String)component.getAttributes().get(LIMIT_PARAMETER_NAME));
    }

    private String limit(String s, int limit) {
        String limited = s;
        if (! (s.length() <= limit))
            limited = s.substring(0, limit);
        return limited;
    }
}

Después registra el converter en el archivo faces-config.xml

<faces -config>
          <converter>
               </converter><converter -id>stringLimiterConverter</converter>
               <converter -class>tes.jsf.converter.StringLimiterConverter</converter>
         
     </faces>

Y ya puedes usarlo a través de la etiqueta de JSF

<h :o utputText value="En un lugar de la Mancha...">
          <f :converter converterId="stringLimiterConverter" />
          <f :attribute name="limit" value="6" />
     </h>

Con el atributo <f:attribute name="limit" value="6" /> podemos indicarle el número de caracteres que queremos que nos muestre

Leave a Comment

Cómo iterar sobre un Set en un dataTable

El otro día iba a crear un h:dataTable con los valores almacenados en un java.util.Set, y me llevé una sopresa al comprobar que no se podía.

La razón es que el componente UIData (que es vital para el funcionamiento de las etiquetas h:dataTable y ui:repeat entre otras ) está basado en índices numéricos, al igual que la interfaz java.util.List.
La interfaz java.util.Set está basada en parejas de elementos clave/valor, por lo que no se lleva bien con el componente UIData.

Una solución para poder usar nuestro Set con dataTables es convertirlo previamenta a un List.
Para ello podemos usar un ELResolver. Ya expliqué cómo hacerlo en otro post

Leave a Comment

Cómo implementar un ELResolver personalizado

El Expression Language de JSF nos permite acceder a propiedades de los objetos siempre que estas esten disponibles mediante getters.

Esto está muy bien, pero en ocasiones lo que queremos obtener es un objeto que solo está accesible a través de un método que no es un getter, como por ejemplo el tamaño de una java.util.Collection disponible a través de su método collection.size(), o los valores de un java.util.Map disponibles a través del método values().

Para ello podemos realizar nuestra propia implementación de la clase javax.el.ELResolver.
Esta clase es la encargada de evaluar las ELs (las expresiones que van entre #{}) y realizar las acciones correspondientes.
Sustiye a los antiguos PropertyResolver y VariableResolver, que están marcados como obsoletos (deprecated) en la especificación 1.2 de JSF (que es la usada en la versión 5 de Java EE).

Para usar nuestro propio ELResolver solo deboemos seguir tres sencillos pasos:

  • 1) Implementar nuestra clase que extienda de javax.el.ELResolver
    Puedes descargarla aquí: MyELResolver
    Necesitarás añadir al classpath de compilación el archivo el-api.jar
  • package tes.jsf;

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;

    import javax.el.ELContext;

    public class MyELResolver extends javax.el.ELResolver {

        @Override
        public Class getCommonPropertyType(ELContext context, Object base) {
            return null;
        }

        @Override
        public Iterator getFeatureDescriptors(ELContext context, Object base) {
            return null;
        }

        @Override
        public Class getType(ELContext context, Object base, Object property) {
            return null;
        }

        @Override
        public Object getValue(ELContext context, Object base, Object property) {
            if (base instanceof Collection) {
                return resolveInCollection(context, (Collection)base, property);
            } else if (base instanceof Map) {
                return resolveInMap(context, (Map)base, property);
            } else {
                return null;
            }
        }

        @Override
        public boolean isReadOnly(ELContext arg0, Object arg1, Object arg2) {
            return true;
        }

        @Override
        public void setValue(ELContext arg0, Object arg1, Object arg2, Object arg3) {
        }

        private Object resolveInCollection(ELContext context, Collection base, Object property) {
            if (property.equals("size")) {
                context.setPropertyResolved(true);
                return base.size();
            } else if (property.equals("toMap")) {
                context.setPropertyResolved(true);
                return collectionToMap(base);
            } else if (property.equals("toList")) {
                context.setPropertyResolved(true);
                return new ArrayList(base);
            }else {
                return null;
            }
        }

        private Object resolveInMap(ELContext context, Map base, Object property) {
            if (property.equals("size")) {
                context.setPropertyResolved(true);
                return base.size();
            } else if (property.equals("values")) {
                context.setPropertyResolved(true);
                return base.values();
            } else if (property.equals("keySet")) {
                context.setPropertyResolved(true);
                return base.keySet();
            } else if (property.equals("entrySet")) {
                context.setPropertyResolved(true);
                return base.entrySet();
            } else {
                return null;
            }
        }

        private Map<object> collectionToMap(Collection col) {
            Map</object><object> map = new HashMap</object><object>();
            for (Object obj : col) {
                map.put(obj, obj);
            }
            return map;
        }
    }

  • 2) Declarar el ELResolver en el archivo faces-config.xml
    La declaración debe hacerse dentro de la seccion <application>.
    <el-resolver>tes.jsf.MyELResolver</el-resolver>
  • 3) Usar las nuevas posibilidades del Language Expression
    • El tamaño de mi Collection es: #{myCollection.size}
    • Puedes usarlo para rellenar tus combos:
    •      <h:selectOneMenu>
                <f:selectItems value="#{myCollection.toMap}" />
           <h:selectOneMenu>
    • Para asociar tu java.util.Set con un dataTable
           <h:dataTable value="#{mySet.toList}" var="entry">
                ...
           </h:dataTable>
    • Para iterar sobre las entradas de un Map
    •      <ui:repeat value="#{myMap.entrySet.toList}" var="entry">
                #{entry.key} : #{entry.value} <br />
           </ui:repeat>
    • Y para todo lo que se te ocurra añadir

Comments (3)