Back-office application in 60 Minutes with Vaadin. Minute 1 – 15. Creating application skeleton.

Vaadin is a component-based rich Internet application Java framework that is designed to make creation and maintenance of web-based user interfaces easy. Due to server-driven programming model, Vaadin takes care of the user interface in browser and the developer concentrates on the implementation of business logic. You do not need to spend your time with browser technologies, like HTML or Javascript, you can think of the web browser as a thin client only.

This article covers development of a kind of “back-office” application. This kind of applications is aimed for maintenance staff, call center agents, marketing managers etc., that need to access some configuration of customer data, custom services of the wide computing system like a shop, insurance or finance company. The requirements for such kind of applications normally are to be cheap, agile in development and maintenance, user friendly and Nobody cares about web design and corporate fashion of web pages. For example: call center application for registering clients issues, an application for marketing staff for creating and maintenance advertising campaigns, tariff or ware management for some finance online service. It is exactly what are we going to implement with Vaadin framework.

Prerequisites

I don’t want to cover setting up the development environment in this article, please refer to the free Vaadin boot at the bottom of the article. You need the following software to run the examples:

  • Linux, Mac OS X or Windows XP or newer. I am running Ubuntu Linux 11.04 on my laptop.
  • Java SDK 1.5 or newer. I used Java 1.6.0_21 (Sun Microsystems Inc).
  • Any IDE for Java EE development: Eclipse (with Vaading plugin), IDEA Intellij or Netbeans. My favorite IDE for several years is IDEA Intellij. currently I am running version 11.0.1.
  • Apache Maven 3 (I used v. 3.0.2). A web and testing frameworks, Jetty Application Server and all required libraries are downloaded by the Maven.
  • Firefox, Chrome or Safari web browser
  • Internet connection to let Maven download some artifacts
  • Optional: Git, Apache Tomcat 6 or newer.

I am going to create and run application with Maven to avoid a coupling with any development IDEs.

Create a New Vaadin Project with Maven

Open a command line shell and go the directory where you are doing your Java development. We are going to create a sub-directory with the Vaading project skeleton there using a Maven archetype generator:

mvn -U archetype:generate -DarchetypeGroupId=com.vaadin -DarchetypeArtifactId=vaadin-archetype-clean -DarchetypeVersion=LATEST -DgroupId=com.r.vaadindemo.backoffice -DartifactId=back-office -Dversion=1.0-SNAPSHOT -Dpackaging=war

Here we go:

