domingo, 21 de fevereiro de 2010

Integrando Vaadin com Spring em cinco minutos


Vaadin é um framework para criar aplicações web ricas (RIA) que usa os componentes do GWT e mapeia os eventos para o servidor, ou seja, abstrai o mapeamento dos serviços do GWT.




Uma aplicação em Vaadin

Basicamente uma aplicação web com Vaadin é bem simples:


import com.vaadin.ui.*;
public class HelloWorld extends com.vaadin.Application {
    public void init() { 
        Window main = new Window("Hello window"); 
        setMainWindow(main);
        main.addComponent(new Label("Hello World!")); 
    }
} 

Baixe o vaadin-6.2.3.jar, coloque dentro do lib e configure o web.xml:


<context-param>
    <description>Vaadin production mode</description>
    <param-name>productionMode</param-name>
    <param-value>false</param-value>
</context-param>
<servlet>
    <servlet-name>Myproject Application</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>
            HelloWorld
        </param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>Myproject Application</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>  

Sua aplicação funcionando:

 

Integrando com o Spring


Estendi a classe AbstractApplicationServlet, que é uma servlet que indica qual a classe Application será usada.

package br.com.dev.naskar.integ.vaadin.spring;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.vaadin.Application;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;

/**
 * Inicializa o Spring e configura a application do Vaadin.
 *
 * @author rafael
 */
@SuppressWarnings("serial")
public class SpringVaadinApplicationServlet extends AbstractApplicationServlet {

    /**
     * nome do bean do spring que sera usado como application.
     */
    private String nameSpringBeanApplication;
   
    /**
     * Class do application.
     */
    private Class applicationClass;
   
    /**
     * Contexto do spring.
     */
    private ApplicationContext springContext;

    @Override
    protected final Class getApplicationClass()
        throws ClassNotFoundException  {
        return applicationClass;
    }
   
    @Override
    public final void init(final ServletConfig config) throws ServletException {
        super.init(config);
       
        nameSpringBeanApplication =
            config.getInitParameter("nameSpringBeanApplication");
       
        String contextConfigLocation =
            config.getInitParameter("contextConfigLocation");
       
        if (contextConfigLocation == null || contextConfigLocation.equals("")) {
            throw new RuntimeException(
                    "Parametro contextConfigLocation nao configurado: "
                    + contextConfigLocation
            );
        }

        springContext =
            new ClassPathXmlApplicationContext(contextConfigLocation);
       
        Application application =
            (Application) springContext.getBean(nameSpringBeanApplication);
       
        if (application == null) {
            throw new RuntimeException(
                    "Spring Bean nao encontrado: " + nameSpringBeanApplication
            );
        }
        applicationClass = application.getClass();
       
    }

    @Override
    protected final Application getNewApplication(
            final HttpServletRequest request
    ) throws ServletException {
        return (Application) springContext.getBean(nameSpringBeanApplication);
    }

}

A cada HttpSession criada para um usuário, é chamado o método getNewApplication, ou seja, precisamos lembrar sempre de configurar o bean com escopo de prototype, para que a cada get do contexto do Spring, seja criado uma nova aplicação.

package br.com.dev.naskar.apres;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.vaadin.Application;
import com.vaadin.ui.Label;
import com.vaadin.ui.Window;

/**
 * Blank application.
 */
@Component
@Scope("prototype")
@SuppressWarnings("serial")
public class BlankApplication extends Application {
   
    /**
     * Mostra a iniciacao da aplicacao.
     */
    public BlankApplication() {
        System.out.println("Iniciando application...");
    }

    /**
     * Cria o layout da janela principal.
     *
     * @return Janela principal.
     */
    public final Window crieWindow() {
        Window mainWindow = new Window("Blank Vaadin Spring");
      Label label = new Label("Hello Vaadin Spring!");
      mainWindow.addComponent(label);
      return mainWindow;
    }

    @Override
    public final void init() {
        this.setMainWindow(crieWindow());
    }

}

Nessa classe podemos usar as injeções, aspectos, ferramentas de teste e tudo que o Spring tem sem problemas.

Veja que temos que trocar a definição da servlet, indicando qual o nome do bean do spring será usado e o onde está o applicationContext.xml do Spring.


  <context-param>
    <description>Vaadin production mode</description>
    <param-name>productionMode</param-name>
    <param-value>false</param-value>
</context-param>
<servlet>
    <servlet-name>main</servlet-name>
    <servlet-class>br.com.dev.naskar.integ.vaadin.spring.SpringVaadinApplicationServlet
    </servlet-class>
    <init-param>
        <description>nome do bean do spring</description>
        <param-name>nameSpringBeanApplication</param-name>
        <param-value>blankApplication</param-value>
    </init-param>
    <init-param>
        <description>nome do bean do spring</description>
        <param-name>contextConfigLocation</param-name>
        <param-value>META-INF/applicationContext*.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
  

Pronto, temos a aplicação funcionando com todo o backend do Spring.


Conclusão

Estive procurando um framework para a camada de apresentação que  não me desse tanto trabalho com detalhes da própria web. Utilizei o JBoss RichFaces, IceFaces (que é o que recomendo se você precisa mesmo usar JSF), mas vieram os problemas inerentes da própria API do ciclo de vida dos managed beans que o JBoss Seam resolveu muito bem com os seus escopos de conversação, mas que de vez em quando dá um nó na cabeça pra entender o fluxo de uma aplicação mais complexa.

Então, pesquisando no velho e bom google encontrei o Vaadin, que é tão simples quanto usar swing ou SWT. A princípio pensei que seria um framework mais orientado ao JavaScript como o Echo, SmartClient, etc. Adoro os protótipos e os ponteiros de função do JavaScript, mas a questão que você não trabalha sozinho num projeto e contratar ficar mais complicado.

Seguindo para a camada de negócio, Spring já um framework maduro e robusto, e que sinceramente, mesmo com o EJB 3.1 e a tendência de aplicações stateful (JBoss Seam), acho que a maioria das aplicações podem ser desenvolvidas com ele sem problemas (stateless). E com o lançamento do Spring Integration, as integrações entre sistemas não precisaram ficar exportando JAX-WS para tudo que é lado e tendo problemas de performance com XML até o ponto de lançarem hardware específico pra isso (WebSphere DataPower Integration Appliance).

Enfim, Vaadin é simples para apresentação e Spring é Robusto.

Em algum futuro post quero vê se coloco o HiIbernate Validator como Validator do Vaadin.


Referências

O código está no Gitorious:  http://gitorious.org/naskar/springvaadinblank/trees/master
Git: git://gitorious.org/naskar/springvaadinblank.git