Vaadin TouchKit and Spring

Hi !

I am currently developping an Vaadin web-app using Spring.
I am using a specific ApplicationServlet in order to be able to use the Vaadin components as normal Spring beans.

My web.xml:

    <servlet>
        <servlet-name>subgeste</servlet-name>
        <servlet-class>net.sebpx.txvdn.vaadin.util.WebContextApplicationServlet</servlet-class>
  ...
    </servlet>

and WebContextApplicationServlet.java:

package net.sebpx.txvdn.vaadin.util;



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

import java.net.MalformedURLException;
import java.net.URL;

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

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * {@link ApplicationServlet} that acquires {@link Application} instances from an
 * associated Spring {@link WebApplicationContext}.
 * This allows a Vaadin application to be configured normally as a Spring bean.
 *
 * <p>
 * For example, annotations such as
 * <code>{@link org.springframework.beans.factory.annotation.Autowired @Autowired}</code>,
 * <code>{@link org.springframework.beans.factory.annotation.Required @Required}</code>, etc.
 * and interfaces such as {@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware},
 * etc. will work on your {@link Application} instances.
 * </p>
 *
 * <p>
 * Important: The application bean must be declared in the associated {@link WebApplicationContext}
 * with <code>scope="session"</code>, and it must be the only instance of the configured application class.
 * </p>
 *
 * <p>
 * An example of "direct" use of this servlet in conjunction with Spring's
 * {@link org.springframework.web.context.ContextLoaderListener ContextLoaderListener}:
 * <blockquote><pre>
 *  &lt;!-- Spring context loader --&gt;
 *  &lt;listener&gt;
 *      &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
 *  &lt;/listener&gt;
 *
 *  &lt;!-- Vaadin servlet --&gt;
 *  &lt;servlet&gt;
 *      &lt;servlet-name&gt;myapp&lt;/servlet-name&gt;
 *      &lt;servlet-class&gt;com.example.WebContextApplicationServlet&lt;/servlet-class&gt;
 *      &lt;init-param&gt;
 *          &lt;param-name&gt;application&lt;/param-name&gt;
 *          &lt;param-value&gt;some.spring.configured.Application&lt;/param-value&gt;
 *      &lt;/init-param&gt;
 *      &lt;init-param&gt;
 *          &lt;param-name&gt;productionMode&lt;/param-name&gt;
 *          &lt;param-value&gt;true&lt;/param-value&gt;
 *      &lt;/init-param&gt;
 *  &lt;/servlet&gt;
 *  &lt;servlet-mapping&gt;
 *      &lt;servlet-name&gt;myapp&lt;/servlet-name&gt;
 *      &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
 *  &lt;/servlet-mapping&gt;
 * </pre></blockquote>
 * with this application context:
 * <blockquote><pre>
 *  &lt;!-- Activate Spring annotation support --&gt;
 *  &lt;context:annotation-config/&gt;
 *
 *  &lt;!-- Define Vaadin application bean --&gt;
 *  &lt;bean class="some.spring.configured.Application" scope="session"/&gt;
 *
 *  &lt;!-- Define other beans... --&gt;
 * </pre></blockquote>
 * </p>
 *
 * <p>
 * An example that creates a Spring MVC "controller" bean for use with Spring's
 * {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}:
 * <blockquote><pre>
 *  &lt;!-- Activate Spring annotation support --&gt;
 *  &lt;context:annotation-config/&gt;
 *
 *  &lt;!-- Define controller bean for Vaadin application --&gt;
 *  &lt;bean id="applicationController" class="org.springframework.web.servlet.mvc.ServletWrappingController"
 *     p:servletClass="com.example.WebContextApplicationServlet"&gt;
 *      &lt;property name="initParameters"&gt;
 *          &lt;props&gt;
 *              &lt;prop key="application"&gt;some.spring.configured.Application&lt;/prop&gt;
 *              &lt;prop key="productionMode"&gt;true&lt;/prop&gt;
 *          &lt;/props&gt;
 *      &lt;/property&gt;
 *  &lt;/bean&gt;
 *
 *  &lt;!-- Define Vaadin application bean --&gt;
 *  &lt;bean class="some.spring.configured.Application" scope="session"/&gt;
 *
 *  &lt;!-- Define other beans... --&gt;
 * </pre></blockquote>
 *
 * @see org.springframework.web.servlet.mvc.ContextLoaderListener
 * @see org.springframework.web.servlet.DispatcherServlet
 * @see org.springframework.web.servlet.mvc.ServletWrappingController
 */
public class WebContextApplicationServlet extends ApplicationServlet {

    protected final Logger log = Logger.getLogger(getClass());

    private WebApplicationContext webApplicationContext;

    /**
     * Initialize this servlet.
     *
     * @throws ServletException if there is no {@link WebApplicationContext} associated with this servlet's context
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        log.debug("finding containing WebApplicationContext");
        try {
            this.webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
        } catch (IllegalStateException e) {
            throw new ServletException("could not locate containing WebApplicationContext");
        }
    }

    /**
     * Get the containing Spring {@link WebApplicationContext}.
     * This only works after the servlet has been initialized (via {@link #init init()}).
     *
     * @throws ServletException if the operation fails
     */
    protected final WebApplicationContext getWebApplicationContext() throws ServletException {
        if (this.webApplicationContext == null)
            throw new ServletException("can't retrieve WebApplicationContext before init() is invoked");
        return this.webApplicationContext;
    }

