Archivo | 12:47 hrs.

OpenCms Content Relation Engine (CRE)

30 sep 2008

Una de las principales mejoras de OpenCms 7 es la incorporación del CRE (Content Relation Engine). Esta herramienta desarrollada por Michael Moossen permite establecer relaciones entre recursos del VFS (Vitual File System) de OpenCms.

Imaginemos lo siguiente:

Tenemos en el proyecto Offline un recurso A de OpenCms de tipo página html o jsp, en cuyo código hay un enlace a una imagen (recurso B) usando la etiqueta <img>.  Publicamos A.

¿Qué ocurre en versiones anteriores de OpenCms?

En el mejor de los casos si el recurso es de tipo página html el navegador mostraría la típica aspa roja indicando que no encuentra la imagen en el proyecto Online. En el peor de los casos, en jsp, suponiendo que la imagen fuese necesaria ya que leemos alguna propiedad (por ejemplo Description para mostrar el texto alternativo) el sistema podría dar un error 500.

¿Qué ocurre en el nuevo OpenCms si hemos usado CRE?

Pues que el sistema avisaría de que no hemos publicado la imagen y permitiría publicarla junto al recurso al que está asociada.

¿Qué ha cambiado en la base de datos de OpenCms?

Se ha añadido una nueva tabla llamada CMS_ONLINE_RESOURCE_RELATIONS para cada proyecto (sustituir ONLINE por OFFLINE) con los siguientes campos:

RESOURCE_SOURCE_ID: De tipo VARCHAR(36), contiene el id del recurso A.
RESOURCE_SOURCE_PATH: TEXT, contiene el path en ruta absoluta del recurso A.
RESOURCE_TARGET_ID: VARCHAR(36), contiene el id del recurso B.
RESOURCE_TARGET_PATH: TEXT, contiene el el path en ruta absoluta al recurso B.
RELATION_TYPE : INTEGER, es el tipo de relación que vamos a establecer.

¿Dónde se configuran las relaciones?

El el fichero opencms-workplace.xml. La configuración por defecto:


<default-preferences>
   <workplace-preferences>
      <workplace-generaloptions>
         ...
         <allowbrokenrelations>false</allowbrokenrelations>
         <publishrelatedresources>true</publishrelatedresources>
         ...
      </workplace-generaloptions>
   </workplace-preferences>
</default-preferences>

¿Cómo se usan las relaciones?

  1. Desde el menú flotante, opción relaciones: Podemos ver las relaciones de los recursos y asignar categorías.
  2. En jsp, usando el macro %(link.strong) o %(link.weak). En vez de usar

     <img src=”<cms:link>/imagenes/imagen.jpg</cms:link>” />

    usamos

     <img src=”<cms:link>%(link.strong:/imagenes/imagen.jpg)</cms:link>” />
  3. Usando categorías:
    • Primero hay que definir categorías, esto se hace creando una estructura de carpetas en /system/categories/ , por ejemplo:
      /system/categories/categoria1/
      /system/categories/categoria1/subcategoria11/
      /system/categories/categoria1/subcategoria12/
      /system/categories/categoria1/subcategoria13/
    • En el .XSD usamos el widget org.opencms.widgets.CmsCategoryWidget para mostrar el listado de categorías.
    • En jsp usamos org.opencms.file.collectors.CmsCategoryResourceCollector para listar los recursos pertenecientes a dicha categoría.
  4. Desde las clases de CmsObject (crear nuevas relaciones y leer o borrar existentes) y CmsRelationFilter.

Por último hay que destacar que Michael Moossen realizó una presentación muy interesante sobre CRE en los OpenCms Days. Las trasparencias que usó están disponibles en el siguiente enlace.

Trasparencias de Michael Moossen para la presentación de CRE en OpenCms Days.

Humor: Como reconocer una tecnología desfasada

23 sep 2008

En Viavansi, desde que migramos al Framework Perfecto, han ido quedando algunas tecnologías en desuso, viendo estas fotos, no hay  que ser ningún experto para saber cuales son esas tecnologías :p
Humor: Como reconocer una tecnología desfasada 2Humor: Como reconocer una tecnología desfasada struts :p
Humor: Como reconocer una tecnología desfasada 3Técnología desfasada. Humor
Humor: Como reconocer una tecnología desfasada 5Humor: Como reconocer una tecnología desfasada 5

Hudson. Parte 1 – Introducción.

17 sep 2008

¿Qué problemática resuelve?

En entornos en los que el desarrollo de software se realiza por un equipo o equipos en los que la evolución y mantenimiento del código se subdivide,  el coste de la integración entre las diferentes piezas de software tiende a aumentar, apareciendo conflictos y haciendo de la generación de distribuibles una ardua tarea.  Con idea de rebajar los costes adicionales provocados por la gestión del proceso de compilación, integración, empaquetado y generación de entregarles, aparecen herramientas de Integración Continua (CI) como Hudson.

¿ Por qué elegir Hudson?

