Supporting IE10 on a Rich Internet Application with Vaadin – the Vaadin 7 migration
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:
- 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 dependencyvaadin-6.7.1.jar
, we now have three dependencies:vaadin-server-7.1.6.jar
,vaadin-shared-7.1.6.jar
, andvaadin-shared-deps-1.0.2.jar
. Additionally, Vaadin now includes the GWT classes in its own JARs, so we had to remove dependencies ongwt-user
as well. - Change
Application
toUI
– In Vaadin 6, theApplication
was the entry point to your application, and you pointed the Vaadin servlet (in web.xml) or Vaadin portlet (in portlet.xml) to yourcom.vaadin.Application
sub-class. In Vaadin 7, you instead supply a subclass of the newcom.vaadin.ui.UI
. TheApplication
class still exists behind the scenes, but it is rarely necessary to provide your own implementation. Also, just callsetContent
on theUI
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 separateUI
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 aVaadinRequest
object, and you should override it to build up your GUI.VaadinRequest
is either aVaadinServletRequest
or aVaadinPortletRequest
depending on your environment and it will provide direct access to theHttpServletRequest
orPortletRequest
that caused the UI to be created, so you don’t need to implementHttpServletRequestListener
andPortletRequestListener
anymore to get to them. This is useful for authorization and personalization.We were overriding the
getSystemMessages
class in ourApplication
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 theVaadinServlet
andVaadinPortlet
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; } }
- Change
web.xml
and/orportlet.xml
– The Vaadin servlet was moved fromcom.vaadin.terminal.gwt.server.ApplicationServlet
tocom.vaadin.server.VaadinServlet
andweb.xml
needs to reflect this. Additionally, the servlet needs an init parameterUI
pointing to your UI class (as described in the previous point). Theapplication
parameter can go.The Vaadin portlet was moved from
com.vaadin.terminal.gwt.server.ApplicationPortlet2
tocom.vaadin.server.VaadinPortlet
, so change this inportlet.xml
. Additionally, as with the Vaadin servlet, the init parameterUI
referencing your UI class replaces theapplication
parameter.Our
portlet.xml
now looks like this (remember we have a customVaadinPortlet
; you will most likely just usecom.vaadin.server.VaadinPortlet
directly asportlet-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>
- Explicitly add a
VerticalLayout
toPanel
s – In Vaadin 6,Panel
s had an implicitVerticalLayout
, and theaddComponent
method onPanel
called the corresponding method on theVerticalLayout
. Now,Panel
has no moreaddComponent
method. It just has a singlecontent
component that can be anything you want, including aVerticalLayout
. One detail is that the defaultVerticalLayout
of the oldPanel
had a default 18px margin set, so to reproduce the old behavior in yourPanel
, use the following code:VerticalLayout content = new VerticalLayout(); content.setMargin(true); Panel panel = new Panel(content); layout.addComponent(...); ...
- Replace
ProgressIndicator
withProgressBar
– there’s three things to keep in mind here:- the
ProgressIndicator
class is deprecated and theProgressBar
replaces it - the progress bar doesn’t do polling for you anymore; this functionality has been moved to the
UI
class, so you need to callUI.setPollInterval
to have the client periodically request updates from the server andUI.setPollInterval(-1)
to disable polling again once the server side thread is done and you’ve hidden the progress bar - 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 theApplication
object. In Vaadin 7, locking is done by calls toVaadinSession.lock()
andVaadinSession.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 thelock()
andunlock()
calls. See The API documentation for more details.
- the
- Replace
UriFragmentUtility
withUI.addUriFragmentChangedListener
andPage.setFragment
– Back button and bookmarking support was previously handled by a kludgy component calledUriFragmentUtility
. Now, it’s a proper part of the Vaadin API. See The Book of Vaadin for more details. - Update import statements – Many classes were moved to a different package, but a simple “Organize imports” (Ctrl-Shift-O in Eclipse) will fix that.
- 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)
tocomponent.addValueChangeListener(valueChangeListener)
; this also removes ambiguity in classes that implemented multipleListener
interfaces
- constants that are now enums, e.g.
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.
- Replace Vaadin JARs in Liferay – remove the existing
vaadin.jar
inWEB-INF/lib
and copy the new Vaadin JARs and their dependencies in their place:vaadin-server-7.1.6.jar
[ maven central ]vaadin-shared-7.1.6.jar
[ maven central ]vaadin-shared-deps-1.0.2.jar
[ maven central ]
as well as dependencies of these:
jsoup-1.6.3.jar
[ maven central ]validation-api-1.0.0.GA.jar
[ maven central ]validation-api-1.0.0.GA-sources.jar
[ maven central ]
- 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/*'
):vaadin-server-7.1.6.jar
[ maven central ]vaadin-client-compiled-7.1.6.jar
[ maven central ]vaadin-themes-7.1.6.jar
[ maven central ]
- 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’shtml/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
- 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 readsprintStream.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:
The Vaadin configuration screen will open up:
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, checkportal-ext.properties
and restart your Liferay application server.Now you’re almost ready to compile your widget set. First:
- 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… - 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:- vaadin-client-compiler-7.1.6 [ maven central ]
- vaadin-client-7.1.6.jar [ maven central ]
- vaadin-client-compiler-deps-1.0.2.jar [ maven central ]
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). - Copy the add-on JARs to Vaadin’s
- 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
- Migrating from Vaadin 6 to Vaadin 7 (vaadin.com)
- Migrating from Vaadin 7.0 to Vaadin 7.1 (vaadin.com)
- Upgrading to Vaadin 7 Step by Step (streamhead.com)
- Integrating Vaadin 7 with Liferay (vaadin.com)
- 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."