Archive Page 2

26
Jun
08

versión móvil

Acabamos de hacer pública nuestra versión para móviles. Son tres vistas, la caja de búsqueda, los resultados de la búsqueda y la página de servicio.

Para hacer las páginas más ligeras hemos eliminado todas las imágenes y opiniones. De cada servicio dejamos sólo nombre, dirección, teléfono y el mapa descargado como una imagen.

La aplicación está hecha en grails. Queríamos probar grails porque se integra muy bien con librerías y otros desarrollos que tenemos hechos en java. De hecho seguro que cuando esta aplicación crezca usaremos esas magníficas capacidades, pero ahora mismo esta aplicación sólo se comunica con el buscador por HTTP y con la base de datos por JDBC. Así que seguramente la única excusa que se nos ocurre para haber usado grails es porque nos apetecía probarlo :).

La experiencia ha sido buena aunque hemos echado de menos una buena integración con IDEs, en concreto con Eclipse. No obstante tendremos que programar más para fundamentar mejor nuestra evaluación.

Implementamos un Device Filter para filtrar a los Iphones por el User-Agent, a estos se les ofrecen las mismas vistas que al resto de los dispositivos excepto por las hojas de estilo y los scripts de Javascript que son especiales para aprovechar las capacidades de los navegadores Safari (WebKit):

  • Manejo de eventos especiales generados por webkit como el Orientation Event, para modificar la interfaz según la orientación del aparato.

Para el resto de dispositivos las vistas se sirven con XHTML Mobile Profile 1.0; usamos atributos acceskeys en algunos enlaces para habilitar la navegación a través del teclado del dispositivo y la función de hacer llamada de la especificación WTAI (Wireless Telephony Application Interface) para que los dispositivos reconozcan números de teléfono en el contenido de las páginas y permitan hacer llamadas directamente.

20
May
08

Simplificación de polígonos con el algoritmo Douglas-Peucker

Hace bastante tiempo ya nos encontramos con un problema del que no teníamos ni idea: nuestra fuente de datos nos pasaba datos de áreas geográficas (más concreto, los contornos) como listas de miles de puntos. En aquel entonces, la API de Google Maps dejaba frito el navegador cuando se intentaba dibujar un polígono con tal número de vértices (hoy ha mejorado y en vez de pintar un polígono vía Javascript, la API te tira una imagen a partir de la segunda petición).

¿Qué tuvimos que hacer? Reducir el número de puntos que definían cada uno de los polígonos intentando ser lo más fieles posible a la forma original porque pintar una provincia cualquiera como un cuadrado no es solución. No sólo teníamos datos de provincias: también teníamos de distritos y barrios. Y eso hacía que esta simplificación de polígonos fuera algo bastante intratable de hacer a mano. Fue así como descubrimos que existía una forma de hacerlo automáticamente y cuyo algoritmo fue desarrollado por David Douglas & Thomas Peucker y es conocido como Douglas-Peucker polyline simplification algorythm.

Partimos de un trozo de código en Pascal que encontramos en algún lugar y lo portamos a Python, que era el lenguaje con el que hicimos todo el script de importación de datos. Se quedó pendiente de publicación el asunto, ya que en su día nadie había portado dicho código a Python (ni Ruby ni Perl ni ningún lenguaje de script medianamente actual). El caso es que eso ha sido hasta hace un par de semanas Schuyler Erle publicó su versión en Mapping Hacks. Su código es mucho más bonito que el nuestro, así que nos ahorraremos el attachment 😉

30
Abr
08

Spring 2.5 y los filtros de contexto

El cambio a spring 2.5 conlleva una nueva forma de cargar tus clases como beans, lo que ellos llaman contexto. Este contexto es el responsable de añadir como beans las clases que tengas marcadas con las anotaciones que spring te da de serie(@Component, @Controller, @Service, @Repository). Si buceamos un poco en el código de spring podemos ver que la clase encargada de hacer esto es ClassPathBeanDefinitionScanner.java. Esta clase tiene implementada una pequeña jerarquía de filtros para añadir o quitar clases del contexto cuando arranca nuestra aplicación. Por defecto, añade todas las clases que estén marcadas con las anotaciones que hemos visto anteriormente:


  this.includeFilters.add(new AnnotationTypeFilter(Component.class));

Ahora vamos a ver las opciones que nos da el framework para añadir clases a ese contexto. En su documentación podemos ver un claro ejemplo de como añadir una nueva anotación:


  <context:component-scan>
    <context:include-filter type="annotation"
      expression="CustomAnnotation"/>
  </context:component-scan>

Lo que viene a decir el xml es que al contexto le vamos a añadir un filtro de tipo anotación y para todas las clases que estén marcadas con la anotación que va dentro del atributo expression.

Con lo que él internamente hará algo tal que así:


  this.includeFilters.add(new AnnotationTypeFilter(CustomAnnotation.class));

Lo que no se explica en la documentación es que por defecto, la clase que se encarga de parsear el contexto, ComponentScanBeanDefinitionParser.java, también soporta otros tipos de filtros además del de anotaciones.

