Vaadin Pro Tools and Liferay

So Vaadin has a complete set of basic widgets (checkboxes, input fields, comboboxes, etc.) and some advanced controls (date picker, menu bar, table, etc.).

They also have the Add-On directory which contains a slew of professional and user contributed widgets (some of my favorites are the masked text fields).

But Vaadin also has the Pro Tools - snazzy widgets with a lot of capabilities, but at a cost.  These widgets aren't free, but they are certainly not outrageous even for small shops or projects.

About Vaadin Pro Tools

There are currently four Pro Tools available:

  • Charts
  • Spreadsheet
  • TestBench
  • TouchKit

Charts is a charting package that combines Vaadin and HighCharts to deliver a responsive charting package that allows for a lot of interaction possibilities between a browser-based chart and server-side logic and data access.

Spreadsheet is just what it sounds like, it's an implementation of a spreadsheet widget that runs right in the browser complete with support for functions, editing, etc.

TestBench is an extension for JUnit which allows for UI validation from a CI perspective.  Rather than requiring a set of eyeballs to review the visual changes in a build, TestBench can use screen caps to compare and identify changes that are inconsistent or unexpected.

TouchKit is a framework for building mobile-compatible Vaadin applications.  A fresh spin on the "write once, run anywhere" concept, TouchKit allows Vaadin applications to act as native mobile apps even though they are still web-based.

Liferay and Pro Tools

So the Pro Tools would be great if we were interested in building servlets, but we're building portlets here.  Can we leverage any of these tools in Liferay?

The answer, my friend, is "Yes we can!".  Well, sort of.

So I've built portlets using Charts and Spreadsheets, but I haven't done anything with TestBench or TouchKit.

Honestly I'm not sure how much sense TestBench and TouchKit make in a portal setup since Vaadin only owns a portion of the page.  Maybe they work, maybe they don't, I wouldn't know until I tried.

There are two important caveats for using the Pro Tools in Liferay:

  1. A license file is necessary for the server (or each node in a cluster) to compile the widgetset with the Pro Tool widget.  For Liferay, you don't have to get a license per developer as listed on the Pro Tools license site because the developers are not compiling the widgetset, only the server (or nodes in the cluster) are.
  2. When compiling the widgetset, the Vaadin compiler will perform an internet call to verify the license.  This verification is outside of Liferay's control and therefore will not use Liferay's proxy settings for internet web access.  The server must have internet web access to the vaadin.com servers.  Make sure your network admin allows this kind of access from the server before undertaking this path.

Charts and Spreadsheets do work, however, so we'll start there.

Getting Vaadin Charts

The Vaadin Charts Overview provides an overview for the kinds of things you can do with Vaadin Charts, and the online demo shows what types of charts are available.

You can download the Vaadin Charts jar from the Add-On Directory (choose the Install/Download link on the right side, then click the download tab for the link to the zip file that contains the charts jar).  Expand the zip file so you'll have access to the jar file.

While you're on the directory page, be sure to click the Activate button to download your unique 30 day license code.

Vaadin 7 Control Panel Update

The first thing you're going to want to do is install/upgrade the Vaadin 7 Control Panel version 1.0.3.0.  At the time this blog is published, the 1.0.3.0 version is sitting in Liferay QA status for a week, but it should be out soon.  The new version has support for the Pro Tools as well as support for Vaadin 7.4.  Note that if this is the first time you've deployed the control panel you must restart your app server.

Compiling the WidgetSet

The Pro Tool widgets are basically Vaadin Add-Ons, but they have extra requirements for licenses which must be present at widgetset compile time.

This means that the license is only necessary when you compile the widgetset, but it is not necessary for runtime use of the Add-On.  So you need a license (well, one per server node) so the widgetset can be built as new widgets are added or a new Vaadin version is used, but otherwise the runtime use of the widget is unlimited (save whatever resource limitations you might have).

