Introducción a Aplicaciones REST con Spring 3.0 Web MVC Framework

Logotipo Spring Framework

El Framework Web MVC (modelo-vista-controlador) de Spring está diseñado alrededor de un Servlet (DispatcherServlet) encargado de enviar las peticiones a los diferentes manejadores (handlers) de la aplicación, a partir de la configuración del mapeo de manejadores, la resolución de las vistas, locale, etc. Es decir, al configurar este serlvet en el fichero web.xml de tu aplicación web, todas las peticiones pasarán por él y se encargará de redirigir a los diferentes manejadores. Los manejadores por defecto se basan en las anotaciones @Controller y @RequestMapping como veremos a continuación. A partir de la versión 3.0 de Spring es posible crear aplicaciones Web RESTfull a través de la anotación @PathVariable.

En esto último se va a centrar este artículo, en la implementación de los controladores a través de anotaciones y en la configuración de los mismos para crear aplicaciones REST.

Dispatcher Servlet

Cómo comentamos al principio, el framework MVC de Spring está diseñado alrededor de un servlet que reenvía las peticiones a los diferentes controladores. Este servlet está plenamente integrado con el contenedor IoC (Inversion of Control) lo que permite usar todas las características del framework Spring.

Workflow del Servlet

Se trata de un servlet (hereda de la clase base HttpServlet) y como tal se declara en el fichero de configuración web.xml de tu aplicación web:

<web-app>

    <servlet>
        <servlet-name>buscar</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>buscar</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

En el ejemplo anterior, tal como se puede ver en el mapeo del servlet, todas las peticiones que acaben en .do serán manejados por el DispatcherServlet buscar.

En el framework Web MVC cada DispatcherServlet tiene asociado su propio WebApplicactionContext, el cuál hereda todos los beans definidos en el WebApplicationContext raíz que pueden ser sobreescritos. Una vez inicializado el DispatcherServlet, el framework busca un fichero llamado [servlet-name]-servlet.xml en el directorio WEB-INF de tu aplicación web y crea los beans allí definidos (si hay beans con el mismo nombre que los del contexto raíz los sobreescribe). En el ejemplo anterior debemos tener un fichero llamado buscar-servlet.xml.

El DispatcherServlet de Spring usa beans especiales para procesar las peticiones y mostrar las vistas apropiadas. Estos beans son parte del Framework y pueden ser configurados en el contexto WebApplicationContext como harías con otros beans. Sin embargo, para la mayoría de los beans se proporcionan parámetros por defecto y no necesitan configuración.

Contexto del DispatcherSerlvet

Implementando Controladores

Los controladores proveen acceso al comportamiento de la aplicación, interpretan los inputs del usuario y los transforman en un modelo que es representado al usuario por una vista (modelo-vista-controlador). Spring implementa un controller de una manera abstracta, lo que permite crear una gran variedad de controladores.

A partir de la versión 2.5, y la mantiene también en la versión 3.0, Spring introdujo el modelo de programación basado en anotaciones, que usa anotaciones como @RequestMapping, @RequestParam, @ModelAttribute, etc. Los controladores implementados a través de este estilo no necesitan heredar de ninguna clase base o implementar ninguna interfaz específica:

@Controller
public class HelloWorldController {

    @RequestMapping("/helloWorld")
    public ModelAndView helloWorld() {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("helloWorld");
        mav.addObject("message", "Hello World!");
        return mav;
    }
}

Definiendo un controlador con @Controller

La anotación @Controller indica que una clase tendrá el rol Controlador. No se requiere heredar de ninguna clase base o del API de Servlet, como ocurría en la versión 2.0. El Dispatcher busca las clases “marcadas” con dicha anotación para mapear los métodos y detectar todas las anotaciones @RequestMapping (ver más abajo).

Para activar la autodetección de los controllers que contienen la anotación, debes añadir a tu configuración el componente que los escanea:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="com.paquete.ejemplo.controllers"/>

    // ...

