Thursday, September 18, 2014

Java + Vaadin + Spring: creating a basis for application's user interface. From the very beginning.

This article shows the way of creating java application with Vaadin user interface and Spring framework features.

Create a Vaadin application is very easy if you have installed corresponding eclipse plugin. Read http://vaadin.com/eclipse to get to know how to install the plugin and create vaadin application in a few mouse clicks.

Another way of creating vaadin application quickly is using Vaadin "application" archetype. Type the following string in the command line once you're getting into workspace directory:

mvn archetype:generate -DarchetypeGroupId=com.vaadin -DarchetypeArtifactId=vaadin-archetype-application -DarchetypeVersion=7.1.8

NOTE: if you got an error : "The desired archetype does not exist (com.vaadin:vaadin-archetype-application:7.1.8)" execute this command: mvn archetype:update-local-catalog

I'd like to show old-school way of Vaadin integration into a simple web application. There are two solutions. Second solution I find more attractive, however it cannot be applied to Vaadin 6. Read more for details.

Firstly, we need to create a project

mvn archetype:generate -DgroupId={project-packaging} -DartifactId={project-name} -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

Solution #1 

Add necessary plugins and dependencies to pom.xml.

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
  <vaadin.version>7.0.0.alpha3</vaadin.version> <!-- 6.8.15 for Vaadin 6-->
  <servlet.version>3.1.0</servlet.version> 
  <spring.version>4.1.0.RELEASE</spring.version>
</properties>
...
<dependency>
  <groupId>com.vaadin</groupId>
  <artifactId>vaadin</artifactId>
  <version>${vaadin.version}</version>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId> 
  <artifactId>javax.servlet-api</artifactId> 
  <version>${servlet.version}</version> 
  <scope>provided</scope> 
</dependency>
....
<plugin> <!-- Helps to launch our application quickly with 'mvn jetty:run' -->
  <groupId>org.mortbay.jetty</groupId> 
  <artifactId>jetty-maven-plugin</artifactId> 