Aunque es un productor reciente ha evolucionado rápidamente y actualmente es uno de los productos de referencia en el sector de las aplicaciones para Integración Continua de software, gracias a la simplicidad de su interfaz y lo sencillo que resulta el desarrollo de nuevos plugins. Prueba de este crecimiento es que actualmente es utilizado por un gran numero de proyectos entre los que se encuentra por ejemplo JBoss.

Para hacernos una idea los principales motivos que en nuestro caso nos hicieron elegir Hudson son:

  • Fácil instalación y uso. Como buen producto cuya utilidad es la de simplificarnos la vida, la instalación y ejecución de Hudson es realmente trivial. Es suficiente con descargar el  fichero hudson.war y ejecutarlo con “java -jar hudson.war” o bien desplegarlo en un servidor de aplicaciones.  Y esto no queda aquí, gracias a su sencilla interfaz, podemos tener nuestros primeros proyectos configurados en pocos minutos.
  • Un sistema de plugins fácilmente extensible. Lo que permite la instalación y creación de nuevos plugins de forma sencilla. Esto ha permitido que exista una gran cantidad de plugins disponibles y que sea muy sencilla la creación de plugins específicos. Como ejemplo, gracias a esta simplicidad pudimos crear un plugin específico para adaptar el comportamiento de Hudson a nuestras necesidades en solo unos días.
  • Un soporte completo de Maven, lo que nos ha permitido una migración rápida de todos nuestros proyectos al nuevo sistema.
  • Soporte para compilación distribuida basada en sistemas esclavos y maestros, para mejorar los tiempos de compilación y evitar la sobrecarga.
  • Soporte para múltiples equipos y grupos de proyectos, lo que nos permite la colaboración.
  • Es un Sistema completamente Software Libre.
  • Nos permite establecer un sistema de alertas a los desarrolladores sobre el estado de sus proyectos.
  • Permite la detección de actualizaciones realizadas en el SCM ( en nuestro caso Subversion) para generar de forma automática los nuevos empaquetados o a intervalos regulares.
  • Y el principal motivo. Tras estar utilizándolo en el departamento de I+D durante unos meses, el software no nos dio ni un solo problema, por lo que actualmente la mayoría de nuestros proyectos están ya bajo el control de Hudson.

Listado de proyectos gestionados por Hudson en Viavansi

Instalación

Como se comenta arriba, la instalación del software es realmente sencilla, y el único requisito es tener instalada una versión de Java reciente y opcionalmente un servidor de aplicaciones como por ejemplo Tomcat, si no se desea ejecutar sobre el servidor que trae embebido.

Para la instalación, en primer lugar necesitara descargar la última versión estable de  http://hudson.dev.java.net.

