Tags

, , ,

I’ve been testing out the Vaadin framework lately on some personal projects and was very impressed with the look and feel and also as to how easy it was to get it up and running. If you are a Java developer the learning curve is almost non-existent as all the work is done purely on the server side and Vaadin takes care of all the client side stuff for you.

I also love the productivity gains that the  Spring Framework offers but unfortunately all the resources I found on integrating both of these frameworks seemed to be either outdated or quite incomplete. Because of these reasons I thought I’ll get a simple demo application going and document the things that had to be done to integrate both of these frameworks together.

The Demo Application

A sample project demonstrating Vaadin and Spring working together can be found in my GitHub repository here.

The demo application consists of the following classes:

  1. VaadinSpringDemoApplication: extension of com.vaadin.Application, the entry point of the application.
  2. MainWindow: extension of com.vaadin.ui.Window used to demonstrate session scoped object injection.
  3. TextLabelService: used to demonstrate service object injection.

The VaadinSpringDemoApplication class uses Spring Autowiring to inject an instance of MainWindow, which in turn uses Autowiring to inject an instance of TextLabelService.

Running the Demo Application

The demo application can be run by issuing the command:

mvn jetty:run

You can then view the application by visiting the URL localhost:8080

Dependency Injection of Vaadin Components

Using dependency injection with Vaadin components aren’t as straight forward as it is with usual Spring components due to the following two reasons:

  1. One issue here is that the Vaadin components are not managed by the Spring container. The VaadinSpringDemoApplication class is instantiated and managed by the servlet container and since the Spring container does not take part in the management of this class, it cannot inject any of the dependencies required by it or any of the classes used by it. We need to get the AspectJ compiler (or loadtimeweaver) involved in this process so that these dependencies can be injected as part of the instantiation of the main Application class.
  2. We would like some of the injected components to be instantiated for each new session so that state associated with one user is not shared or affected by that of another. Normally its good practice for components to not contain user or session specific state so that the same component instances can be shared between sessions, but this becomes unavoidable when it comes to UI components and such. We get around this issue by scoping such components to the current session.

Configuring the Application

Configuring Maven

First we need to configure Maven by adding the following dependencies:

  <dependencies>
    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin</artifactId>
      <version>${vaadin.version}</version>
    </dependency>
    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!-- Required due to a bug in Spring, see https://jira.springsource.org/browse/SPR-6819 -->
    <dependency>
      <groupId>javax.persistence</groupId>
      <artifactId>persistence-api</artifactId>
      <version>1.0</version>
      <optional>true</optional>
    </dependency>
    <!-- AspectJ dependencies -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${aspectj.version}</version>
    </dependency>
  </dependencies>

In addition to the spring dependencies we also need to configure the AspectJ plugin so that we can get compile time weaving going so as to enable dependency injection:

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>${aspectj-maven-plugin.version}</version>
        <configuration>
          <!-- Required as the plugin does not resolve this by default -->
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.target}</target>
          <aspectLibraries>
            <aspectLibrary>
              <groupId>org.springframework</groupId>
              <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
          </aspectLibraries>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>test-compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

Configuring Spring

Now that we’ve configured maven, we have to tell Spring where to find our components so that they can be injected by Spring. We do this by introducing a spring context file with the following content:

<?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: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:spring-configured/>
    <context:component-scan base-package="com.navinpeiris.vaadin_spring"/>

</beans>

Configuring the Servlet Container

Since we want Spring to know when the servlet container instantiates the resources we want configured, we need to add the following to the web.xml file:

...
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
...

Since we also want Spring to be aware of when a new sessions are started, we also need to add the following:

...
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>
...

Annotating the Required Classes

Firstly, we want to annotate the main Application class with the @Configurable annotation so that Spring knows that this class is eligible for Spring driven configuration:

@Configurable
public class VaadinSpringDemoApplication extends Application {

Service Objects

Next, we need to annotate the necessary component classes so that they become eligible for dependency injection.

We would like some components to be shared across all the user sessions, for example, those providing common services, management functions, or data access objects. The TextLabelService is used as a demonstration of such a class in our example application.

We make service level objects applicable for injection by simply annotating the class with the @Component annotation, for example:

@Component
public class TextLabelService {

Session Scoped Objects

In comparison, we would like some components to be instantiated per session, for example Vaadin UI components that contain session specific state. We can achieve this by annotating such classes with the @Scope("session") annotation. The RequestContextListener that we added to web.xml will then help us instantiate a new instance of such objects per session. An example of such a class in our demo application is the MainWindow class which is annotated as follows:

@Component
@Scope("session")
public class MainWindow extends Window {

Injecting the Components

We can now inject the components configured in above using the @Autowire annotation as follows:

Now that we’ve got all this setup, we can inject the MainWindow class into any other class using the @Autowire annotation as follows:

    @Autowired
    private MainWindow mainWindow;
    ....
    @Autowired
    private TextLabelService textLabelService;

Demonstrating Spring Integration

If we now startup our demo application and navigate to localhost:8080 we should be able to see the following text in the browser:

Vaading-Spring Demo
MainWindow instanceId: 1
TextLabelService instanceId: 1

This shows us that both of the required components were successfully injected, and that everything works as expected.

If we now open up another browser (to force a new session to be started) and go to the same URL, we should see text similar to:

Vaading-Spring Demo
MainWindow instanceId: 2
TextLabelService instanceId: 1

This shows us that a new instance of the MainWindow was created for this session, but we are still using the same instance of TextLabelService as the previous session.

About these ads