    /**
     * Create and configure a new instance of the configured application class.
     *
     * <p>
     * The implementation in {@link WebContextApplicationServlet} invokes
     * {@link BeanFactory#createBean(Class) BeanFactory.createBean()}
     * on the associated {@link WebApplicationContext} class.
     * </p>
     *
     * @param request the triggering {@link HttpServletRequest}
     * @throws ServletException if bean creation fails
     */
    @Override
    protected Application getNewApplication(HttpServletRequest request) throws ServletException {
        Class<? extends Application> applicationClass;
        try {
            applicationClass = this.getApplicationClass();
        } catch (ClassNotFoundException e) {
            throw new ServletException("failed to create new instance of application class", e);
        }
        log.debug("acquiring new instance of " + applicationClass + " from " + this.webApplicationContext);
        try {
            return this.getWebApplicationContext().getBean(applicationClass);
        } catch (BeansException e) {
            throw new ServletException("failed to acquire new instance of " + applicationClass, e);
        }
    }
}

Now, I would like to transform my app into a mobile application thanks to TouchKit.
I saw that I am supposed to change the ApplicationServlet with this :

<servlet-class>
  	com.vaadin.touchkit.mobileapplication.MobileApplicationServlet
</servlet-class>

I guess that my Spring annotations will no longer work with the MobileApplicationServlet.

Has someone already tryed to use TouchKit and Spring ?

Any help would be appreciated.

Thank you !

Sebastian

cant you let WebContextApplicationServlet extend MobileApplicationServlet?

There’s a trick to this, but it is an easy trick.

Put this in your web.xml file (changing the names as necessary of course):

        <servlet>
        <servlet-name>pizzaorder</servlet-name>
        <servlet-class>com.vaadin.addon.touchkit.server.TouchKitApplicationServlet</servlet-class>
        <init-param>
            <description>Vaadin application class to start</description>
            <param-name>application</param-name>
            <param-value>nz.co.senanque.pizzaordermobile.PizzaorderMobile</param-value>
        </init-param>
        <init-param>
            <description>Application widgetset</description>
            <param-name>widgetset</param-name>
            <param-value>nz.co.senanque.pizzaordermobile.widgetset.PizzaordermobileWidgetset</param-value>
        </init-param>
        <init-param>
            <param-name>fallbackApplication</param-name>
            <param-value>nz.co.senanque.pizzaorder.PizzaOrder</param-value>
        </init-param>
        <init-param>
            <param-name>fallbackWidgetset</param-name>
            <param-value>com.vaadin.terminal.gwt.DefaultWidgetSet</param-value>
        </init-param>
        <init-param>
            <param-name>productionMode</param-name>
            <param-value>false</param-value>
        </init-param>
    </servlet>

So now you’re using the touchkit servlet and an application, actually two of them because there’s an optional fallback if you want. The PizzaOrderMobile class extends com.vaadin.addon.touchkit.ui.TouchKitApplication. So far this is just normal touchkit stuff from the examples.
Also add the ContextLoaderListener to the web.xml file so that Spring will load your applicationContext.xml file, just normal Spring stuff there.

Now for the trick.

Add a second Spring file to your WEB-INF directory. I call mine PizzaOrderMobile.xml after the name of my Application class.
In the onBrowserDetailsReady() in the PizzaOrderMobile Application class write some code that locates that file and loads it. This is done for every instance of the Application class that Vaadin creates, ie once for every Vaadin session == once per user login.

In that second file (PizzaOrderMobile.xml) you put this:

<bean id="application" class="nz.co.senanque.pizzaordermobile.PizzaorderMobile" factory-bean="springContextLoader" factory-method="get">
        <property name="theme" value="pizza-mobile"/>
    </bean>

What this does is call my factory bean’s get() method to fetch the bean instance of the application. There’s some messing about with ThreadLocal to store the newly created Application instance earlier and then feed it to the get method but the net result is the actual application instance for this session is retuened to Spring to wire up as required. You can add other beans into this file and they are all session dependent. They are not actually session scoped in Spring terms but they act more or less like it in that you can store session dependent info in those beans safely.

I got most of this from Archie Cobb’s dellroadstuff, see dellroad-stuff-vaadin-addon which might do everything you need except last time I looked it didn’t handle touchkit (it might do now or I might have misunderstood something). My own project source is at
pizzaOrderDemo
the factory bean is in a different project at
madura-vaadinsupport
and it is called nz.co.senanque.vaadinsupport.application.SpringApplicationLoader, it also has the code that loads the context file and messes with ThreadLocal (and is mostly Archie’s code)

(Note due to a logistic issue you’ll find the pizzaorder demo doesn’t compile just now. It will in a few hours, though, and all the code is there. Just an ivy thing.)

Edit: The ivy issue is resolved now.