Pushing Using Vaadin, Websockets and Liferay 6

In Vaadin 7.2, server push support was introduced but only for servlet deployments. The initial plan was to add support for portlets "right after that release". That's not exactly what happened but for the upcoming Vaadin 7.6, there has been some changes which makes it easier to support push in a portal environment.

The portal challenge

Portals bring some extra challenges for server push, mainly because you only have access to shared portal requests in a portal environment and not necessarily to the low level HTTP request. Some requests in a portal, such as resource requests, are delivered only to one portlet and could theoretically be used for a push connection. The portlet 2.0 specification (JSR-286) however does not mention anything about asynchronous processing, leaving requests open for a long time or upgrading resource requests to websocket connections. Quite understandable as the portlet 2.0 specification was finalized in 2008, before the Servlet 3.0 spec (2009) and the Websockets spec (2013).

Setting up a Vaadin portlet for push - the theory

What Liferay and other portals offer is the possibility to deploy servlets along with portlets, so that all servlets and portlets are part of the same web application (WAR file). We can therefore use a portlet to serve the UI and all related parts, and a separate servlet for the push channel. For this approach to work we must ensure three things:

 

  1. the same session must be accessible from both the servlet and the portlet

  2. the same VaadinSession and UI instances must be used by both the servlet and the portlet

  3. push requests must be sent through the servlet and not the portlet

 

Vaadin 7.6 solves the first problem by storing the VaadinSession object in the session using application scope, which makes it available for all servlets and portlets in the same war file.

 

The last two steps you can implement using a custom VaadinServlet and VaadinService. This has already been done in the Portal Push add-on, so let's see how that can be used.

Creating an example Liferay portlet

 

1. Start by creating a Liferay project using the vaadin liferay archetype

 

mvn archetype:generate -DarchetypeRepository=https://maven.vaadin.com/vaadin-prereleases -DarchetypeGroupId=com.vaadin -DarchetypeArtifactId=vaadin-archetype-liferay-portlet -DarchetypeVersion=7.6.0.beta2 -DgroupId=com.example.portalpush -DartifactId=portal-push-liferay-example -Dversion=1.0-SNAPSHOT

 

2. Configure a Liferay profile in Maven

 

See https://vaadin.com/book/-/page/portal.liferay.html. Alternatively, define the parameters for the liferay-maven-plugin in pom.xml

              

3. Add the Vaadin pre-release repository in <repositories> and <pluginRepositories>. This is only needed as we are using a beta version of Vaadin 7.6.

 

<repositories>

<repository>

   <id>vaadin-pre-releases</id>

   <url>https://maven.vaadin.com/vaadin-prereleases</url>

</repository>

</repositories>

 

<pluginRepositories>

...

<pluginRepository>

   <id>vaadin-pre-releases</id>

   <url>https://maven.vaadin.com/vaadin-prereleases</url>

</pluginRepository>

</pluginRepositories>

 

4. Add the portal push add-on

 

<dependency>

       <groupId>org.vaadin.artur</groupId>      

       <artifactId>portal-push</artifactId>

       <version>0.2.0</version>            

</dependency>

 

5. Change web.xml and portlet.xml to use the servlet and portlet class from the add-on

 

./src/main/webapp/WEB-INF/web.xml:

<servlet-class>org.vaadin.artur.portalpush.PushServlet</servlet-class>

 

./src/main/webapp/WEB-INF/portlet.xml:

<portlet-class>org.vaadin.artur.portalpush.PushPortlet</portlet-class>

 

6. Edit the UI class and add @Push and something which pushes

 

@Push(transport=Transport.WEBSOCKET_XHR)

...

private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);

service.scheduleWithFixedDelay(new Runnable() { // Run every second

           @Override

           public void run() {

               access(new Runnable() {// Synchronize with Vaadin

                   @Override

                   public void run() {

                       layout.addComponent(new Label(

                               "Server time is now: " + new Date()));

                   }

               });

           }

       }, 1, 1, TimeUnit.SECONDS);

 

7. Build and deploy the portlet

 

mvn install liferay:deploy

 

There is a full working example project available at https://github.com/Artur-/portal-push-liferay-example. Always remember to use UI.access from non-request threads to avoid synchronization problems and race conditions.

Potential pitfalls

Liferay & Long Polling

This approach to push requires the portal to support deploying Servlet 3.0 servlets. Unfortunately Liferay 6 supports only 2.4 and will automatically add a servlet-filter in front of the servlet, which breaks the required async support. Because of this, websockets will work in Liferay 6 using this approach, but long polling will not work.

Websockets and session

Session timeout in portals is based on HTTP requests, so if you are using websockets for all communications, you can possibly get session timeouts even though the user is using the portlet. The simplest way to get around this is to use the WEBSOCKET_XHR transport so that all server requests are sent as HTTP requests and session extension will work in the same way as when not using push.

Tomcat 7

Tomcat 7 is not capable of sharing the HTTP session between websockets and the servlet. This sometimes have strange implications, upgrade to Tomcat 8 to avoid problems.

 

If you experience problems, please refer to http://vaadin.com/wiki/-/wiki/Main/Configuring+push+for+your+environment or create an issue at https://github.com/Artur-/portal-push/issues