Supporting IE10 on a Rich Internet Application with Vaadin – the Vaadin 7 migration

by Frans FlippoNovember 12, 2013

Amidst the popularity of JavaScript-CSS frameworks like Bootstrap and Angular.js, Java developers like myself are still most comfortable with what we know – Java code. Vaadin provides a framework for Java developers to write rich internet applications that are highly interactive while usually not having to write a line of Javascript code. It builds on top of GWT, taking care of synchronizing server state with client state, which means that most of the time all you have to deal with is Java code — not Java code that’s compiled to JavaScript; real, server-side Java code.

At Trifork we especially like Vaadin because it integrates very nicely into the Liferay portal. This means you can write highly interactive portlets using Vaadin and deploy them to Liferay. My colleague David described this in detail last year in a different blog post.

The great migration

So we had two Vaadin 6-based portlets running for a client in a Liferay 6 portal. However, browsers change and with the release of Internet Explorer 10 we started to get complaints that using the portlets on this browser led to JavaScript errors. It was time to support IE10. Which meant a major upgrade from Vaadin 6 to Vaadin 7.1.

What changed?

Vaadin 7 brought a major rewrite of the Vaadin API: interfaces were changed, classes were moved, a new Connector class was introduced for custom widgets, client polling was changed and server push was introduced, and many other things. In general, the API feels more solid and mature now. Vaadin 7 makes more use of Java 5 features like enums and generics. In addition, widgetset compilation is now easier as under Maven everything is handled by the Vaadin plugin, including GWT compilation.

The migration is described briefly on the Vaadin website, but especially the details of upgrading a Vaadin portlet in Liferay are difficult find. I hope to provide in this blog post a comprehensive guide to upgrading a Vaadin portlet in Liferay from Vaadin 6 to Vaadin 7. This will be a very technical hands-on one, so hold on…!

The Vaadin application