Before compiling the widgetset, we must deploy the Charts Add-On.

Go to the Vaadin 7 Control Panel and choose Add-Ons from the right side.  Click the "Choose File" button and locate the vaadin-charts-2.0.0.jar file from the downloaded zip earlier.  Click the "Upload Add-On" button to get to the confirmation page:

Deploy Charts Add On

Click "Yes" to deploy the Charts Add-On.  You'll then get to the deployed Add-Ons:

Vaadin Charts Selected

Normally you'd now be able to go and compile the widgetset with your new widgets and all would be fine.  But the Pro Tools require a license, so we'll add that by clicking Settings on the right side.

Enter Charts License

Remember the temporary license file you downloaded earlier?  Open the file using your favorite text editor and paste it into the input field labeled Vaadin Charts License.

Now you can click the "Compile WidgetSet" button and all will be fine.  If you get a license error during the compile, likely the charts license was not entered correctly or your server does not have internet access.

Building a Chart Portlet

So the shared environment is ready to go, so let's now create a simple chart portlet to see it all in action...

In order to take IDE preference out of the mix, I'm going to do everything w/ Maven directly.  Using an IDE works too, but your preferred IDE may be different from mine so if I show you the command line version, you should be able to make it work in your environment.

First we'll start a Maven project using the liferay-portlet-archetype:

mvn archetype:generate -DarchetypeGroupId=com.liferay.maven.archetypes -DarchetypeArtifactId=liferay-portlet-archetype -DarchetypeVersion=6.2.2 -DgroupId=com.dnebinger.vaadin -DartifactId=vaadin-charts-demo -Dversion=1.0.0.0

This will give us a basic Liferay portlet project for Liferay MVC, but we'll be dumping Liferay MVC for the Vaadin shared environment.  We'll start by adding the following to the pom.xml file:

<!-- Add a repository definition so we can reference the Vaadin Add Ons -->
<repositories>
	<repository>
		<id>vaadin-addons</id>
		<url>http://maven.vaadin.com/vaadin-addons</url>
	</repository>
</repositories>
...
<dependency>
	<groupId>com.vaadin</groupId>
	<artifactId>vaadin-server</artifactId>
	<version>${vaadin.version}</version>
	<scope>provided</scope>
</dependency>
<!-- I tend to use commons lang to keep the code simpler -->
<dependency>
	<groupId>commons-lang</groupId>
	<artifactId>commons-lang</artifactId>
	<version>2.6</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>org.vaadin.addon</groupId>
	<artifactId>vaadin-charts</artifactId>
	<version>2.0.0</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>com.google.code.gson</groupId>
	<artifactId>gson</artifactId>
	<version>2.2</version>
</dependency>

The portlet.xml file in the newly created project needs to be tweaked for our new Vaadin portlet.  Change the portlet.xml content to be:

<?xml version="1.0"?>

<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0">
	<portlet>
		<portlet-name>vaadin-charts-demo</portlet-name>
		<display-name>vaadin-charts-demo</display-name>
		<!-- Normal Vaadin portlets will use this class as the portlet class -->
		<portlet-class>com.vaadin.server.VaadinPortlet</portlet-class>

		<!-- Specify the main UI class which will be instantiated by the portlet -->
		<init-param>
			<name>UI</name>
			<value>com.dnebinger.vaadin.charts.demo.ChartsDemoUI</value>
		</init-param>
		<!-- Specify the use of the shared portal widgetset -->
		<init-param>
			<name>widgetset</name>
			<value>com.vaadin.portal.gwt.PortalDefaultWidgetSet</value>
		</init-param>

		<expiration-cache>0</expiration-cache>
		<supports>
			<mime-type>text/html</mime-type>
		</supports>
		<portlet-info>
			<title>vaadin-charts-demo</title>
			<short-title>vaadin-charts-demo</short-title>
			<keywords>vaadin-charts-demo</keywords>
		</portlet-info>
		<security-role-ref>
			<role-name>administrator</role-name>
		</security-role-ref>
		<security-role-ref>
			<role-name>guest</role-name>
		</security-role-ref>
		<security-role-ref>
			<role-name>power-user</role-name>
		</security-role-ref>
		<security-role-ref>
			<role-name>user</role-name>
		</security-role-ref>
	</portlet>