</beans>

Mapeando peticiones con @RequestMapping

Se usa la anotación @RequestMapping para mapear URLs como por ejemplo “/mostrareventos” con una clase, o bien un método dentro de la clase. Si es a nivel de clase, se suele mapear una URL concreta con métodos adicionales que acotan el mapeo a través de métodos HTTP específicos (POST, GET, etc.) o bien mediante parámetros en la petición.

@Controller
@RequestMapping("/mostrareventos")
public class AppointmentsController {

    private final AppointmentBook appointmentBook;

    @Autowired
    public AppointmentsController(AppointmentBook appointmentBook) {
        this.appointmentBook = appointmentBook;
    }

    @RequestMapping(method = RequestMethod.GET)
    public Map<String, Appointment> get() {
        return appointmentBook.getAppointmentsForToday();
    }

    @RequestMapping(value="/{dia}", method = RequestMethod.GET)
    public Map<String, Appointment> getForDay(
                @PathVariable @DateTimeFormat(iso=ISO.DATE) Date dia,
                Model model) {
        return appointmentBook.getAppointmentsForDay(dia);
    }

    @RequestMapping(value="/new", method = RequestMethod.GET)
    public AppointmentForm getNewForm() {
        return new AppointmentForm();
    }

    @RequestMapping(method = RequestMethod.POST)
    public String add(@Valid AppointmentForm evento,
                       BindingResult result) {
        if (result.hasErrors()) {
            return "mostrareventos/new";
        }
        appointmentBook.addAppointment(evento);
        return "redirect:/mostrareventos";
    }
}

En el ejemplo anterior la anotación @RequestMapping se usa primero a nivel de clase. De esa manera indica que todos los métodos de este controlador manejarán las peticiones con el path relativo “/mostrareventos”. El métdo get() refina dicha URL y se ejecuta únicamente cuando se trata de un método GET. El método add(), por el contrario, se ejecuta cuando se realiza un POST de un formulario del sitio web.

Los métodos getForDay() y getNewForm() se ejecutarán cuando se trate de una petición GET y además la URL contenga “/mostrareventos/díaEventoo “/mostrareventos/new”, respectivamente.

URI Templates

Para acceder a partes de la URL en los métodos manejadores usamos los patrones de URI o URI Templates, dando valores dentro del path de @RequestMapping. Luego podemos usar la anotación @PathVariable para indicar que un parámetro del método se va a enlazar con un valor que viene en una variable de la URI. Veamos un ejemplo:

@RequestMapping(value="/alumnos/{alumnoId}", method=RequestMethod.GET)
public String findAlumno(@PathVariable String alumnoId, Model model) {
  Alumno alumno = alumnoService.findAlumno(alumnoId);
  model.addAttribute("alumno", alumno);
  return "displayAlumno";
}

La URI Template “/alumnos/{alumnoId}” especifica la variable con el nombre alumnoId. Cuando un controlador maneja esta petición, el valor de alumnoId se establece con el valor que viene en la URL. Por ejemplo, cuando llega una petición con esta URL “/alumnos/francisco” el valor “francisco” se asociará al parámetro alumnoId de tipo String del método manejador.

Puedes usar varias anotaciones @PathVariable para enlazar múltiples variables URI Templates:

@RequestMapping(value="/alumno/{alumnoId}/asignatura/{asignaturaId}",
                 method=RequestMethod.GET)
public String findAsignatura(@PathVariable String alumnoId,
                 @PathVariable String asignaturaId, Model model) {
  Alumno alu = alumnoService.findAlumno(alumnoId);
  Asignatura asig = alumno.getSignatura(signaturaId);
  model.addAttribute("Asignatura", asignatura);
  return "displayAsignatura";
}

El método findAsignatura se ejecutaría al invocar, por ejemplo la URL “/alumno/245/asignatura/32“. Podemos indicar una parte a nivel de clase y otra a nivel de método como en el siguiente ejemplo:

@Controller
@RequestMapping("/alumno/{alumnoId}")
public class RelativePathUriTemplateController {

  @RequestMapping("/asignatura/{asignaturaId}")
  public void findPet(@PathVariable String alumnoId,
                       @PathVariable String asignaturaId,
                         Model model) {
    // implementación omitida
  }
}

Conclusiones

Tal y cómo nos propusimos al inicio de este artículo pretendíamos realizar una introducción a la programación de aplicaciones REST con Spring 3.0. Cómo cualquier aplicación Spring MVC, hemos configurado el DispatcherServlet, hemos aprendido a declarar controladores mediante la anotación @Controller. También hemos mapeado peticiones con la anotación @RequestMapping y por último hemos asociado valores de variables de la URL a parámetros de los métodos manejadores de los controladores mediante URI Templates y la anotación @PathVariable. Con todo esto podemos comenzar a construir una aplicación REST en la que cada recurso sea accesible mediante una URL amigable del tipo:

http://mi-dominio/gestionalumnos/
http://mi-dominio/gestionalumnos/alumno/new
http://mi-dominio/gestionalumnos/alumno/234
http://mi-dominio/gestionalumnos/alumno/234/asignatura/32
http://mi-dominio/gestionalumnos/asignatura/new
http://mi-dominio/gestionalumnos/asignatura/32
etc...

Este artículo está basado en la documentación de referencia de Spring. Para ampliar estos y otros temas puedes consultar la Referencia del framework Spring MVC.

Configura tu Entorno de Desarrollo Android y Crea tu Primera Aplicación

Android

En este post aprenderemos a configurar el entorno de desarrollo Android. Al final del mismo tendremos configurado el Sistema y seremos capaces de desarrollar, compilar y ejecutar una pequeña aplicación en el emulador. Para ello vamos a seguir los siguientes pasos:

  1. Instalar el SDK de Android
  2. Instalar el entorno de desarrollo Eclipse y el plugin ADT (Android Development Tools)
  3. Configuración del ADT e instalación de los paquetes del SDK con las versiones del S.O.
  4. Desarrollar y ejecutar un proyecto.

Debemos tener en cuenta que necesitamos previamente tener instalada una versión de Java, se requiere JDK 5 o superior.

Instalación del SDK

El SDK de Android está dividido en dos partes, el paquete principal y los componentes del mismo. Hay versiones para Mac OS X, Windows y Linux.

Descargamos el paquete principal desde la dirección http://developer.android.com/sdk/index.html para el sistema operativo que deseemos.

Descomprimimos el fichero descargado en una carpeta de fácil acceso. Por defecto los ficheros del SDK vienen comprimidos en un directorio llamado android-sdk-<plataforma de la máquina>.

Instalación de Eclipse

Eclipse es un entorno de desarrollo open source que incluye una colección de herramientas y que fue originalmente creado por IBM para Java. Actualmente es el IDE (Integrated Development Environment) favorito para la comunidad de desarrolladores de Java.

Descarga “Eclipse for Java Developers” (es una versión del entorno de desarrollo orientado a la programación con Java) desde http://www.eclipse.org/downloads. Instala Eclipse descomprimiendo el fichero descargado.

Eclipse organiza el trabajo en proyectos y los proyectos se almacenan en Workspaces y cada workspace se almacena en el directorio que tú elijas. La primera vez que ejecutes Eclipse te pedirá la localización del workspace. Si trabajas en Windows te recomiendo que elijas un directorio sin espacios (ej: c:workspace) y en linux (/home/usuario/workspace).

Eclipse IDE

Instalación del plugin ADT

Una vez ejecutado Eclipse pulsa el menú Help->Install New Software…

Pulsa Add, en la esquina superior derecha

En el diálogo que aparece para añadir repositorio, tecleamos “ADT Plugin” para el nombre y la siguiente URL para la localización:

https://dl-ssl.google.com/android/eclipse/

Pulsamos OK