In our case we had to make the following changes to the Vaadin applications:

  1. Change dependencies – Vaadin went from a single vaadin.jar to separate JARs for the client side, the server side, and shared classes. So instead of a single dependency vaadin-6.7.1.jar, we now have three dependencies: vaadin-server-7.1.6.jar, vaadin-shared-7.1.6.jar, and vaadin-shared-deps-1.0.2.jar. Additionally, Vaadin now includes the GWT classes in its own JARs, so we had to remove dependencies on gwt-user as well.

    vaadin-application-code

  2. Change Application to UI – In Vaadin 6, the Application was the entry point to your application, and you pointed the Vaadin servlet (in web.xml) or Vaadin portlet (in portlet.xml) to your com.vaadin.Application sub-class. In Vaadin 7, you instead supply a subclass of the new com.vaadin.ui.UI. The Application class still exists behind the scenes, but it is rarely necessary to provide your own implementation. Also, just call setContent on the UI to set its root component. There’s no more “main window” concept; the UI is the browser window. (As this article actually describes, Vaadin now supports multi-tabbed browsing, where each tab is linked to a separate UI instance that maintains its own state; I haven’t tried this, but it sounds really cool. For now, you can stick with the old behavior (more or less) by annotating your UI class with @PreserveOnRefresh.)

    The UI.init() method takes a VaadinRequest object, and you should override it to build up your GUI. VaadinRequest is either a VaadinServletRequest or a VaadinPortletRequest depending on your environment and it will provide direct access to the HttpServletRequest or PortletRequest that caused the UI to be created, so you don’t need to implement HttpServletRequestListener and PortletRequestListener anymore to get to them. This is useful for authorization and personalization.

    We were overriding the getSystemMessages class in our Application in order to set a custom message for the Vaadin “Communication error” message. This is no longer the place to do this. Instead, we needed to subclass the VaadinServlet and VaadinPortlet for this:

    public class MyVaadinServlet extends VaadinServlet {
    
      private static final long serialVersionUID = 1L;
    
      @Override
      public void servletInitialized() throws ServletException {
        super.servletInitialized();
        getService().setSystemMessagesProvider(new MySystemMessagesProvider());
      }
    
    }
    
    public class MySystemMessagesProvider implements SystemMessagesProvider {
      private static final long serialVersionUID = 1L;
    
      @Override
      public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo) {
        CustomizedSystemMessages msgs = new CustomizedSystemMessages();
        msgs.setCommunicationErrorCaption("...");
        msgs.setCommunicationErrorMessage("...");
        return msgs;
      }
    }
    
  3. Change web.xml and/or portlet.xml – The Vaadin servlet was moved from com.vaadin.terminal.gwt.server.ApplicationServlet to com.vaadin.server.VaadinServlet and web.xml needs to reflect this. Additionally, the servlet needs an init parameter UI pointing to your UI class (as described in the previous point). The application parameter can go.

    The Vaadin portlet was moved from com.vaadin.terminal.gwt.server.ApplicationPortlet2 to com.vaadin.server.VaadinPortlet, so change this in portlet.xml. Additionally, as with the Vaadin servlet, the init parameter UI referencing your UI class replaces the application parameter.

    Our portlet.xml now looks like this (remember we have a custom VaadinPortlet; you will most likely just use com.vaadin.server.VaadinPortlet directly as portlet-class):

    <?xml version="1.0" encoding="UTF-8"?>
    <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>my-portlet</portlet-name>
        <display-name>My portlet</display-name>
        <portlet-class>nl.trifork.vaadin.MyVaadinPortlet</portlet-class>
        <init-param>
          <name>UI</name>
          <value>nl.trifork.vaadin.MyUI</value>
        </init-param>
        <supports>
          <mime-type>text/html</mime-type>
          <portlet-mode>view</portlet-mode>
          <portlet-mode>edit</portlet-mode>
          <portlet-mode>help</portlet-mode>
        </supports>
        <portlet-info>
          <title>My portlet</title>
        </portlet-info>
      </portlet>
    </portlet-app>
    
  4. Explicitly add a VerticalLayout to Panels – In Vaadin 6, Panels had an implicit VerticalLayout, and the addComponent method on Panel called the corresponding method on the VerticalLayout. Now, Panel has no more addComponent method. It just has a single content component that can be anything you want, including a VerticalLayout. One detail is that the default VerticalLayout of the old Panel had a default 18px margin set, so to reproduce the old behavior in your Panel, use the following code:
      VerticalLayout content = new VerticalLayout();
      content.setMargin(true);
      Panel panel = new Panel(content);
      layout.addComponent(...);
      ...
    

    vaadin-progressbar

  5. Replace ProgressIndicator with ProgressBar – there’s three things to keep in mind here:
    1. the ProgressIndicator class is deprecated and the ProgressBar replaces it
    2. the progress bar doesn’t do polling for you anymore; this functionality has been moved to the UI class, so you need to call UI.setPollInterval to have the client periodically request updates from the server and UI.setPollInterval(-1) to disable polling again once the server side thread is done and you’ve hidden the progress bar
    3. since your server-side thread will be updating your progress bar (and possibly other parts of your GUI) while the client polling-initiated request might try to do the same, Vaadin provides a locking mechanism that you need to use to avoid synchronization errors. In Vaadin 6, this was implemented by using Java-level locking (with synchronized blocks) on the Application object. In Vaadin 7, locking is done by calls to VaadinSession.lock() and VaadinSession.unlock(), like this:
        getUI().getSession().lock();
        try {
          progressIndicator.setValue(newValue);
        } finally {
          getUI().getSession().unlock();
        }
      

      The try ... finally is essential, or the session would be locked forever if anything went wrong in between the lock() and unlock() calls. See The API documentation for more details.

  6. Replace UriFragmentUtility with UI.addUriFragmentChangedListener and Page.setFragmentBack button and bookmarking support was previously handled by a kludgy component called UriFragmentUtility. Now, it’s a proper part of the Vaadin API. See The Book of Vaadin for more details.
  7. Update import statements – Many classes were moved to a different package, but a simple “Organize imports” (Ctrl-Shift-O in Eclipse) will fix that.
  8. Fix deprecation warnings – this applies to
    • constants that are now enums, e.g. Notification.Type, Label.ContentMode
    • event handlers that are now more strongly typed, e.g. change component.addListener(valueChangeListener) to component.addValueChangeListener(valueChangeListener); this also removes ambiguity in classes that implemented multiple Listener interfaces

Custom components and add-ons

Probably the most profound change in Vaadin 7 is the client-side architecture. In the old situation you had a server side component, and a client side component. The server-side component had a @ClientWidget annotation pointing to the client-side (GWT) implementation.

In Vaadin 7, a third Connector class is introduced which aims to separate the data transfer from the painting of the widget. The Connector is now annotated with the new @Connector annotation, and the @ClientWidget is no longer used.

Vaadin claims to ease migrating from the old situation by supplying a LegacyConnector, but unfortunately it’s far from trivial to migrate an old component. In some cases, a complete rewrite might be easier. Luckily, of the two add-ons we used, one (image strip) had been upgraded by the autor, and our modifications were easy to reapply afterwards; the other was an extension of Vaadin’s own twincolselect and was straightforward to upgrade. For more complicated situations, expect to invest a lot of time, or pay for Vaadin’s technical support.