</portlet-app>

The liferay-portlet.xml file needs some minor changes for Vaadin compatibility:

	<portlet>
		<portlet-name>vaadin-charts-demo</portlet-name>
		<icon>/icon.png</icon>
		<!-- Instanceable indicates whether multiple copies are allowed on the same page. -->
		<instanceable>false</instanceable>
		<!-- ajaxable should always be false for Vaadin portlets -->
		<ajaxable>false</ajaxable>
	</portlet>

Because we're using the shared environment and provided scope in the pom.xml file, we need to add dependencies into the liferay-plugin-package.properties file:

name=vaadin-charts-demo
module-group-id=liferay
module-incremental-version=1
tags=
short-description=
change-log=
page-url=http://www.liferay.com
author=Liferay, Inc.
licenses=LGPL

# First the vaadin jars, then the addon jar(s), then spring jars.
portal-dependency-jars=\
	vaadin-server.jar,vaadin-shared.jar,guava-vaadin.jar,atmosphere-runtime-vaadin.jar,\
	jsoup.jar,streamhtmlparser-jsilver-vaadin.jar,vaadin-slf4j-jdk14.jar,json-java.jar,\
	commons-beanutils.jar,commons-collections.jar,commons-lang.jar,commons-io.jar,\
	commons-pool.jar,commons-configuration.jar,commons-digester.jar,\
	spring-core.jar,spring-asm.jar,spring-context.jar,spring-context-support.jar,\
	spring-expression.jar,spring-transaction.jar,spring-web.jar,spring-aop.jar,\
	vaadin-charts.jar

Next is to develop the ChartsDemoUI class.  Below is one that I put together from the Vaadin charts demo site source for a pie chart:

package com.dnebinger.vaadin.charts.demo;

import java.util.Arrays;
import java.util.Random;

import com.vaadin.addon.charts.Chart;
import com.vaadin.addon.charts.model.ChartType;
import com.vaadin.addon.charts.model.Configuration;
import com.vaadin.addon.charts.model.DataSeries;
import com.vaadin.addon.charts.model.DataSeriesItem;
import com.vaadin.addon.charts.model.Labels;
import com.vaadin.addon.charts.model.PlotOptionsPie;
import com.vaadin.addon.charts.model.YAxis;
import com.vaadin.addon.charts.model.style.Color;
import com.vaadin.addon.charts.model.style.SolidColor;
import com.vaadin.addon.charts.themes.ValoLightTheme;
import com.vaadin.annotations.Theme;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Component;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

@Theme("valo")
public class ChartsDemoUI extends UI {

	private VerticalLayout mainLayout;

	@Override
	protected void init(VaadinRequest request) {
		// create the main vertical layout
		mainLayout = new VerticalLayout();

		// give it some dimensions
		mainLayout.setWidth("650px");
		mainLayout.setHeight("500px");

		// set the layout as the content area.
		setContent(mainLayout);
		
		mainLayout.addComponent(getChart());
	}

    private static Random rand = new Random(0);
    private static Color[] colors = new ValoLightTheme().getColors();

    protected Component getChart() {
        Component ret = createChart();
        
        ret.setWidth("100%");
        ret.setHeight("450px");
        
        return ret;
    }

