13 may. 2013

Integración de Vaadin con Spring

Como muchos sabéis he realizado un arquetipo de maven para la integración de Vaadin con Spring, apoyándome en la aproximación que el propio equipo de Vaadin tenía en su wiki y cuya entrada en este blog era Vaadin Spring Archetype made by me :D

Profundizando un poquito más en la integración de estos dos frameworks que tanto me gustan, he visto que hay otras maneras de hacerlo, que además creo que son bastante más simples que la que se ha implementado en el arquetipo, lo cual me obliga a cambiarlo...

La solución pasa por usar una clase "Inyectora", una serie de escuchadores y parámetros de contexto en el archivo web.xml y una configuración mínima de spring que permita el "Autowiring" de las dependencias de modo transparente, ahora bien, esta integración es ligeramente diferente entre las dos últimas versiones mayores disponibles de Vaadin, en este primer post me centraré en la integración de la versión 6 de Vaadin.


Para la realización de esta integración, seguiremos los siguientes pasos:
  1. Creación del proyecto maven:
    mvn archetype:generate \
        -DarchetypeGroupId=com.vaadin \
        -DarchetypeArtifactId=vaadin-archetype-clean \
        -DarchetypeVersion=LATEST \
        -DgroupId=com.javiserrano \
        -DartifactId=vaadin6.spring \
        -Dversion=1.0-SNAPSHOT \
        -Dpackaging=war
    
  2. Adición de las dependencias de spring al archivo pom generado en la ejecución del punto anterior:
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>3.2.2.RELEASE</version>
    </dependency>
    
  3. Modificación del archivo web.xml generado por el arquetipo  añadiendo desde la linea 9 a la 22 ambas incluidas, quedando de la siguiente manera:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
      <display-name>Vaadin Web Application</display-name>
      <context-param>
        <description>Vaadin production mode</description>
        <param-name>productionMode</param-name>
        <param-value>false</param-value>
      </context-param>
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/context.xml</param-value>
      </context-param>
      <listener>       
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
      </listener>
      <servlet>
        <servlet-name>Vaadin Application Servlet</servlet-name>
        <servlet-class>com.vaadin.terminal.gwt.server.ApplicationServlet</servlet-class>
        <init-param>
          <description>Vaadin application class to start</description>
          <param-name>application</param-name>
          <param-value>com.javiserrano.MyVaadinApplication</param-value>
        </init-param>
      </servlet>
      <servlet-mapping>
        <servlet-name>Vaadin Application Servlet</servlet-name>
        <url-pattern>/*</url-pattern>
      </servlet-mapping>
    </web-app>
    
  4. Debemos crear el archivo context.xml en la carpeta src/main/webapp/WEB-INF/spring con el siguiente contenido:
    <?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:lang="http://www.springframework.org/schema/lang"
       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/lang 
       http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
        <context:annotation-config />
        <context:component-scan base-package="com.javiserrano" />
    </beans>
    
  5. Ahora toca la creación de la clase "Inyectora", que quedará así:
    package com.javiserrano;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import org.springframework.web.context.support.WebApplicationContextUtils;
    
    import com.vaadin.Application;
    
    public class Inject {
      static Application application;
      static ApplicationContext applicationContext;
    
      public static void inject( Object object ) {
        if ( applicationContext == null ) {
          ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
          HttpServletRequest request = requestAttributes.getRequest();
          HttpSession session = request.getSession( false );
          applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext( session.getServletContext() );
        }
        AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
        beanFactory.autowireBeanProperties( object, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false );
      }
    
    }
    
  6. Ahora vamos a crear la ventana principal con el siguiente contenido:
    package com.javiserrano;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.vaadin.ui.Button;
    import com.vaadin.ui.Button.ClickEvent;
    import com.vaadin.ui.Window;
    import com.vaadin.ui.Button.ClickListener;
    
    @SuppressWarnings ( "serial" )
    public class MyWindow extends Window implements ClickListener {
        @Autowired
        HelloService helloService;
    
        public MyWindow(String caption) {
            super(caption);
            Inject.inject(this);
            Button button= new Button("Click me!", (Button.ClickListener)this);
            addComponent(button);
        }
    
        public void buttonClick(ClickEvent event){
            if (helloService != null){
                showNotification("The injected service says", helloService.sayHello(), 
                                 Notification.TYPE_HUMANIZED_MESSAGE);
            }
        }
    }
    
  7. Ahora vamos a crear el servicio HelloService con el siguiente contenido:
    package com.javiserrano;
    
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Service;
    
    @Service
    @Scope("prototype")
    public class HelloService {
    
        public String sayHello() {
            return "Hello Vaadiners from a Spring Service!!!";
        }
    }
    
    
    NOTA: Es importante que todo aquello que queramos inyectar vía Spring tenga las anotaciones @Service o @Component para que Spring sea capaz de detectarlas e inyectarlas automáticamente.
  8. Ahora toca modificar la clase principal, MyVaadinApplication, creada automáticamente por el arquetipo para que quede de la siguiente forma:
    package com.javiserrano;
    
    import com.vaadin.Application;
    
    /**
     * The Application's "main" class
     */
    @SuppressWarnings("serial")
    public class MyVaadinApplication extends Application
    {
        private MyWindow window;
    
        @Override
        public void init()
        {
            window = new MyWindow("My Vaadin + Spring Application");
            setMainWindow(window);
        }
        
    }
    
  9. Por último, probamos la aplicación ejecutando en un terminal:
    mvn clean install jetty:run
    
    Abrimos un navegador web y navegamos a http://localhost:8080/vaadin6.spring
Espero que os simplifique la integración de estos dos frameworks, como me la ha simplificado a mi.


Los fuentes los podéis descargar haciendo click en... vaadin6.spring.zip

Como siempre, un saludo y a disfrutar!

2 comentarios:

dherrerabits dijo...

Genial post. Gracias!

AwAkE dijo...

Gracias a ti David!

Publiqué también la integración para la versión 7... creo que es mucho más limpia.

Un saludo!