Instalar ADT

En el diálogo de Software disponible, seleccionamos el checkbox de la opción Developer Tools y pulsamos Next.

En la siguiente ventana veremos la lista de herramientas a descargar. Pulsamos Next.

Leemos y aceptamos los acuerdos de licencia, y pulsamos Finish. Si aparece una advertencia de seguridad diciendo que la autenticidad o validez del software no puede ser contrastada, pulsamos OK.

Una vez completada la instalación, reiniciamos Eclipse.

Para configurar el ADT y especificar dónde está instalado el SDK de Android, pulsamos Window->Preferences y a la izquierda del diálogo que aparece pulsamos sobre Android. En la caja de texto con la etiqueta SDK location debemos introducir la ruta del SDK (ejemplo en linux: /home/usuario/android-sdk-linux_x86) y pulsamos OK.

Preferencias ADT

Instalación de los componentes del SDK

Para la instalación de los componentes que faltan debemos ejecutar el SDK. Esta vez lo podemos hacer desde el propio Eclipse, pulsando sobre el  botón de la barra de herramientas que abre la aplicación Android SDK y AVD Manager. Esta opción también la encontraremos desde el menú Window.

AndroidToolEclipse

Pulsamos sobre la opción Available Packages en la parte izquierda de la aplicación, para seleccionar los paquetes que queramos. Los componentes básicos son SDK Tools, SDK Platform Tools y al menos una versión de la plataforma, los recomendados son la documentación y los ejemplos y si queremos instalarlo por completo podemos marcar las herramientas de terceros y las APIS de Google. Marcaremos todos los paquetes en el caso de que no tengamos problemas de espacio y pulsamos Install. El proceso de instalación lleva bastante tiempo por lo que mejor dejamos que se vaya instalando mientras hacemos cualquier otra cosa.

Pantallazo-Android SDK and AVD Manager

Hello World!

Para ver que está todo configurado correctamente vamos a desarrollar nuestro primer proyecto Android, un sencillo Hello World. Ejecutamos Eclipse, si es que no lo tenemos ya en ejecución, y seleccionamos  File->New->Android Project. Si esta opción no se encuentra en el menú, seleccionamos la opción Other… y en el cuadro de diálogo desplegamos Android y pulsamos sobre Android Project.

Especificamos un nombre al proyecto (HelloWorld), más abajo la versión de Android para la que vamos a desarrollar; seleccionamos la versión 2.2 y especificamos el nombre del paquete. En Android los paquetes son importantes, entre otras cosas, a efectos de firmas de aplicaciones. Suele ser el nombre del dominio y opcionalmente uno o varios subdominios al estilo Java (por ejemplo com.example.calculator). Pulsamos Finish y Eclipse nos generará una estructura básica de proyecto con los ficheros necesarios:

  • AndroidManifest.xml en el directorio raíz es el fichero que une todos los componentes de la aplicación, los bloques que la componen y los permisos que requiere.
  • Layout: main.xml, en el directorio res->Layout especifica el layout de la pantalla. En este sencillo caso es un layout lineal con un texto, cuyo valor viene dado por la etiqueta @string/hello.
  • Strings.xml en el directorio res->values es el que almacena los valores de las cadenas de texto a visualizar en la pantalla. Aquí se encuentra el valor de la cadena a mostrar en la pantalla, y que podemos modificar si deseamos.
  • En el directorio gen/nombrepaquete/R.java, el fichero responsable de enlazar los recursos y las clases java. Es un fichero autogenerado y no debemos editarlo.
  • En el diretorio src se encuentran las clases java.  Es el código que se convertirá en ejecutable y correrá en la máquina java virtual Dalvik (la máquina virtual Java de Android). Automáticamente nos ha generado una clase HelloWorldActivity.java que hereda de la clase Activity. En Android las Activity representan pantallas de aplicación. Una aplicación puede tener una, varias o ninguna Activity. En este ejemplo sencillo invocamos al método setContentView() pasándole cómo parámetro el código para el layout principal (y único definido en la aplicación).