    public static Chart createChart() {
        rand = new Random(0);

        Chart chart = new Chart(ChartType.PIE);

        Configuration conf = chart.getConfiguration();

        conf.setTitle("Browser market share, April, 2011");

        YAxis yaxis = new YAxis();
        yaxis.setTitle("Total percent market share");

        PlotOptionsPie pie = new PlotOptionsPie();
        pie.setShadow(false);
        conf.setPlotOptions(pie);

        conf.getTooltip().setValueSuffix("%");

        DataSeries innerSeries = new DataSeries();
        innerSeries.setName("Browsers");
        PlotOptionsPie innerPieOptions = new PlotOptionsPie();
        innerSeries.setPlotOptions(innerPieOptions);
        innerPieOptions.setSize(237);
        innerPieOptions.setDataLabels(new Labels());
        innerPieOptions.getDataLabels().setFormatter(
                "this.y > 5 ? this.point.name : null");
        innerPieOptions.getDataLabels().setColor(new SolidColor(255, 255, 255));
        innerPieOptions.getDataLabels().setDistance(-30);

        Color[] innerColors = Arrays.copyOf(colors, 5);
        innerSeries.setData(new String[] { "MSIE", "Firefox", "Chrome",
                "Safari", "Opera" }, new Number[] { 55.11, 21.63, 11.94, 7.15,
                2.14 }, innerColors);

        DataSeries outerSeries = new DataSeries();
        outerSeries.setName("Versions");
        PlotOptionsPie outerSeriesOptions = new PlotOptionsPie();
        outerSeries.setPlotOptions(outerSeriesOptions);
        outerSeriesOptions.setInnerSize(237);
        outerSeriesOptions.setSize(318);
        outerSeriesOptions.setDataLabels(new Labels());
        outerSeriesOptions
                .getDataLabels()
                .setFormatter(
                        "this.y > 1 ? ''+ this.point.name +': '+ this.y +'%' : null");

        DataSeriesItem[] outerItems = new DataSeriesItem[] {
                /* @formatter:off */
                new DataSeriesItem("MSIE 6.0", 10.85, color(0)),
                new DataSeriesItem("MSIE 7.0", 7.35, color(0)),
                new DataSeriesItem("MSIE 8.0", 33.06, color(0)),
                new DataSeriesItem("MSIE 9.0", 2.81, color(0)),
                new DataSeriesItem("Firefox 2.0", 0.20, color(1)),
                new DataSeriesItem("Firefox 3.0", 0.83, color(1)),
                new DataSeriesItem("Firefox 3.5", 1.58, color(1)),
                new DataSeriesItem("Firefox 3.6", 13.12, color(1)),
                new DataSeriesItem("Firefox 4.0", 5.43, color(1)),
                new DataSeriesItem("Chrome 5.0", 0.12, color(2)),
                new DataSeriesItem("Chrome 6.0", 0.19, color(2)),
                new DataSeriesItem("Chrome 7.0", 0.12, color(2)),
                new DataSeriesItem("Chrome 8.0", 0.36, color(2)),
                new DataSeriesItem("Chrome 9.0", 0.32, color(2)),
                new DataSeriesItem("Chrome 10.0", 9.91, color(2)),
                new DataSeriesItem("Chrome 11.0", 0.50, color(2)),
                new DataSeriesItem("Chrome 12.0", 0.22, color(2)),
                new DataSeriesItem("Safari 5.0", 4.55, color(3)),
                new DataSeriesItem("Safari 4.0", 1.42, color(3)),
                new DataSeriesItem("Safari Win 5.0", 0.23, color(3)),
                new DataSeriesItem("Safari 4.1", 0.21, color(3)),
                new DataSeriesItem("Safari/Maxthon", 0.20, color(3)),
                new DataSeriesItem("Safari 3.1", 0.19, color(3)),
                new DataSeriesItem("Safari 4.1", 0.14, color(3)),
                new DataSeriesItem("Opera 9.x", 0.12, color(4)),
                new DataSeriesItem("Opera 10.x", 0.37, color(4)),
                new DataSeriesItem("Opera 11.x", 1.65, color(4))
                /* @formatter:on */
        };

        outerSeries.setData(Arrays.asList(outerItems));
        conf.setSeries(innerSeries, outerSeries);
        chart.drawChart(conf);

        return chart;
    }