Si desea ejecutar Hudson en modo autónomo sera suficiente con ejecutar “java -jar hudson.war”y acceder con el navegador al puerto 8080 (http://localhost:8080) para poder utilizar Hudson.  Si lo desea, en la web oficial tambien puede encontrar los scripts para convertir hudson en un servicio.

Por otro lado la instalación más común de Hudson es su despliegue en un servidor de aplicaciones. Por ejemplo, para su despliegue en un Tomcat, bastara con copiar el fichero hudson.war dentro del directorio webapps.

Hudson utiliza un directorio especial para almacenar toda la configuración propia y de los proyectos que gestiona, por lo que no hay peligro de perdida de datos si posteriormente actualizamos a versiones más recientes del software. Por defecto el directorio es “.hudson”, localizado en el home del usuario, pero  es posible modificar este directorio estableciendo la variable de entorno HUDSON_HOME.

Primeros pasos

En Hudson cada proyecto de desarrollo es una tarea, por lo que para hacer que un proyecto este gestionado por hudson es suficiente con darle un nombre a la tarea y asignarle un tipo entre los disponibles (Build a maven2 project, Build a free-style software project, …)

Paso 1. Crear Un nuero proyecto (Job) en Hudson

En adelante a modo de ejemplo vamos a considerar un proyecto de tipo Maven 2. Una vez que tenemos el tipo de proyecto seleccionado, pasamos a la pantalla de configuración, en la que entre otras muchas opciones, tendremos que seleccionar el SCM en el que se encuentra proyecto(en nuestro caso Subversión), el pom.xml asociado al proyecto y el conjunto de goals que deseamos ejecutar. No entraremos en detalle a explicar cada uno de las opciones de configuración, ya que otra de las ventajas de Hudson es que ofrece ayuda en linea para todas sus opciones.

Una vez guardada la nueva tarea, ésta podrá ser ejecutada automáticamente si hemos configurado algún Build Triggers o lanzada de forma manual pulsando sobre Build Now, y una vez que la tarea esta en ejecución podremos ver en todo momento su estado y consola.

Administración

Desde el menú “Manage Hudson”, situado en la página principal, es posible acceder a todas las opciones de configuración. En particular desde la opción “Configure System” podremos acceder a todos los parametros generales como configuración del  SMTP, proxy, rutas a Maven, Ant, …
Y de nuevo para cada opción de configuración dispondremos de ayuda contextual que nos hara la vida mucho más facil.

Como ya hemos comentado, uno de los puntos fuertes de Hudson es su sistema y manejador de plugins, al que podremos  acceder desde la opción “Manage Plugins”. Esta opción nos permitirá añadir nuevas características (como herramientas de control de estilo, nuevos SCM soportados, …) directamente desde su interfaz.

Consola de administración de Hudson

La lista de todos los plugins está disponible en la siguiente dirección:  http://hudson.gotdns.com/wiki/display/HUDSON/Plugins

Conclusión

Hudson es un es un fantastico software para realizar las tareas de Integración Continua, y aunque aquí se han presentado solo las características básicas, a pesar de la simplicidad de su interfaz es un software altamente extensible y con un gran potencial.

Por otro lado, y aunque se enfoca principalmente hacia proyectos Java, Hudson permite gestionar proyectos de cualquier tipo, como por ejemplo proyectos C o Ruby on Rails, lo que lo hace muy recomendable enentorno heterogéneos.

En general, los productos de Integración Continua suelen ser complejos de manejar y gestionar, y como hemos visto, este no es el caso de Hudson, lo que resulta esencial si queremos convertir esta herramienta en una plataforma horizontal en nuestra empresa o grupo de desarrollo. Su facilidad de uso la ha demostrado durante el tiempo que llevamos utilizándolo, ya que no solo no ha requerido de personal especializado en este tipo de tareas para su gestión, sino que tampoco ha necesitado de una compleja explicación para que nuestros grupos de desarrollo empezaran a utilizarlo.

En resumen una herramienta para simplificarnos la vida a los desarrolladores que ofrece lo que promete.

Hudson.  Parde 2. Crea tus propios plugins

Curiosidad: La extraña implementación de NaN

11 sep 2008

Si probáis a evaluar esta expresión: Double.NaN==Double.NaN , comprobareis con extrañeza que Java retorna false.

Sin embarco,  si realizamos la comparación con el método equals, la cosa mejora  y todo se comporta como esperábamos.

¿Por qué ocurre esto? ¿Es un Bug?

Por extraño que nos pudiera parecer no es un error en la implementación, y podemos encontrar este comportamiento en otros lenguajes orientados a objetos como C#.

La implementación del tipo double en Java se basa en el estandard “IEEE 754 Binary Floating-Point Arithmetic standard“, que define que dos números con el valor NaN nunca son iguales, por lo que el operador “==” se definió de acuerdo con esta especificación. El problema surge al conciliar este comportamiento con la especificación del método equals para Object, por lo que se decidió sobrescribir el método equals de Double para adaptarlo al comportamiento de comparación de equivalencia esperado. Y aquí tenemos el conflicto “método equals” VS  “IEEE 754″.

Por lo que lo recomendado es utilizar el operador cuando deseas hacer comparaciones de acuerdo con el estándar para números flotantes, y utilizar el método equals si en tu código es deseable que un NaN sea igual a un NaN.
La solución.

Si no nos queremos complicar, en la mayoría de los casos bastara con utilizar :
Double.isNaN(varNum) ;

Servidores de Correo y su Integridad

11 sep 2008

No es noticia que te fastidien una aplicación que estaba corriendo bien porque otro servicio anda por ahí haciendo de las suyas y tú eres el último en enterarte.

Este caso es el que nos pasó recientemente, y lo peor de todo es que era en un entorno de producción.

El ejemplo en cuestión se trataba de un sistema que hacía uso de VIAFIRMA (nuestra plataforma de autenticación y firma digital). Cuando ésta enviaba los correos firmados digitalmente con el certificado instalado en producción, el “maravilloso” Exchange empezó a aplicar una regla para los correos cuyo destinatario fuese un correo externo.

Esta regla no era otra que interceptar el correo e inyectarle un disclaimer en el pie, del tipo “…este mensaje contiene información confidencial….”.

El catastrófico resultado en los correos que recibían los usuarios:


La firma no es válida
El mensaje incluye una firma digital, pero la firma no es válida.
La firma no coincide correctamente con el contenido del mensaje. El mensaje parece que ha sido manipulado después de que el remitente lo firmara. Usted no debería confiar en la validez de este mensaje hasta que verifique su contenido con el remitente.
Firmado por: "la empresa en cuestión"

Para hacer más difícil aún la detección del problema, y sin saber aún por qué, Exchange no siempre estaba aplicando esta regla y se escapaban correos sin el disclaimer.

Por tanto, en sistemas de información donde apuesten por la automatización del formato de los correos salientes desde el lado servidor siempre tendrán que tener en cuenta qué REGLAS de INTEGRIDAD podrían estar rompiendo.

Conexión JDBC con Oracle Cluster

09 sep 2008

Normalmente, para tareas de desarrollo hacíamos referencias a servidores Oracle que teníamos en nuestra propia red local o incluso en nuestro propio equipo (XE – express edition). Pero distinto es cuando pasamos una aplicación a producción y no tenemos a ese maravilloso DBA que te soluciona la papeleta cuando te encuentras con algo que se sale de lo normal en tus desarrollos.

En este caso ese maravilloso DBA estaba buscando papas en la playa, pero afortunadamente mr. google estaba ahí para solucionarnos la papeleta. Por ello, dejo aquí estos fragmentos de código como referencia rápida en futuras conexiones a Clusters de Oracle.

Llamada stand-alone (sin cluster ni nada raro)


url="jdbc:oracle:thin:@192.168.1.2:1521:DEV"

Llamada a un cluster de Oracle con Balanceo de Carga


url="
jdbc:oracle:thin:@(DESCRIPTION=
(LOAD_BALANCE=on)
(ADDRESS=(PROTOCOL=TCP)(HOST=myserver01)(PORT=1521))
(ADDRESS=(PROTOCOL=TCP)(HOST=myserver02)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=my_orasrv)))"