El más sencillo sería el filtro de asignación, que simplemente añadiría una clase o un grupo de clases que extiendan o implementen algo común:


  <context:component-scan>
    <context:include-filter type="assignable"
      expression="org.myapp.IService"/>
  </context:component-scan>

Con este código se añadirían todas las clases que extendieran de la interfaz org.myapp.IService.

Otro de los filtros que soporta es por expresiones regulares, con este filtro spring cargará en el contexto las clases que coincidan con una expresión regular determinada:


  <context:component-scan>
    <context:include-filter type="regex"
      expression="(Service|Controller)\w+"/>
  </context:component-scan>

Con este código se cargarán todas clases que empiecen por Service o Coltroller.

Por último, tenemos un filtro un poco más complejo que se encarga de evaluar expresiones propias de Aspect Oriented Programming(AOP):


  <context:component-scan>
    <context:include-filter type="aspectj"
      expression="Service || Controller"/>
  </context:component-scan>

Con lo que cargaremos todas las clases que extiendan o implementen la clase Service o Controller.

La verdad es que con el grupo de anotaciones que spring trae de serie es más que suficiente para resolver la mayoría de los problemas pero nos podemos encontrar en escenarios donde no queremos tener como dependencia spring en una clase que puede ser utilizada tanto por spring como por otra aplicación o módulo que no use spring, con lo que acceder a los filtros del contexto nos vendrá muy bien.

27
Feb
08

sobre spring 2.5, anotaciones y expresiones regurales

Una de las nuevas características de la última versión estable de Spring MVC es la posibilidad de configurar la relación entre una url y el método que se ejecuta mediante la anotación @RequestMapping. Sobre las nuevas características hay muy buenos artículos, como el aparecido en infoq o en el mismo blog de springsource. Pero lo que estos artículos no explican es como configurar nuestra aplicación para que esta anotación acepte como valores expresiones regulares.

En este artículo vamos describir los componentes que se encargan de estas relaciones y como añadir nuevas clases para poder trabajar con expresiones regulares.

El primer paso es crear una clase que se encargue de machear las expresiones que vamos a identificar como rutas válidas. Por defecto la clase AntPathMatcher se encarga de machear expresiones tipo ant(‘/**’, ‘/home/*’, ..) como no queremos perder esta funcionalidad podemos extender nuestra clase de esta, sobreescribir el método doMatch y marcarla como un componente con la anotación @Component:


@Component("regexPathMatcher")
public class RegexPathMatcher extends AntPathMatcher {
  @Override
  protected boolean doMatch(String pattern, String path, boolean fullMatch) {
    boolean match = super.doMatch(pattern, path, fullMatch);
      if (!match) {
        jregex.Pattern pat = new jregex.Pattern(pattern);
        match = pat.matcher(path).matches();
      }
    return match;
  }
}

Las clases encargadas de relacionar los controladores con las rutas que resuelven siempre extienden de la interfaz HandlerAdapter. En el caso de que estemos usando este sistema de anotaciones la clase por defecto encargada será AnnotationMethodHandlerAdapter. A esta clase le tenemos que indicar que el componente encargado de machear las rutas ya no es el de por defecto, para esto declararemos este componente en el xml de configuración y le inyectaremos el nuevo matcher:


<bean id="regexMethodHandlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="pathMatcher" ref="regexPathMatcher"/>
</bean>

El último componente que hay que configurar es el encargado de decidir si la ruta que llega de una petición es válida en la aplicación. Estos componentes extienden de la clase abstracta AbstractHandlerMapping, en el caso de usar anotaciones el componente por defecto es DefaultAnnotationHandlerMapping. Para configurarlo dentro del xml tendremos que añadir:


<bean id="regexUrlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
  <property name="alwaysUseFullPath" value="true"/>
  <property name="pathMatcher" ref="regexPathMatcher"/>
</bean>

Una vez hecho esto nuestra aplicación soportará rutas un poco más elaboradas como estas:


@RequestMapping("/home(/page({=page}\\d+))?")
@RequestMapping("/user/({=user}[\\w\\d_-.]+)/(contacts|followers)")
23
Feb
08

Sería una buena opción, si fuera posible

Podría parecer más fácil definir una rutina almacenada e invocarla desde el disparador utilizando una simple sentencia CALL. Esto sería ventajoso también si se deseara invocar la misma rutina desde distintos disparadores. Pero no se puede.

De la documentación de triggers en MySQL.

¿Quieres un caramelo? Espero que no, porque no tengo.

05
Feb
08

JBoss borra los logs al arrancar

En realidad no es JBoss el que se carga los logs, es log4j, y la solución es poner a false el parámetro append del DailyRollingFileAppender en el log4j.xml, o sea:

<appender name="FILE" class="org.jboss.logging.appender.DailyRollingFileAppender">
<errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
...
<param name="Append" value="true"/>

De esa forma los ficheros no se sobrescriben al rearrancar.

29
Ene
08

crisis

O es un programa tonto o es un tío muy bueno

Afortunadamente ni uno ni otro.