</plugin>
...
<!-- Wanna use Spring features in web application? Add corresponding dependencies -->

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>${spring.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>${spring.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>${spring.version}</version>
</dependency>

By default, application context file must be named applicationContext.xml and it must be located in the same directory with web.xml file. If you need to have another locaton use context parameter (<context-param> tag in web.xml file) specifying the context file name and location:
web.xml

<context-param>
  <param-name>contextConfigLocation</param-name> 
  <param-value>classpath*:/META-INF/spring/applicationContext*.xml</param-value> <!--wild card is okay -->
</context-param>
...
<!-- To load the Spring context -->
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- To allow session-scoped beans in Spring -->
<listener>
  <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>


JavaEE Specification requires to declare main servlet class which is inherited from HttpServlet (or GenericServlet if you want). In case of Vaadin application your servlet class may be inherited from com.vaadin.terminal.gwt.server.AbstractApplicationServlet.

AbstractApplicationServlet has two abstract methods to be implemented

protected abstract Application getNewApplication(HttpServletRequest request) throws ServletException;

protected abstract Class<? extends Application> getApplicationClass() throws ClassNotFoundException;

getNewApplication returns vaadin application instance
getApplicationClass returns the class object of the application instance

Application instance has the following characteristics
 - only single instance per application is required
 - globally available
 - potentially, it has many fields which could be injected
It's a good idea to have the application instance initialized in a spring context. isn't it?

Application instance should be inherited from com.vaadin.Application.
Vaadin 7 requires getRoot() method is to be overriden at application instance class, there is no such requirement in Vaadin 6, however if you need to migrate from 6-th version to 7-th you'd better take a look on com.vaadin.Application.LegacyApplication.
As this example is for Vaadin 7 we need to have one more class declared, this class must extend com.vaadin.ui.Root.
Finally, let's take a look on the code (imports and some other non-significant fields are omitted).

Servlet class
public class SampleVadinServlet extends AbstractApplicationServlet {

 protected WebApplicationContext applicationContext;

 @Override
 public void init(ServletConfig servletConfig) throws ServletException {
  super.init(servletConfig);
  applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletConfig.getServletContext());
 }

 @Override
 protected Application getNewApplication(HttpServletRequest request) throws ServletException {
  return (RootApplication)applicationContext.getBean("root");
 }

 @Override
 protected Class<? extends Application> getApplicationClass() throws ClassNotFoundException {
  return RootApplication.class;
 }
}


Application instance class
public class RootApplication extends Application {

 private SampleApp sampleApp;

 @Override
 protected Root getRoot(WrappedRequest request) throws RootRequiresMoreInformationException {
  return sampleApp;
 }

 public SampleApp getSampleApp() {
  return sampleApp;
 }

 public void setSampleApp(SampleApp sampleApp) {
  this.sampleApp = sampleApp;
 }
 
}

Root application class (required by Vaadin 7)
public class SampleApp extends Root {

    public SampleApp() {
    }
    
    @Override
    protected void init(WrappedRequest request) {
        VerticalLayout view = new VerticalLayout();
        view.addComponent(new Label("Hello Vaadin!"));
        setContent(view);
    }
}

Application context beans
<bean name="sampleApp" class="com.vbashur.servlet.SampleApp" scope="session"/> 
<bean name="root" class="com.vbashur.servlet.RootApplication" scope="session">  
 <property name="sampleApp" ref="sampleApp"/>
</bean>

web.xml

<servlet>
 <servlet-name>Vaadin Application Servlet</servlet-name>
 <servlet-class>com.vbashur.servlet.SampleVadinServlet</servlet-class>  
</servlet>
  
<servlet-mapping>
 <servlet-name>Vaadin Application Servlet</servlet-name>
 <url-pattern>/*</url-pattern>
</servlet-mapping>

We got a working example of the simpliest vaadin application.
"That's all folks!"
Wait a minute, what the heck is 'VAADIN' directory which is automatically generated by using vaadin archetype with maven? What a strange css files there? How are we going to use vaadin addons (https://vaadin.com/directory)? And how are we going to apply our own css? None of that is represented in our vaadin application and seems that we can't use many vaadin features.

Yes that's right! And we need to modify our application.
By default vaadin uses webapp/VAADIN/ folder for storing static data like images, css files and widgetsets (http://dev.vaadin.com/wiki/WidgetSet).
Under webapp/VAADIN directory I create themes/mytheme folder and add some *.scss files (https://vaadin.com/book/-/page/themes.sass.html) or css files (up to you)

Add the following string to web.xml

<servlet-mapping>
 <servlet-name>Vaadin Application Servlet</servlet-name>
 <url-pattern>/VAADIN/*</url-pattern>
</servlet-mapping>

see https://vaadin.com/wiki/-/wiki/Main/Defining+the+theme+for+a+UI to get to know more about themes

Solution #2

Dependencies in pom.xml

<properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <vaadin.version>7.3.0</vaadin.version>
 <servlet.version>3.1.0</servlet.version>
 <spring.version>4.1.0.RELEASE</spring.version>
</properties>

<dependencies>
 <dependency>
  <groupId>com.vaadin</groupId>
  <artifactId>vaadin-themes</artifactId>
  <version>${vaadin.version}</version>
 </dependency>
 <dependency>
  <groupId>com.vaadin</groupId>
  <artifactId>vaadin-server</artifactId>
  <version>${vaadin.version}</version>
 </dependency>
 <dependency>
  <groupId>com.vaadin</groupId>
  <artifactId>vaadin-client-compiled</artifactId>
  <version>${vaadin.version}</version>
 </dependency>
 <dependency>
  <groupId>com.vaadin</groupId>
  <artifactId>vaadin-client</artifactId>
  <version>${vaadin.version}</version>
  <scope>provided</scope>
 </dependency>
 <dependency>
  <groupId>com.vaadin</groupId>
  <artifactId>vaadin-push</artifactId>
  <version>${vaadin.version}</version>
 </dependency>
 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>${servlet.version}</version>
  <scope>provided</scope>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>${spring.version}</version>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>${spring.version}</version>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>${spring.version}</version>
 </dependency>
</dependencies>


<repositories>
 <repository>
  <id>vaadin-addons</id>
  <url>http://maven.vaadin.com/vaadin-addons</url>
 </repository>
 <repository>
  <id>vaadin-snapshots</id>
  <url>http://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
  <releases>
   <enabled>false</enabled>
  </releases>
  <snapshots />
 </repository>
</repositories>

Declare a class that extends com.vaadin.ui.UI - it's gonna be a root of your Vaadin application

//@Theme("mytheme") <-- uncomment for using custom theme
public class MainUI extends UI  {
    
    // protected WebApplicationContext applicationContext;

    @Override
    protected void init(VaadinRequest request) {
     
        final VerticalLayout layout = new VerticalLayout();
        
        layout.setMargin(true);
        setContent(layout);
        // applicationContext = WebApplicationContextUtils.getWebApplicationContext(VaadinServlet.getCurrent().getServletContext()); <-- uncomment for application context initilization
        
        Button button = new Button("Click Me");
        button.addClickListener(new Button.ClickListener() {
            public void buttonClick(ClickEvent event) {
                layout.addComponent(new Label("Thanks"));
            }
        });
        layout.addComponent(button);        
    }
}

web.xml
<servlet>
 <servlet-name>Vaadin Application Servlet</servlet-name>
 <servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
    <init-param>    
     <description>Vaadin UI</description>      
        <param-name>UI</param-name>
        <param-value>com.vbashur.ui.MainUI</param-value>
    </init-param>
</servlet>

<servlet-mapping>
 <servlet-name>Vaadin Application Servlet</servlet-name>
 <url-pattern>/*</url-pattern>
</servlet-mapping>

One note about web.xml, official Vaadin user manual recommends to declare version 3.0 or later at web.xml <web-app> tag if you use servlet API version 3.0 or later.
For example:

<web-app id="WebApp_ID" version="3.1"
  xmlns="http://java.sun.com/xml/ns/j2ee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
      metadata-complete="true">

That's enough for making Vaadin application work. Now let's take a look on themes, add VAADIN/themes/mytheme forlder under src/main/resources. You may have any theme name and the folder with your css files should be named correspondingly (mytheme), but the theme folder must be placed at VAADIN/themes.
Vaadin 7 provides improved css so called Saas [https://vaadin.com/book/vaadin7/-/page/themes.creating.html] for creating your own styles.
Vaadin improved styles have *.scss extension for its files and improved css syntax.
Create your styles.scss and mytheme.scss at theme directory.

style.scss
@import "mytheme.scss";

.mytheme {
  @include mytheme;
}

mytheme.scss
@import "../reindeer/reindeer.scss";
/* This contains all of your theme. */
/* If somebody wants to extend the theme she will include this mixin. */
@mixin mytheme {
  /* Include all the styles from the reindeer theme */
  @include reindeer;
  /* Insert your theme rules here */  
}

.main {
 background-color: #fee; 
}

.main-content {
 background-color: #faa; 
}

/* ------
   Footer
   ------ */
.footer {
 background:#f9f9f9;
 border-top:1px solid #ababab;
 font:bold 11px Arial, Helvetica, sans-serif;
 color:#666;
 padding:10px 0;
}

Please note the theme directory (mytheme) is a place for favicon.ico.
To apply your custom theme uncomment the line with aspect @Theme
@Theme("mytheme")
public class MainUI extends UI  

You also can use predefined themes such as valo, reindeer, liferay etc, just type the theme name as aspect argument, e.g. @Theme("liferay")

How to include initialize context? At first, create your application context at src\main\resources\META-INF\spring, then specify context file location and required context loaders at web.xml. If you're interested in context initialization details refer to this link http://habrahabr.ru/post/222579/ [Russian language] - very nice analysis!

<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>classpath*:/META-INF/spring/applicationContext*.xml</param-value> <!--wild card is okay -->
</context-param>

<!--  To load the Spring context -->
<listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!--  To allow session-scoped beans in Spring -->
<listener>
 <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener> 

Uncomment the lines with application context in MainUI.java:

    ... 
    protected WebApplicationContext applicationContext;
    ...
    applicationContext = WebApplicationContextUtils.getWebApplicationContext(VaadinServlet.getCurrent().getServletContext()); 

Another example of spring integration you can find at https://vaadin.com/wiki/-/wiki/Main/Spring+Integration

Okay, first step is done. Wish you a nice UI!

See code listings at https://bitbucket.org/vbashur/diff/src/ vaadinapp project

No comments:

Post a Comment