donde myserver01 y myserver02 son las IP virtuales de los nodos montados. Y así con todos los nodos que tuviera el cluster. Esto lo encontramos en el foro de Sun http://forums.sun.com.

Cluster sin Balanceo de Carga

url=”jdbc:oracle:thin:@(DESCRIPTION=
(ADDRESS=(PROTOCOL=TCP)(HOST=cluster_alias)(PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=my_orasrv)))”

donde invocan al cluster mediante su alias. Esta otra la encontramos en el foro de www.oraFAQ.com

Errores al utilizar Axis2 en Mac OS X Leopard

08 sep 2008

Al volver a utilizar un proyecto Java EE que invocaba a un servicio web mediante Axis, y que funcionaba correctamente en Windows, me he encontrado con un error bastante extraño que al invocar decía:

java.util.regex.PatternSyntaxException: Dangling meta character ‘*’ near index 0 *.local

Al principio pensaba que podría deberse a que la JVM no estuviese captando la configuración de salida por el proxy de la oficina, pero advertí que el error se daba tanto saliendo a través del proxy, como puenteándolo.

Pues bien, con un poco de Google, me encontré la solución.  Resulta que Tomcat dentro de Eclipse recupera la configuración de red de Leopard, y por defecto la variable nonProxyHosts (Omitir ajustes proxy para estos servidores y dominios) tiene el siguiente valor:

*.local, 169.254/16

Que no le gusta nada a Axis2 (se quejaba del *.local)… así que la cambiamos, eliminando esos valores. Para ello, nos vamos a Preferencias del Sistema -> Red -> Avanzado -> Proxies.

Y reiniciando el Tomcat, funcionó correctamente. Viva interné.

Arroz con Mango: Seam + Logger & Finder Interceptor + Lucene…

05 sep 2008

Bueno, tuve unas necesidades en un proyecto, así que comencé por hacer un GenericDAO, luego le agregué un FinderInterceptor el cual permite que cuando hagas findByWhatever te busque un NamedQuery que esté definido en la Entidad de JPA, como tip le agregué un logger el cual loguea cada método conjunto con los parámetros pasados, aqui comienzo a explicar uno por uno:

GenericDAO; interfase que define el contrato del JPA DAO el cual implementa de forma genérica las funcionalidades del mismo; esta interfase está interceptada por un Finder del cual hablaremos más tarde; tuve que usar Lucene ya que el proyecto corre sobre MSSQL Server y el bendito no soporta translate:


package com.viavansi.fdu.persistencia.DAO;
import java.io.Serializable;
import java.util.List;

import org.apache.lucene.queryParser.ParseException;

import com.viavansi.framework.core.persistencia.servicios.excepciones.ExcepcionPersistencia;/**
 * Interface for data access objects.
 *
 * <p>
 * Generic Interface DAO which provides the basic contracted operations for
 * every DAO; an implementation is also provided.
 *
 * @param <T>
 * The persistent class.
 * @param <PK>
 * The class of the primary key of the persistent class.
 */