Liferay integration

Liferay has for a while had good integration with Vaadin, running Vaadin portlets while having them look the same as other portlets.

Liferay ships with Vaadin JARs, themes, and widget sets, and we’ll need to upgrade these all to their respective Vaadin 7 versions. In addition, if you’re using add-ons, you’ll need the new Liferay Vaadin Control Panel to build a custom widget set containing the add-ons you need.

  1. Replace Vaadin JARs in Liferay – remove the existing vaadin.jar in WEB-INF/lib and copy the new Vaadin JARs and their dependencies in their place:

    as well as dependencies of these:

  2. Update the Vaadin resources in Liferay – download the following JARs and extract their VAADIN folders and their contents to Liferay’s html directory (e.g. cd html; unzip vaadin-server.jar 'VAADIN/*'):
  3. Update portal-ext.properties – Add or update the following properties in your portal-ext.properties. Note: it’s important that the widgetset be one that does not exist yet in your Liferay’s html/VAADIN/widgetsets directory; we’ll get to that in the next point when we compile a new widget set.
    # Path under which the VAADIN directory is located.
    # (/html is the default so it is not needed.)
    # vaadin.resources.path=/html
    
    # Portal-wide widget set
    vaadin.widgetset=com.vaadin.MyCustomWidgetSet
    
    # Theme to use
    vaadin.theme=reindeer
    
  4. Compile a new widget set – if you’re using add-ons then you will need to compile a Vaadin 7 widget set with those add-ons included. For this we install the new liferay-vaadin-plugin. You can download a prepackaged version from the Vaadin website (log-in required). However, this version doesn’t compile for IE10, which is what this whole thing was about. So we made a little code change. E-mail me if you want that version, or download the liferay-vaadin-plugin sources yourself and add the following lines in src/main/java/com/arcusys/liferay/vaadinplugin/util/WidgetsetUtil.java, line 154 (below the line that reads printStream.print("\n");:
      // Force ie10 inclusion
      printStream.print(
        "<inherits name=\"com.google.gwt.useragent.UserAgent\"/>\n");
      printStream.print(
        "<extend-property name=\"user.agent\" values=\"ie10\"/>\n");
    

    Now deploy the plugin in the usual way by dropping the WAR file in Liferay’s deploy directory. If you log in to Liferay as an administrator now, you can select “Vaadin configuration” from the Control Panel:

    Opening the Vaadin configuration screen

    The Vaadin configuration screen will open up:

    The Vaadin configration panel

    Check that that the “Active Widget Set” is the one you entered in your portal-ext.properties. If if still says com.vaadin.portal.gwt.PortalDefaultWidgetSet, or -worse-, com.vaadin.DefaultWidgetSet, check portal-ext.properties and restart your Liferay application server.

    Now you’re almost ready to compile your widget set. First:

    1. Copy the add-on JARs to Vaadin’s WEB-INF/lib. Remember the add-ons must be built with Vaadin-7 or this will not work. In other words, you will need to upgrade all of your add-ons! Unfortunately, not all add-ons have been upgraded yet. Not by far…
    2. Put Vaadin’s client JARs in in the Liferay root under WEB-INF/vaadin-client-jars (which you will need to create). The widget set compiler needs the following JARs to be present there:

    Now click the “Re-scan” link, and select the add-ons you want included in your widget set. Finally, press the Compile Widget Set button, sit back, and wait. You can follow the progress in the Output Console. Your shiny new Vaadin 7 widget set will be in Liferay’s html/VAADIN/widgetsets/com.vaadin.MyCustomWidgetSet (or whatever name you chose for it).

  5. You’re done! Your Vaadin 7-based portlet should now be running under Liferay, with IE10 support. If you have a Liferay theme that styles your Vaadin widgets, or a custom Vaadin theme, you will most likely need to change the stylesheet. The Firebug extension on Firefox or the Web inspector on WebKit-based browsers will be of help here.

Automating it

We’ve scripted some of the update steps, including downloading and replacing the Vaadin JARs and resources in Liferay. Run it from your Tomcat home (i.e. the directory that contains bin, conf, webapps, etc.). Use at your own risk 😉 The source code is at the bottom of this blog post. You’ll need to have curl, unzip and basic Unix tools installed to run it. Tested on Mac OS X 10.6.8 and Ubuntu.

Finally

Please let us know: was this post useful? What problems are you running into upgrading to Vaadin 7? Any experiences you can share with other readers? We’d love to hear it. Just use the comment box below.

Links

  1. Migrating from Vaadin 6 to Vaadin 7 (vaadin.com)
  2. Migrating from Vaadin 7.0 to Vaadin 7.1 (vaadin.com)
  3. Upgrading to Vaadin 7 Step by Step (streamhead.com)
  4. Integrating Vaadin 7 with Liferay (vaadin.com)
  5. Vaadin Control Panel for Liferay (vaadin.com)

The Liferay upgrade script

#!/bin/bash

# Variables
CATALINA_HOME=`pwd`
PORTAL_EXT_PROPERTIES=$CATALINA_HOME/lib/portal-ext.properties
LIFERAY_WEBAPP=$CATALINA_HOME/webapps/ROOT
VAADIN_CLIENT=$LIFERAY_WEBAPP/WEB-INF/vaadin-clients-jars
TEMP=$CATALINA_HOME/temp
VAADIN_THEME=reindeer

# Check required commands are available
echo -n "curl: "
if ! which curl; then
  echo curl not found. Please install curl and try again.
  exit 1
fi
echo -n "unzip: "
if ! which unzip; then
  echo unzip not found. Please install unzip and try again.
  exit 1
fi

# Validations
if [ ! -d $CATALINA_HOME/webapps ]; then
  echo $CATALINA_HOME/webapps does not exist. Script must be run from a Tomcat directory. Aborting.
  exit 1
fi
echo "Tomcat directory: $CATALINA_HOME"
cd $CATALINA_HOME

if [ ! -d $LIFERAY_WEBAPP ]; then
  echo $LIFERAY_WEBAPP not found.
  exit 1
fi

if [ ! -f $LIFERAY_WEBAPP/WEB-INF/lib/portal-impl.jar ]; then
  echo $LIFERAY_WEBAPP doesn\'t seem to be a Liferay installation. Aborting.
  exit 1
fi
echo "Liferay web application: $LIFERAY_WEBAPP"

LIFERAY_VERSION=`unzip -c $LIFERAY_WEBAPP/WEB-INF/lib/portal-impl.jar META-INF/MANIFEST.MF|grep "Liferay-Portal-Version: "|awk -F": " '{ print $2 }'|sed -e 's/[^0-9\.]//g'`
if [ "x$LIFERAY_VERSION" == "x" ]; then
  LIFERAY_VERSION="<unknown>"
fi
echo "Liferay version: $LIFERAY_VERSION"

echo "This will replace the Vaadin installation bundled with Liferay with a Vaadin 7.1.6 installation."
echo -n "Continue? [y/N] "
read RESP
if [ "x$RESP" != "xy" -a "x$RESP" != "xY" ]; then
  exit 0
fi

if [ "x$LIFERAY_VERSION" != "x6.1.1" ]; then
  echo -n "This script has only been tested with Liferay version 6.1.1. Found: \"${LIFERAY_VERSION}\". Continue? [y/N] "
  read RESP
  if [ "x$RESP" != "xy" -a "x$RESP" != "xY" ]; then
    exit 0
  fi
fi

# Remove existing Vaadin installation
echo
echo Removing old Vaadin JARs...
echo - `pwd`/$LIFERAY_WEBAPP/html/VAADIN
rm -rf $LIFERAY_WEBAPP/html/VAADIN
echo - `pwd`/$LIFERAY_WEBAPP/WEB-INF/lib/vaadin.jar
rm -f $LIFERAY_WEBAPP/WEB-INF/lib/vaadin.jar

# Download JARs in parallel from Maven's central repository
echo
echo "Downloading Vaadin JARs (this may take a few minutes)..."
mkdir -p $VAADIN_CLIENT
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-server/7.1.6/vaadin-server-7.1.6.jar >$LIFERAY_WEBAPP/WEB-INF/lib/vaadin-server.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-shared/7.1.6/vaadin-shared-7.1.6.jar >$LIFERAY_WEBAPP/WEB-INF/lib/vaadin-shared.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-shared-deps/1.0.2/vaadin-shared-deps-1.0.2.jar >$LIFERAY_WEBAPP/WEB-INF/lib/vaadin-shared-deps.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=org/jsoup/jsoup/1.6.3/jsoup-1.6.3.jar >$LIFERAY_WEBAPP/WEB-INF/lib/jsoup.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-themes/7.1.6/vaadin-themes-7.1.6.jar >$TEMP/vaadin-themes.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-client-compiled/7.1.6/vaadin-client-compiled-7.1.6.jar >$TEMP/vaadin-client-compiled.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-client-compiler/7.1.6/vaadin-client-compiler-7.1.6.jar >$VAADIN_CLIENT/vaadin-client-compiler.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-client/7.1.6/vaadin-client-7.1.6.jar >$VAADIN_CLIENT/vaadin-client.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=com/vaadin/vaadin-client-compiler-deps/1.0.2/vaadin-client-compiler-deps-1.0.2.jar >$VAADIN_CLIENT/vaadin-client-compiler-deps.jar &
curl -f -s -S http://search.maven.org/remotecontent?filepath=javax/validation/validation-api/1.0.0.GA/validation-api-1.0.0.GA.jar >$LIFERAY_WEBAPP/WEB-INF/lib/validation-api.GA.jar
curl -f -s -S http://search.maven.org/remotecontent?filepath=javax/validation/validation-api/1.0.0.GA/validation-api-1.0.0.GA-sources.jar >$LIFERAY_WEBAPP/WEB-INF/lib/validation-api.GA-sources.jar

FAILED=0
for job in `jobs -p`; do
  wait $job >/dev/null || let FAILED+=1
done
if [ $FAILED -gt 0 ]; then
  echo Some downloads failed. Aborting.
  exit 1
fi
echo Validating JARs...
unzip -tqq $LIFERAY_WEBAPP/WEB-INF/lib/vaadin-server.jar || exit 1
unzip -tqq $LIFERAY_WEBAPP/WEB-INF/lib/vaadin-shared.jar || exit 1
unzip -tqq $LIFERAY_WEBAPP/WEB-INF/lib/vaadin-shared-deps.jar || exit 1
unzip -tqq $LIFERAY_WEBAPP/WEB-INF/lib/jsoup.jar || exit 1
unzip -tqq $TEMP/vaadin-themes.jar || exit 1
unzip -tqq $TEMP/vaadin-client-compiled.jar || exit 1
unzip -tqq $VAADIN_CLIENT/vaadin-client-compiler.jar || exit 1
unzip -tqq $VAADIN_CLIENT/vaadin-client.jar || exit 1
unzip -tqq $VAADIN_CLIENT/vaadin-client-compiler-deps.jar || exit 1
unzip -tqq $LIFERAY_WEBAPP/WEB-INF/lib/validation-api.GA.jar || exit 1
unzip -tqq $LIFERAY_WEBAPP/WEB-INF/lib/validation-api.GA-sources.jar || exit 1

# Copy Vaadin resources to Liferay web application
echo
echo Copying Vaadin resources to Liferay web application...
pushd $LIFERAY_WEBAPP/html >/dev/null
unzip -q $LIFERAY_WEBAPP/WEB-INF/lib/vaadin-server.jar 'VAADIN/*'
unzip -q $TEMP/vaadin-themes.jar 'VAADIN/*'
unzip -q $TEMP/vaadin-client-compiled.jar 'VAADIN/*'
popd >/dev/null

# Add vaadin properties to portal-ext.properties (assuming it's in $PORTAL_EXT_PROPERTIES)
echo
echo Updating $PORTAL_EXT_PROPERTIES...
if [ ! -f $PORTAL_EXT_PROPERTIES ]; then
  echo "$PORTAL_EXT_PROPERTIES not found. Please add the following properties manually:"
  echo "# Path under which the VAADIN directory is located."
  echo "# (/html is the default so it is not needed.)"
  echo "# vaadin.resources.path=/html"
  echo
  echo "# Portal-wide widget set"
  echo "vaadin.widgetset=com.vaadin.LiferayWidgetSet"
  echo
  echo "# Theme to use"
  echo "vaadin.theme=$VAADIN_THEME"
elif ! grep -c vaadin.widgetset $PORTAL_EXT_PROPERTIES >/dev/null; then
  cp $PORTAL_EXT_PROPERTIES ${PORTAL_EXT_PROPERTIES}.bak
  cat >>$PORTAL_EXT_PROPERTIES <<EOF
# Path under which the VAADIN directory is located.
# (/html is the default so it is not needed.)
# vaadin.resources.path=/html

# Portal-wide widget set
vaadin.widgetset=com.vaadin.LiferayWidgetSet

# Theme to use
vaadin.theme=$VAADIN_THEME
EOF
  echo "Updated $PORTAL_EXT_PROPERTIES."
else
  echo "$PORTAL_EXT_PROPERTIES already contains vaadin.widgetset property. No changes made."
fi

echo
echo "Vaadin 7 upgrade complete! Please restart Tomcat."