[email protected]:/opt/devres/vaadin-demo$ mvn -U archetype:generate -DarchetypeGroupId=com.vaadin -DarchetypeArtifactId=vaadin-archetype-clean -DarchetypeVersion=LATEST -DgroupId=com.r.vaadindemo.backoffice -DartifactId=back-office -Dversion=1.0-SNAPSHOT -Dpackaging=war
[INFO] Scanning for projects...
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/maven-metadata.xml
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml (11 KB at 9.7 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/maven-metadata.xml (20 KB at 14.7 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/maven-metadata.xml
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/maven-metadata.xml (701 B at 1.4 KB/sec)
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/maven-metadata.xml
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml (11 KB at 14.7 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/maven-metadata.xml (20 KB at 21.1 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/maven-metadata.xml
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/maven-metadata.xml (701 B at 1.4 KB/sec)
[INFO] 
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] Archetype repository missing. Using the one from [com.vaadin:vaadin-archetype-clean:1.6.1] found in catalog remote
Downloading: http://repo1.maven.org/maven2/com/vaadin/vaadin-archetype-clean/maven-metadata.xml
Downloaded: http://repo1.maven.org/maven2/com/vaadin/vaadin-archetype-clean/maven-metadata.xml (2 KB at 2.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/com/vaadin/vaadin-archetype-clean/maven-metadata.xml
Downloaded: http://repo1.maven.org/maven2/com/vaadin/vaadin-archetype-clean/maven-metadata.xml (2 KB at 2.2 KB/sec)
[INFO] Using property: groupId = com.r.vaadindemo.backoffice
[INFO] Using property: artifactId = back-office
[INFO] Using property: version = 1.0-SNAPSHOT
[INFO] Using property: package = com.r.vaadindemo.backoffice
Confirm properties configuration:
groupId: com.r.vaadindemo.backoffice
artifactId: back-office
version: 1.0-SNAPSHOT
package: com.r.vaadindemo.backoffice
 Y: : y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: vaadin-archetype-clean:LATEST
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.r.vaadindemo.backoffice
[INFO] Parameter: artifactId, Value: back-office
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.r.vaadindemo.backoffice
[INFO] Parameter: packageInPathFormat, Value: com/r/vaadindemo/backoffice
[INFO] Parameter: package, Value: com.r.vaadindemo.backoffice
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.r.vaadindemo.backoffice
[INFO] Parameter: artifactId, Value: back-office
[INFO] project created from Archetype in dir: /home/rubezham/devres/vaadin-demo/back-office
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.520s
[INFO] Finished at: Thu Feb 02 13:02:25 CET 2012
[INFO] Final Memory: 8M/73M
[INFO] ------------------------------------------------------------------------

Vaading archetype generator creates a “back-office” directory with Maven project file pom.xml, deployment descriptors and meta files web.xml, context.xml as well as simple Vaadin application class MyVaadinApplication.java.

[email protected]:/opt/devres/vaadin-demo$ cd back-office
[email protected]:/opt/devres/vaadin-demo/back-office$ find
.
./pom.xml
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/r
./src/main/java/com/r/vaadindemo
./src/main/java/com/r/vaadindemo/backoffice
./src/main/java/com/r/vaadindemo/backoffice/MyVaadinApplication.java
./src/main/webapp
./src/main/webapp/WEB-INF
./src/main/webapp/WEB-INF/web.xml
./src/main/webapp/META-INF
./src/main/webapp/META-INF/MANIFEST.MF
./src/main/webapp/META-INF/context.xml

A Maven project file pom.xml contains a project configuration:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.r.vaadindemo.backoffice</groupId>
  <artifactId>back-office</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>Vaadin Web Application</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <vaadin.version>6.7.1</vaadin.version>
    <gwt.version>2.3.0</gwt.version>
    <gwt.plugin.version>2.2.0</gwt.plugin.version>
  </properties>

I use Vaadin framework version 6.7.4 that still runs on GWT version 2.3.0, so I change the value of correspondent tag:

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <vaadin.version>6.7.4</vaadin.version>
    <gwt.version>2.3.0</gwt.version>
    <gwt.plugin.version>2.2.0</gwt.plugin.version>
  </properties>

...

    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin</artifactId>
      <version>${vaadin.version}</version>
    </dependency>

There is also a Jetty application server as a Maven plugin for the development purposes

      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <version>6.1.24</version>
        <configuration>
          <stopPort>9966</stopPort>
          <stopKey>back-office</stopKey>
          <!-- Redeploy every x seconds if changes are detected, 0 for no automatic redeployment -->
          <scanIntervalSeconds>0</scanIntervalSeconds>
          <!-- make sure Jetty also finds the widgetset -->
          <webAppConfig>
            <contextPath>/back-office</contextPath>
            <baseResource implementation="org.mortbay.resource.ResourceCollection">
              <!-- Workaround for Maven/Jetty issue http://jira.codehaus.org/browse/JETTY-680 -->
              <!-- <resources>src/main/webapp,${project.build.directory}/${project.build.finalName}</resources> -->
              <resourcesAsCSV>src/main/webapp,${project.build.directory}/${project.build.finalName}</resourcesAsCSV>
            </baseResource>
          </webAppConfig>
        </configuration>
      </plugin>

Here I change value of tag to “5″ to let Jetty to find and redeploy changed classes every 5 seconds:

          <scanIntervalSeconds>5</scanIntervalSeconds>

The context path of your web application on Jetty server is defined with tag, that means that the application is available on Jetty server under http://localhost:8080/back-office URL.

...
          <webAppConfig>
            <contextPath>/back-office</contextPath>
...

Also I extend a build configuration with unit testing support by adding test source and output directories …

  <build>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
...

… and testNG framework dependencies:

    <!-- Dependencies for unit testing -->
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>6.3.1</version>
      <scope>test</scope>
    </dependency>

We got an Vaadin application class generated by Maven archetype generator:

public class MyVaadinApplication extends Application
{
    private Window window;

    @Override
    public void init()
    {
        window = new Window("My Vaadin Application");
        setMainWindow(window);
        Button button = new Button("Click Me");
        button.addListener(new Button.ClickListener() {
            public void buttonClick(ClickEvent event) {
                window.addComponent(new Label("Thank you for clicking"));
            }
        });
        window.addComponent(button);
        
    }

The application class is instantiated once per user session per Vaadin servlet. The init() method is a initializer of the application, it is called by the framework when the application is started. It is aimed to build main layout of the page.

Generated application class is a poor java code. Main window instance is already stored in parent application class, no need to store it twice. Also the init() method looks out too heavy. I rename MyVaadinApplication class to BackOfficeApplication class. Than I refactor it in the following way:

/**
 * Back office application's "main" class
 */
public class BackOfficeApplication extends Application
{
    private static final long serialVersionUID = 1L;

    /**
     * Main initializer of the application, called on once per user session
     * */
    @Override
    public void init()
    {
        // An init() method basic initialisations like creating a main window
        // and populating it with the basic layout. Don't make it too heavy!
        mainWindow();
        mainLayout();
    }

    /**
     * Builds main window of the application
     * */
    protected void mainWindow() {
        setMainWindow(new Window("Back-office application"));
    }

    /**
     * Builds layout of the main applicaiton window
     * */
    protected void mainLayout() {
        getMainWindow().addComponent(new MainLayout());
    }
}

I have renamed the application class, therefore I need to correct a web.xml descriptor. Here application class is referred by GWT servlet:

    <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.r.vaadindemo.backoffice.BackOfficeApplication</param-value>
        </init-param>
    </servlet>

Application class initializes a main window that contains a full sized main layout component:

/**
 * Main layout for application window
 *
 */
public class MainLayout extends VerticalLayout {

    private static final long serialVersionUID = 1L;

    /**
     * Layout initialization in constructor
     * */
    public MainLayout() {
        // Full size of the layout
        this.setSizeFull();
        // Adding hello world label component
        this.addComponent(helloWorldLabel());
    }

    /**
     * Builds world label
     * */
    protected Label helloWorldLabel() {
        return new Label("Hello world!");
    }
}

Main layout component is a top element of our web application. I’ve place a “hello world” label here, it will be replaced with a real content in the next article.

Every class must be unit-tested! We testing main layout and the label with TestNG framework. Test classes are located in ${project.basedir}/src/test/java directory:

package com.r.vaadindemo.backoffice;

import com.vaadin.ui.Component;
import com.vaadin.ui.Label;
import junit.framework.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Unit test for {@link MainLayout} class
 *
 */
public class MainLayoutTest {
    public MainLayout mainLayout;

    @BeforeMethod
    public void beforeMethod() {
        mainLayout = new MainLayout();
    }

    @AfterMethod
    public void afterMethod() {
        mainLayout = null;
    }

    @Test
    public void testMainLayout() {
        Assert.assertNotNull(mainLayout);
        Assert.assertEquals("Full size layout. Check width", (float) 100, mainLayout.getWidth());
        Assert.assertEquals("Full size layout. Check height", (float) 100, mainLayout.getHeight());
        Assert.assertEquals("Check percentage units / width", MainLayout.UNITS_PERCENTAGE, mainLayout.getWidthUnits());
        Assert.assertEquals("Check percentage units / height", MainLayout.UNITS_PERCENTAGE, mainLayout.getHeightUnits());
    }

    @Test
    public void testHelloWorldLabel() {
        Assert.assertNotNull(mainLayout);
        Assert.assertNotNull("Layout has a child component", mainLayout.getComponent(0));
        Assert.assertEquals("Layout has a child label", Label.class, mainLayout.getComponent(0).getClass());
        Label helloWorldLabel = (Label) mainLayout.getComponent(0);
        Assert.assertTrue("Hello world label is visible", helloWorldLabel.isVisible());
        Assert.assertEquals("Check value of the label", "Hello world!", helloWorldLabel.getValue());
    }
}

That’s it! Application’s skeleton is ready. Lets build the project and execute unit tests:

[email protected]:/opt/devres/vaadin-demo/back-office$ mvn -U clean package
[INFO] Scanning for projects...
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for com.r.vaadindemo.backoffice:back-office:war:1.0-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-compiler-plugin is missing. @ line 22, column 15
[WARNING] 
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING] 
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING] 
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Vaadin Web Application 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ back-office ---
[INFO] Deleting /home/rubezham/devres/vaadin-demo/back-office/target
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ back-office ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/rubezham/devres/vaadin-demo/back-office/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ back-office ---
[INFO] Compiling 2 source files to /home/rubezham/devres/vaadin-demo/back-office/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ back-office ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/rubezham/devres/vaadin-demo/back-office/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ back-office ---
[INFO] Compiling 1 source file to /home/rubezham/devres/vaadin-demo/back-office/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.7.1:test (default-test) @ back-office ---
[INFO] Surefire report directory: /home/rubezham/devres/vaadin-demo/back-office/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running TestSuite
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.406 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ back-office ---
[INFO] Packaging webapp
[INFO] Assembling webapp [back-office] in [/home/rubezham/devres/vaadin-demo/back-office/target/back-office-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/home/rubezham/devres/vaadin-demo/back-office/src/main/webapp]
[INFO] Webapp assembled in [86 msecs]
[INFO] Building war: /home/rubezham/devres/vaadin-demo/back-office/target/back-office-1.0-SNAPSHOT.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.376s
[INFO] Finished at: Thu Feb 02 14:45:57 CET 2012
[INFO] Final Memory: 12M/166M
[INFO] ------------------------------------------------------------------------

The project is built, TestNG unit tests are done without errors. Now, I start-up jetty server with the back-office application:

[email protected]:/opt/devres/vaadin-demo/back-office$ mvn -U jetty:run
[INFO] Scanning for projects...
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for com.r.vaadindemo.backoffice:back-office:war:1.0-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-compiler-plugin is missing. @ line 22, column 15
[WARNING] 
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING] 
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING] 
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Vaadin Web Application 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-jetty-plugin:6.1.24:run (default-cli) @ back-office >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ back-office ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/rubezham/devres/vaadin-demo/back-office/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ back-office ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ back-office ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/rubezham/devres/vaadin-demo/back-office/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ back-office ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< maven-jetty-plugin:6.1.24:run (default-cli) @ back-office <<<
[INFO] 
[INFO] --- maven-jetty-plugin:6.1.24:run (default-cli) @ back-office ---
2012-02-02 14:49:30.358:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Configuring Jetty for project: Vaadin Web Application
[INFO] Webapp source directory = /home/rubezham/devres/vaadin-demo/back-office/src/main/webapp
[INFO] Reload Mechanic: automatic
[INFO] Classes = /home/rubezham/devres/vaadin-demo/back-office/target/classes
[INFO] Context path = /back-office
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] web.xml file = /home/rubezham/devres/vaadin-demo/back-office/src/main/webapp/WEB-INF/web.xml
[INFO] Webapp directory = /home/rubezham/devres/vaadin-demo/back-office/src/main/webapp
[INFO] Starting jetty 6.1.24 ...
2012-02-02 14:49:30.380:INFO::jetty-6.1.24
2012-02-02 14:49:30.506:INFO::No Transaction manager found - if your webapp requires one, please configure one.
2012-02-02 14:49:30.715:INFO::Started [email protected]:8080
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 5 seconds.

Application is available on the local Jetty server under http://localhost:8080/back-office/ URL:

Back-office application skeleton in brower

References

Stay tuned!

The next article:

  • Back-office application in 60 Minuteswith Vaadin. Minute 16 – 30. Adding more GUI elements

… is coming soon!

Share