Portlets rarely live alone. A page can contain multiple portlets and when the user interacts with one portlet, you may need to have the other portlets react to the change immediately. This is not normally possible with Vaadin portlets, as Vaadin applications need to get an Ajax request from the client-side to change their user interface. On the other hand, the regular inter-portlet communication (IPC) mechanism in Portlet 2.0 Specification requires a complete page reload, but that is not appropriate with Vaadin or in general Ajax applications, which do not require a page reload. One solution is to communicate between the portlets on the server-side and then use a server-push mechanism to update the client-side.

The Vaadin IPC for Liferay Add-on takes another approach by communicating between the portlets through the client-side. Events (messages) are sent through the LiferayIPC component and the client-side widget relays them to the other portlets, as illustrated in Figure 13.6, “Vaadin IPC for Liferay Architecture”.


Vaadin IPC for Liferay uses the Liferay JavaScript event API for client-side inter-portlet communication, so you can communicate just as easily with other Liferay portlets.

Notice that you can use this communication only between portlets on the same page.

Figure 13.7, “Vaadin IPC Add-on Demo with Two Portlets” shows Vaadin IPC for Liferay in action. Entering a new item in one portlet is updated interactively in the other.


The Vaadin IPC for Liferay add-on is available from the Vaadin Directory as well as from a Maven repository, as described in Chapter 15, Using Vaadin Add-ons.

The contents of the installation package are as follows:

vaadin-ipc-for-liferay-x.x.x.jar
The add-on JAR in the installation package must be installed in the WEB-INF/lib directory under the root context. The location depends on the server - for example in Liferay running in Tomcat it is located under the webapps/ROOT folder of the server.
doc
The documentation folder includes a README.TXT file that describes the contents of the installation package briefly, and licensing.txt and license-asl-2.0.txt, which describe the licensing under the Apache License 2.0. Under the doc/api folder is included the complete JavaDoc API documentation for the add-on.
vaadin-ipc-for-liferay-x.x.x-demo.war
A WAR containing demo portlets. After installing the add-on library and compiling the widget set, as described below, you can deploy the WAR to Liferay and add the two demo portlets to a page, as shown in Figure 13.7, “Vaadin IPC Add-on Demo with Two Portlets”. The source of the demo is available at dev.vaadin.com/svn/addons/IPCforLiferay/trunk/.

LiferayIPC is an invisible user interface component that can be used to send messages between two or more Vaadin portlets. You add it to an application layout as you would any regular user interface component.

LiferayIPC liferayipc = new LiferayIPC();
layout.addComponent(liferayipc);

You should be careful not to remove the invisible component from the portlet later if you modify the layout of the portlet.

The component can be used both for sending and receiving messages, as described next.

In many cases, such as when considering security or efficiency, it is better to pass the bulk data on the server-side and use the client-side IPC only for notifying the other portlet(s) that the data is available. Session attributes are a conveninent way of sharing data on the server-side. You can also share objects through them, not just strings.

The session variables have a scope, which should be APPLICATION_SCOPE. The "application" refers to the scope of the Java web application (WAR) that contains the portlets.

If the communicating portlets are in the same Java web application (WAR), no special configuration is needed. You can also communicate between portlets in different WARs, in which case you need to disable the private-session-attributes parameter in liferay-portlet.xml by setting it to false. Please see Liferay documentation for more information regarding the configuration.

You can also share Java objects between the portlets in the same WAR, not just strings. If the portlets are in different WARs, they normally have different class loaders, which could cause incompatibilities, so you can only communicate with strings and any object data needs to be serialized.

Session attributes are accessible through the PortletSession object, which you can access through the portlet context from the Vaadin Application class.

Person person = new Person(firstname, lastname, age);
...

PortletSession session =
        ((PortletApplicationContext2)getContext()).
            getPortletSession();

// Share the object
String key = "IPCDEMO_person";
session.setAttribute(key, person,
                     PortletSession.APPLICATION_SCOPE);

// Notify that it's available
liferayipc.sendEvent("ipc_demodata_available", key);

You can then receive the attribute in a LiferayIPCEventListener as follows:

public void eventReceived(LiferayIPCEvent event) {
    String key = event.getData();

    PortletSession session =
            ((PortletApplicationContext2)getContext()).
                getPortletSession();

    // Get the object reference
    Person person = (Person) session.getAttribute(key);

    // We can now use the object in our application
    BeanItem<Person> item = new BeanItem<Person> (person);
    form.setItemDataSource(item);
}

Notice that changes to a shared object bound to a user interface component are not updated automatically if it is changed in another portlet. The issue is the same as with double-binding in general.

The IPC events support transmitting only plain strings, so if you have object or other non-string data, you need to format or serialize it to a string representation. For example, the demo application formats the trivial data model as a semicolon-separated list as follows:

private void sendPersonViaClient(String firstName,
                                 String lastName, int age) {
    liferayIPC_1.sendEvent("newPerson", firstName + ";" +
                           lastName + ";" + age);
}

You can use standard Java serialization for any classes that implement the Serializable interface. The transmitted data may not include any control characters, so you also need to encode the string, for example by using Base64 encoding.

// Some serializable object
MyBean mybean = new MyBean();
...

// Serialize
ByteArrayOutputStream baostr = new ByteArrayOutputStream();
ObjectOutputStream oostr;
try {
    oostr = new ObjectOutputStream(baostr);
    oostr.writeObject(mybean); // Serialize the object
    oostr.close();
} catch (IOException e) {
    getWindow().showNotification("IO PAN!"); // Complain
}

// Encode
BASE64Encoder encoder = new BASE64Encoder();
String encoded = encoder.encode(baostr.toByteArray());

// Send the IPC event to other portlet(s)
liferayipc.sendEvent("mybeanforyou", encoded);

You can then deserialize such a message at the receiving end as follows:

public void eventReceived(LiferayIPCEvent event) {
    String encoded = event.getData();

    // Decode and deserialize it    
    BASE64Decoder decoder = new BASE64Decoder();
    try {
        byte[] data = decoder.decodeBuffer(encoded);
        ObjectInputStream ois =
                new ObjectInputStream( 
                        new ByteArrayInputStream(data));

        // The deserialized bean
        MyBean deserialized = (MyBean) ois.readObject();
        ois.close();

        ... do something with the bean ...

    } catch (IOException e) {
        e.printStackTrace(); // Handle somehow
    } catch (ClassNotFoundException e) {
        e.printStackTrace(); // Handle somehow
    }
}