El Emulador

Ejecutar la aplicación en un dispositivo físico o en un dispositivo emulado es similar, ejecuta el mismo código base al igual que en el dispositivo. Para usar el emulador debemos crearnos uno desde las herramientas Android.
Abrimos el SDK y seleccionamos Virtual Devices. Como no tenemos ninguno pulsamos New… y seleccionamos un nombre (GingerBread, por ejemplo), la versión de Android que se ejecuta en él (2.3) y el tamaño de la SD externa en Mb. Pulsamos Create AVD y a continuación seleccionamos de la lista el dispositivo creado y pulsamos Start… para ejecutarlo. En unos breves instantes aparecerá en la pantalla un sistema Android, con ciertas carencias, como localización gps y acelerómetro pero totalmente funcional para la mayoría de los desarrollos.
Para ejecutar el proyecto, desde Eclipse lo seleccionamos y pulsamos el botón derecho del ratón para hacer clic sobre Run As->Android Application. Automáticamente, en el dispositivo virtual, aparecerá la siguiente ventana:
Emulador
Si has llegado a este punto sin problemas, ¡enhorabuena! Ya has configurado tu entorno de desarrollo y has ejecutado una sencilla aplicación. Ahora se abre un mundo de posibilidades para ti en el ecosistema Android.

Gestión de Ramas con Subversion y Eclipse IDE

Sincronizar ramas Subversion

Crear ramas, etiquetar y realizar merge son conceptos comunes para casi todos los sistemas de control de versiones. En este artículo vamos a adentrarnos en la gestión de ramas dentro de un proyecto de desarrollo con Eclipse. Este proyecto lo tenemos enlazado a Subversion utilizando el plugin Subclipse. Gestionar ramas (branching) es una parte fundamental de un control de versiones, se asume que estás familiarizado con los conceptos básicos de Subversion.

¿Qué es una rama?

Se trata de una línea de desarrollo que existe independientemente de otra. Una rama siempre es una copia de otra y desde el momento de copia contiene su propia historia. Subversion permite mantener ramas paralelas de tus ficheros y directorios. Ayuda a duplicar cambios de una rama a otra y, finalmente, puede hacer que se reflejen cambios de tu copia de trabajo en diferentes ramas por lo que puedes mezclar y enlazar diferentes líneas de desarrollo en tu trabajo diario.

Sincronizar ramas Subversion

Crear una rama

Vamos con un caso práctico: Lo normal es que el proyecto esté asociado a la línea principal de desarrollo, que llamamos Trunk. Todas las modificaciones y funcionalidades que se deseen incorporar a la versión de producción deberán estar contenidas en esta rama (pueden existir otras estrategias pero ésta es la que usamos en nuestro trabajo). En un momento determinado nos puede interesar abrir otra línea de desarrollo o rama en la que trabajará de forma independiente en el tiempo un desarrollador o equipo de desarrolladores, por ejemplo. Así dejamos la copia de trabajo del Trunk libre por si es necesario una nueva versión de producción de manera inmediata para corregir una incidencia o incorporar una funcionalidad urgente. Para realizar esto hacemos clic sobre el proyecto con el botón derecho del ratón y vamos a Team -> Branch/Tag. Subversion considera exactamente igual una rama (branch) que una etiqueta (tag), es decir, son copias de un conjunto de carpetas y ficheros (se diferencian en que las ramas tienen history y las etiquetas no).

A continuación seleccionamos el directorio en el que se almacenará esta rama. Todas las ramas se suelen guardar bajo el directorio branches del repositorio por convenio. También debemos indicar la revisión sobre la que queremos crear la rama. Si queremos que se haga desde la última versión de desarrollo, seleccionamos la opción HEAD. Por último podemos indicar un comentario, por ejemplo, “Creación nueva rama”.
A partir de este momento la copia de trabajo en eclipse pasa a ser la de la rama (si no es así podemos pulsar sobre el proyecto con el botón derecho del ratón y pulsar sobre Team -> Switch to another Branch/tag/revision… para seleccionar la nueva rama), por lo que cualquier modificación que realicemos y subamos al control de versiones se hará sobre ésta. Se harán las modificaciones oportunas en el desarrollo de nuevas funcionalidades y las subiremos al repositorio haciendo commit.