@FinderExecutor
public interface GenericDao<T, PK extends Serializable> { /**
  * Merge.
  *
  * @param persistentObject
  */
 void update(T persistentObject) throws ExcepcionPersistencia; /**
  * Make the instance persistent.
  *
  * @throws ExcepcionPersistencia
  */
 void create(T newInstance) throws ExcepcionPersistencia; /**
  * Make the object transient.
  *
  * @param persistentObject
  * @throws ExcepcionPersistencia
  */
 void delete(T persistentObject) throws ExcepcionPersistencia; /**
  * Returns a persistent object specified by its key.
  *
  * @throws ExcepcionPersistencia
  */
 T read(PK id) throws ExcepcionPersistencia; /**
  * Returns all persistent entities.
  */
 List<T> findAll() throws ExcepcionPersistencia; /**
  * Resolves and executes a finder. <p/>
  * <p>
  * This implementation uses the short name of the type class, appending a .
  * and the method name so the name of the query to look up becomes
  * Pet1.findByName if the method is findByName and the type Pet1. <p/>
  * <p>
  * An other implementation would be useful as well that does not return a
  * list but a single object.
  */
 List<T> executeFinder(String method, Object[] queryArguments)
  throws ExcepcionPersistencia; List<T> searchByText(String expresion) throws ExcepcionPersistencia,
  ParseException;

Ahora definimos el DAO que contiene injectado el EntityManager:


package com.viavansi.fdu.persistencia.DAO;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.jboss.seam.annotations.In;

import com.viavansi.framework.core.excepciones.CodigoError;
import com.viavansi.framework.core.persistencia.servicios.excepciones.ExcepcionDatosNoEncontrados;
import com.viavansi.framework.core.persistencia.servicios.excepciones.ExcepcionPersistencia;

/**
 * Generic data access object.
 *
 * @param <T>
 * The persistent class.
 * @param <PK>
 * The class of the primary key of the persistent class.
 */
public abstract class GenericJpaDaoFDU<T, PK extends Serializable> implements
  GenericDao<T, PK> {

protected Class<T> type;

@In("fduPersistenceContext")
 protected EntityManager entityManager;

public EntityManager getEntityManager() {
  return entityManager;
 }

public void setEntityManager(EntityManager entityManager) {
  this.entityManager = entityManager;
 }

public void create(T newInstance) throws ExcepcionPersistencia {
  try {
  this.entityManager.persist(newInstance);
  } catch (Exception e) {
  throw new ExcepcionPersistencia(CodigoError.ERROR_NO_DEFINIDO, e);
  }
 }

public void delete(T persistentObject) throws ExcepcionPersistencia {
  try {
  this.entityManager.remove(persistentObject);
  } catch (Exception e) {
  throw new ExcepcionPersistencia(CodigoError.ERROR_NO_DEFINIDO, e);
  }
 }

public T read(PK id) throws ExcepcionDatosNoEncontrados {
  T t = (T) this.entityManager.find(type, id);
  if (t == null) {
  throw new ExcepcionDatosNoEncontrados(
  CodigoError.ERROR_DATOS_NO_ENCONTRADOS,
  "Datos no encontrados");
  }
  return t;
 }

public void update(T transientObject) throws ExcepcionPersistencia {
  try {
  this.entityManager.merge(transientObject);
  } catch (Exception e) {
  throw new ExcepcionPersistencia(CodigoError.ERROR_NO_DEFINIDO, e);
  }
 }

@SuppressWarnings("unchecked")
 public List<T> findAll() throws ExcepcionPersistencia {
  try {
  return entityManager.createQuery(
  "select obj from " + this.type.getName() + " obj")
  .getResultList();
  } catch (Exception e) {
  throw new ExcepcionPersistencia(CodigoError.ERROR_NO_DEFINIDO, e);
  }
 }

/**
  * Resolves and executes a finder. <p/>
  * <p>
  * This implementation uses the short name of the type class, appending a .
  * and the method name so the name of the query to look up becomes
  * Pet1.findByName if the method is findByName and the type Pet1. <p/>
  * <p>
  * An other implementation would be useful as well that does not return a
  * list but a single object.
  *
  * @throws ExcepcionPersistencia
  */
 @SuppressWarnings( { "unchecked" })
 public List<T> executeFinder(String method, Object[] queryArguments)
  throws ExcepcionPersistencia {
  try {
  String queryName = queryNameFromMethod(method);
  Query query = entityManager.createNamedQuery(queryName);
  for (int i = 0; i < queryArguments.length; i++) {
  query.setParameter(i, queryArguments[i]);
  }
  return query.getResultList();
  } catch (Exception e) {
  throw new ExcepcionPersistencia(CodigoError.ERROR_NO_DEFINIDO, e);
  }
 }

/**
  * Resolves the name of the named query.
  *
  * @param finderMethod
  * "findPerson, etc."
  * @return
  */
 protected String queryNameFromMethod(String finderMethod) {
  return type.getSimpleName() + "." + finderMethod;
 }

/*
  * (non-Javadoc)
  *
  * @see
  * com.viavansi.fdu.persistencia.DAO.GenericDao#findWhere(java.lang.String)
  */
 @SuppressWarnings("unchecked")
 public List<T> searchByText(String expression)
  throws ExcepcionPersistencia, ParseException {
  PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance()
  .getPropertyUtils();
  StringBuilder builder = new StringBuilder();
  boolean firstField = true;
  for (PropertyDescriptor descriptor : propertyUtils
  .getPropertyDescriptors(type)) {
  if (firstField) {
  firstField = false;
  } else {
  builder.append(" OR ");
  }
  builder.append(descriptor.getName() + ":" + expression);
  }
  FullTextEntityManager fullTextEntityManager = Search
  .createFullTextEntityManager(entityManager);
  QueryParser parser = new QueryParser("id", new ISOLatin1Analyzer());
  org.apache.lucene.search.Query luceneQuery = parser.parse(builder
  .toString());
  Query query = fullTextEntityManager.createFullTextQuery(luceneQuery,
  type);
  List result = query.getResultList();
  if (result.size() == 0) {
  throw new ExcepcionDatosNoEncontrados();
  }
  return result;
 }

/**
  *
  * @param <T>
  * @param expression
  * @param entityManager
  * @param type
  * @return
  * @throws ExcepcionPersistencia
  * @throws ParseException
  */
 @SuppressWarnings("unchecked")
 public static <T> List<T> searchByText(String expression,
  EntityManager entityManager, Class<T> type)
  throws ExcepcionPersistencia, ParseException {
  PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance()
  .getPropertyUtils();
  StringBuilder builder = new StringBuilder();
  boolean firstField = true;
  for (PropertyDescriptor descriptor : propertyUtils
  .getPropertyDescriptors(type)) {
  if (firstField) {
  firstField = false;
  } else {
  builder.append(" OR ");
  }
  builder.append(descriptor.getName() + ":" + expression);
  }
  FullTextEntityManager fullTextEntityManager = Search
  .createFullTextEntityManager(entityManager);
  QueryParser parser = new QueryParser("id", new ISOLatin1Analyzer());
  org.apache.lucene.search.Query luceneQuery = parser.parse(builder
  .toString());
  Query query = fullTextEntityManager.createFullTextQuery(luceneQuery,
  type);
  return query.getResultList();
 }

}

Nuestro 1er DAO para una Entidad; como verán, se apoya en el GenericDAO, y en su implementación:


package com.viavansi.fdu.persistencia.DAO;
import java.util.List;

import org.jboss.seam.annotations.Name;

import com.viavansi.fdu.persistencia.VO.ProcessInfoVO;

/**
 * @author gmedina
 *
 */
@Name("processInfoDAO")
public class ProcessInfoDAO extends GenericJpaDaoFDU<ProcessInfoVO, Long> {

public ProcessInfoDAO() {
  this.type = ProcessInfoVO.class;
 }

public List<ProcessInfoVO> findByProcessName(String name) {
  return null;
 }

public List<ProcessInfoVO> findByJbpmName(String jbpmName) {
  return null;
 }

public List<ProcessInfoVO> findByArea(String area) {
  return null;
 }

}

Vamos a mostrar nuestro Finder interceptor el cual sigue el Interceptor pattern y está soportado por Seam; muy sencillo, si el método se llama findByWhatever el busca un named query llamado findByWhatever, en tu implementación del DAO solo debes retornar null, si retornas algo entonces el finder se anula automáticamente:


package com.viavansi.fdu.interceptor;
import org.jboss.seam.annotations.intercept.AroundInvoke;
import org.jboss.seam.intercept.InvocationContext;

import com.viavansi.fdu.persistencia.DAO.GenericDao;

/**
 * @author gmedina
 *
 */
public class FinderInterceptor { /**
  *
  * @param invocation
  * @return
  * @throws Throwable
  */
 @AroundInvoke
 @SuppressWarnings("unchecked")
 public Object executeFinder(InvocationContext invocation) throws Throwable {
  String methodName = invocation.getMethod().getName();
  if (methodName.startsWith("findBy")) {
  GenericDao dao = (GenericDao) invocation.getTarget();
  Object result = invocation.proceed();
  return result == null ? dao.executeFinder(methodName, invocation
  .getParameters()) : result;
  } else
  return invocation.proceed();
 }

Nuestro persistence.xml con la configuración de Lucene que necesita:


<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
 <!--
  Conexión por defecto de la aplicación utilizando POOL de conexiones
 -->
 <persistence-unit name="fduPersistenceUnit"
  transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <jta-data-source>java:comp/env/jdbc/fdu</jta-data-source>
  <class>com.viavansi.fdu.persistencia.VO.UsuarioVO</class>
  <class>com.viavansi.fdu.persistencia.VO.MiembroVO</class>
  <class>com.viavansi.fdu.persistencia.VO.RolVO</class>
  <class>com.viavansi.fdu.persistencia.VO.ProcessInfoVO</class>
  <properties>
  <property name="hibernate.show_sql" value="true" />
  <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
  <property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />
  <!--
  Configuración para el soporte de prefijos en Hibernate. Estrategia
  para generación de nombres de tablas asociadas a anotaciones Table
  EJB3.0.
  -->
  <property name="hibernate.ejb.naming_strategy"
  value="com.viavansi.framework.persistencia.jpa.NamingStrategy" />
  <property name="hibernate.search.default.directory_provider"
  value="org.hibernate.search.store.FSDirectoryProvider" />
  <property name="hibernate.search.default.indexBase"
  value="/Java/lucene/fdu/app" />
  </properties>
 </persistence-unit> </persistence>

Finalmente nuestro DAO el cual tiene anotaciones de JPA y Hibernate Search/Lucene


package com.viavansi.fdu.persistencia.VO;
import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.search.annotations.Analyzer;
import org.hibernate.search.annotations.DateBridge;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Resolution;
import org.hibernate.search.annotations.Store;

import com.viavansi.fdu.persistencia.DAO.ISOLatin1Analyzer;

/**
 * @author gmedina
 *
 */
@NamedQueries( {
  @NamedQuery(name = "ProcessInfoVO.findByProcessName", query = "from ProcessInfoVO processInfo where processInfo.processName = ?"),
  @NamedQuery(name = "ProcessInfoVO.findByJbpmName", query = "from ProcessInfoVO processInfo where processInfo.jbpmName = ?"),
  @NamedQuery(name = "ProcessInfoVO.findByArea", query = "from ProcessInfoVO processInfo where processInfo.area = ?") })
@Indexed
@Analyzer(impl = ISOLatin1Analyzer.class)
@Entity
@Table(name = "`${PREFIX_FDU}PROCESS_INFO`")
public class ProcessInfoVO implements Serializable {

private static final long serialVersionUID = 1570433106072090149L;

private Long id;
 private String processName;
 private String jbpmName;
 private Date startDate;
 private Date dueDate;
 private boolean active;
 private String area;

/**
  * @return the id
  */
 @Id
 @DocumentId
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 public Long getId() {
  return id;
 }

/**
  * @param id
  * the id to set
  */
 public void setId(Long id) {
  this.id = id;
 }

/**
  * @return the processName
  */
 @Field(index = Index.TOKENIZED, store = Store.NO)
 @Column(name = "PROCESS_NAME", unique = true, nullable = false, insertable = true, updatable = true, length = 255)
 public String getProcessName() {
  return processName;
 }

/**
  * @param processName
  * the processName to set
  */
 public void setProcessName(String processName) {
  this.processName = processName;
 }

/**
  * @return the jbpmName
  */
 @Field(index = Index.TOKENIZED, store = Store.NO)
 @Column(name = "JBPM_NAME", unique = true, nullable = false, insertable = true, updatable = true, length = 255)
 public String getJbpmName() {
  return jbpmName;
 }

/**
  * @param jbpmName
  * the jbpmName to set
  */
 public void setJbpmName(String jbpmName) {
  this.jbpmName = jbpmName;
 }

/**
  * @return the startDate
  */
 @Field(index = Index.UN_TOKENIZED)
 @DateBridge(resolution = Resolution.DAY)
 @Temporal(TemporalType.DATE)
 @Column(name = "START_DATE", unique = false, nullable = true, insertable = true, updatable = true)
 public Date getStartDate() {
  return startDate;
 }

/**
  * @param startDate
  * the startDate to set
  */
 public void setStartDate(Date startDate) {
  this.startDate = startDate;
 }

/**
  * @return the dueDate
  */
 @Field(index = Index.UN_TOKENIZED)
 @DateBridge(resolution = Resolution.DAY)
 @Temporal(TemporalType.DATE)
 @Column(name = "DUE_DATE", unique = false, nullable = true, insertable = true, updatable = true)
 public Date getDueDate() {
  return dueDate;
 }

/**
  * @param dueDate
  * the dueDate to set
  */
 public void setDueDate(Date dueDate) {
  this.dueDate = dueDate;
 }

/**
  * @return the active
  */
 @Column(name = "ACTIVE", unique = false, nullable = false, insertable = true, updatable = true)
 public boolean isActive() {
  return active;
 }

/**
  * @param active
  * the active to set
  */
 public void setActive(boolean active) {
  this.active = active;
 }

/**
  * @return the area
  */
 @Field(index = Index.TOKENIZED, store = Store.NO)
 @Column(name = "AREA", unique = false, nullable = false, insertable = true, updatable = true, length = 100)
 public String getArea() {
  return area;
 }

/**
  * @param area
  * the area to set
  */
 public void setArea(String area) {
  this.area = area;
 }

/*
  * (non-Javadoc)
  *
  * @see java.lang.Object#hashCode()
  */
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  return result;
 }

/*
  * (non-Javadoc)
  *
  * @see java.lang.Object#equals(java.lang.Object)
  */
 @Override
 public boolean equals(Object obj) {
  if (this == obj) {
  return true;
  }
  if (obj == null || !(obj instanceof ProcessInfoVO)) {
  return false;
  }
  ProcessInfoVO other = (ProcessInfoVO) obj;
  if (id == null) {
  if (other.id != null) {
  return false;
  }
  } else if (!id.equals(other.id)) {
  return false;
  }
  return true;
 }

}

Nota: Lucene necesita construir un indice inicial, les pegaré un pedazo de código de como hacer esto:


@SuppressWarnings("unchecked")
 public void reIndexLuceneDB() {
  FullTextEntityManager fullTextEntityManager = Search
  .createFullTextEntityManager(entityManager);
  Class[] entityClasses = { UsuarioVO.class, RolVO.class,
  ProcessInfoVO.class };
  for (Class entityClass : entityClasses) {
  for (Object object : entityManager.createQuery(
  "select obj from " + entityClass.getName() + " obj")
  .getResultList()) {
  fullTextEntityManager.index(object);
  }
  }
 }

Bueno, he pegado tanto código que explicarlo todo en un solo post es difícil, asi que manden sus preguntas.

Que lo disfruten.

Guido.

www.viafirma.com

03 sep 2008

Debido al gran interés que esta generando Viafirma, nuestra plataforma de Autenticación y Firma Digital en su versión comercial, hemos creado viafirma.com, en la que se recoge toda la información relacionada con las versiones comerciales de la plataforma ( Versión Standard y Advanced).