    /**
     * Add a small random factor to a color form the vaadin theme.
     *
     * @param colorIndex
     *            the index of the color in the colors array.
     * @return the new color
     */
    private static SolidColor color(int colorIndex) {
        SolidColor c = (SolidColor) colors[colorIndex];
        String cStr = c.toString().substring(1);

        int r = Integer.parseInt(cStr.substring(0, 2), 16);
        int g = Integer.parseInt(cStr.substring(2, 4), 16);
        int b = Integer.parseInt(cStr.substring(4, 6), 16);

        double opacity = (50 + rand.nextInt(95 - 50)) / 100.0;

        return new SolidColor(r, g, b, opacity);
    }
}

Now we can build and deploy our shiny new Vaadin Charts portlet.  Drop it on a page and you'll get the following:

Sample Chart

The other charts from the Vaadin demo site, http://demo.vaadin.com/charts/, can also be used.

Conclusion

Certainly the code for the chart may seem convoluted, but it is not all that bad.  Had I written the code instead of just copying the example code from http://demo.vaadin.com/charts/#DonutChart, I would have added more comments to explain what was going on.  Let me just say that although the code seems convoluted, it is easy to create much simpler charts.

It's important to note that, just like all other Vaadin code, the chart is written and implemented completely in Java.  You don't have to worry about HTML, Javascript, etc.  You get a responsive chart that looks great and is backed by whatever data you gather to drive it (be it jdbc queries, data coming from ServiceBuilder or the Liferay APIs, etc).

As indicated earlier, I have created Vaadin portlets that use the Charts widget as well as the Spreadsheet widget.  Maybe I'll put together another blog post with a spreadsheet portlet, but for now take my word for it that it works.

A final word about Licensing...  The way that the Vaadin Pro Tools licenses work, by default you purchase a license per developer.  The developer, with the license, can build a Vaadin project where the widgetset is compiled for the project (either a Vaadin servlet or a Vaadin portlet from the Vaadin archetypes).  The compiled project can be built and deployed to an application server or cluster with no additional licensing issues.

Since the shared Vaadin environment compiles the widgetset on each node, a license is needed per instance when compiling the widgetset with one (or more) of the Pro Tools.  Note, however, that since the developers are not compiling the widgetset for the deployed project, developers themselves do not need the license, only the servers do.

Since the license applies to the compiled widgetset, not a compiled project, you can use unlimited instances of the widgets in your portlets.  1,000 charts?  Sure, no extra cost to build and display them.

 

ブログ
Great example David! Appreciate your focus on Vaadin and Liferay integration. Is there a similar example for using the Vaadin spreadsheet demo with the "self contained" approach? I understand the shared approach will not be supported in Liferay 7 and have migrated to the "self contained" approach, and want to exploit the spreadsheet and charts Vaadin addons.
I'm not aware of any standalone version, but I believe you could use the stuff I've set up here to build a standalone version.

I wouldn't rule out the shared approach for V7 on L7. I haven't had the cycles but now that the new alphas are available on CE I've been trying to carve out some time trying to get the V7CP reworked for L7 compatibility.

When I get to it, I'll post up here one way or the other...
Hi, David!
I have a big problem connected to using Vaadin Spreadsheet 2.0.0.alpha1 with Vaadin 8.1.4 on Liferay 7 GA4. How to install vaadin-spreadsheet to portal? I'm able to run vaadin 8 portlet, but I don't know what I have to do else. I've tried to add gradle dependencies:
java -jar blade.jar sh start ..... poi-3.15.jar
java -jar blade.jar sh start ..... vaadin-spreadsheet-2.0.0.alpha1.jar
but without success with the error "Can't resolve org.apache.poi dependency"
Please, help!