Mantener sincronizada una rama

De forma periódica es conveniente actualizar esta rama con los posibles cambios introducidos en el trunk, ya que aunque no hemos terminado con nuestro trabajo en la rama, deseamos que los cambios en el trunk no se aparten mucho del contenido de la rama, y la sincronización entre ellas sea menos traumática. Ya dijimos que cada rama tiene una línea de desarrollo independiente.

Para realizar un merge del trunk sobre nuestra rama pulsamos sobre el proyecto y vamos a Team -> Merge. Aparecerá a continuación una pantalla en la que debemos seleccionar el modo en el que haremos merge:

Mantener sincronizada una rama

Seleccionamos la primera opción “Merge a range of revisions” para incorporar los cambios en el trunk o en otra rama. Deberemos elegir a continuación la localización de los elementos a sincronizar, y como ya habíamos dicho lo vamos a realizar desde el trunk. En la siguiente pantalla podemos especificar las opciones del proceso de merge.

Ya que se han hecho cambios en el trunk mientras nosotros trabajábamos en nuestra rama, pueden aparecer conflictos. La pantalla siguiente nos avisa de que hay un conflicto en una clase que debemos resolver. Para resolverlo elegimos la opción Launch a graphical conflict resolution editor y se abrirá un editor gráfico con ambas versiones y los cambios señalizados de forma gráfica.

Resolución de conflicto al hacer merge ramas

Al solucionar manualmente el conflicto y cerrar el editor aparece una pantalla de confirmación de resolución del conflicto. Seleccionamos Yes y pulsamos ok.

Al terminar todo el proceso nos muestra una pantalla de resumen. Ya tenemos en una copia local los cambios del trunk y de la rama. Ahora tenemos que consolidar los cambios a la rama haciendo commit y subiendo las modificaciones al control de versiones (dentro de la rama).

Resumen merge ramas Subversion

Reintegrar una rama

Qué ocurre cuando has finalizado tu trabajo en la nueva línea de desarrollo. Tu nueva funcionalidad está acabada y está preparada para pasar al trunk, por lo que debemos cambiar la rama asociada al proyecto pulsando sobre el proyecto con el botón derecho del ratón y seleccionar Team -> Switch to another Branch/tag/revision… para asociar al trunk.

A continuación hacemos un merge, como ya indicamos en el punto anterior, ahora con la opción Reintegrate a branch.

Reintegrar una rama al trunk de subversion

En la pantalla siguiente seleccionamos la rama desde la que vamos a sincronizar. El proceso posterior es similar a lo comentado en el punto anterior, sin olvidar que la sincronización se realiza sobre la copia local y que debemos consolidar los cambios haciendo commit.

Evidentemente todo el proceso comentado aquí puede diferir de las necesidades de cada proyecto: puede interesar hacer merge de unos pocos paquetes, algunos módulos, o de ciertas revisions, en vez del HEAD. En el mundo real pueden surgir falsos conflictos en la sincronización, errores de versiones, que aprendemos a manejar con la práctica y experiencia. También tenemos que tener en cuenta que las versiones del cliente subversión y del servidor son compatibles (sobre todo al reintegrar una rama en el trunk puede dar algún tipo de problema). Lo que está claro es que dominar este proceso nos puede facilitar la vida en ciertos proyectos donde concurren desarrollos en paralelo de diferentes módulos.

Versiones

Eclipse Galileo Build id: 20100218-1602

Subversion Client Adapter: 1.6.10

Subclipse Integration: 3.0.0

Eclipse Mylin: 3.3.2