 ¿Por qué una versión comercial de Viafirma?

En la mayoría de los casos, nuestros clientes desean utilizar Viafirma sobre aplicaciones que no son compatibles con GPL; además, nos exigen soporte técnico, mantenimiento, plazos para nuevos desarrollos, tiempos de respuesta, una documentación completa, y en resumen, unas garantías que no ofrece la versión GPL. Por este motivo y para poder financiar los nuevos desarrollos a los que nos estamos enfrentando, hemos creado el portal  viafirma.com.

¿Implica esto un cambio de filosofía de la versión Software Libre?

No. Tenemos tanto que agradecer a la comunidad que nuestra intención sigue siendo apostar por una plataforma libre . De hecho acabamos de liberar como GPL la nueva versión de Viafirma 1.3.2, si lo desean pueden consultar la lista completa de nuevas funcionalidades en viafirma.org.

 

Nuevo cocktail: Windows Vista con una pizca de iTunes y Quicktime, precio recomendado “Error 46″

03 sep 2008

Hola buenas a tod@s,

esta vez os escribo para comentaros un quebradero de cabeza más que me ha dado Windows Vista e iTunes (con Quicktime), un par de problemas:

1) Cada vez que arranco el iTunes, se vuelve a reinstalar.

2) Se produce un error de ActiveX al abrir el Quick Time Player: Error 46

Para el primero de los problemas, la solución fue un poco drástica: eliminar el acceso directo del iTunes que tenemos en el Escritorio y crear un nuevo acceso directo al iTunes.exe que tenemos en nuestra carpeta de “%Program Files%/iTunes”, lo colocamos en nuestro Escritorio y aquí no ha pasado nada :-D

Para solucionar el segundo error ya tenemos que hacer un par de cosillas más.

Primero os comento cuál es la causa de este error:
Parece ser que al instalar QuickTime Player las claves introducidas en el registro  de Windows Vista no adquieren permisos de acceso correctos. Exactamente lo que ocurre es que nuestro usuario (sea Administrador o no) no tiene acceso ni siquiera de lectura a dichas claves.

Esto os lo cuento yo y queda muy bonito ¿verdad?, pero ¿cómo podeis comprobar esto vosotros mismos?
Pues descárguense el programa Process Monitor  de Microsoft. Tras instalarlo y dejar abierta la aplicación, intenten abrir QTPlayer y miren los errores que se muestran en el Proces Monitor producen al intentar abrir el QuickTime Player, seguro les aparecerá “ACCESS DENIED” a distintas claves del registro (podeis filtrar las lineas que se muestran).

Pues bien, parece que tenemos claro ahora cuál es el problema, ¿cómo “repcuperar” los permisos de lectura sobre dichas claves?

Si hacemos lo típico, ejecutar “regedit” e intentar modificar los permisos manualmente veremos que no tenemos autorización para realizar estos cambios en esas claves afectadas. ¡Vaya chasco!

Bien, busquemos otra forma de modificar estos permisos. Usemos ahora otro programa de Microsoft llamado SubInACL: esta es una herramienta de administración, para modificar y consultar distintos parámetros del sistema como claves del registro, servicios, etc., pero vamos al grano.

Con este programa tenemos ahora que modificar los permisos asignados a estas claves que no pudimos modificar con “regedit.exe” .

Una vez descargado e instalado, debemos copiar el script que contiene Script.zip y descomprimir el .cmd en la carpeta”%Program Files%\Windows Resource Kits\Tools“, que es donde se debe haber instalado SubInACL.
Este script es parecido a otros que aparecen en algunos foros, pero este en concreto contiene algunas correcciones realizadas por mi y adaptado a Windows Vista en Español, no en Inglés. Recomiendo hacer una copia de seguridad del registro antes de ejecutar nada, siempre es conveniente trabajar sobre seguro.

A continuación, abramos una terminal de consola (Inicio / Ejecutar / cmd.exe para los no iniciados) y vayamos a la carpeta donde copiamos el script. Ejecutemos en la consola “resetSpanish.cmd” y veamos en el log mostrado que no salgan errores:

Done:        X, Modified        X, Failed        0, Syntax errors        0

lo importante es que “Failed”  y “Syntax errors” salgan a cero.

Este script hay que lanzarlo con un usuario Administrador y recomiendo desactivar el sistema de protección de cuentas de que dispone Windows Vista (Panel de Control / Cuentas de Usuario / Activar o desactivar el control de cuentas de usuario),
una vez lanzado el script podeis volverlo a activar.

Una vez hecho esto, podremos abrir “QuickTime Player” sin obtener el dichoso error 46.

Y recordad: siempre tenemos una alternativa en LINUX …

Namasté.