Inter Portlet Communication between pages

Introduction

Inter Portlet Communication (IPC) is designed to allow portlets to communicate with each other. The JSR 286 standard describes how portlets should be build, so they can expose events and parameters that can be read by another JSR 286 portlet. The assumption of this is that both portlets are on the same page.
The standard however, does not describe how portlets can communicate with each other when they are placed on different pages. The concepts of a portlet is that is should beĀ inter-operable without needing to know the specification of other components.
When you want to enable IPC between pages you basically violate that basic concept as you expect a specific portlet on a specific page.
Although it is not a best practice to do so we see a lot of customers who have this requirement.

Main Article

In this post I used the Parameter Form Portlet and Parameter Display Portlet which you can find in the WSRP Tools Portlet Producer

The Parameter Form portlet triggers an event when you press the OK button.

In order to capture that event we need to create a custom data control which exposed a method:

public void handleEvent(Object payload) {    
 	if(!(payload instanceof HashMap)){
     		throw new IllegalArgumentException("Payload not a HashMap<String,String>.");
 	}
        String p1 = "",p2 = "",p3 = "";
        HashMap map = (HashMap) payload;
        String[] pa1 = (String[])map.get("parameter1");
        String[] pa2 = (String[])map.get("parameter2");
        String[] pa3 = (String[])map.get("parameter3");
        if(pa1 != null)
            p1 = pa1[0];
        if(pa2 != null)
            p2 = pa2[0];
        if(pa3 != null)
            p3 = pa3[0];
        //Only forward when all 3 parameters have values
        if(!p1.equals("") && !p2.equals("") && !p3.equals("")){
            HashMap<String,String> paramMap = new HashMap<String,String>();
            paramMap.put("parameter1", p1);
            paramMap.put("parameter2", p2);
            paramMap.put("parameter3", p3);
            Map<String, Object> pageFlowScope = ADFContext.getCurrent().getPageFlowScope();
            pageFlowScope.put("paramMap", paramMap);
            //now the navigation
            FacesContext fctx = FacesContext.getCurrentInstance();
            Application application = fctx.getApplication();
            NavigationHandler navHandler = application.getNavigationHandler();
            navHandler.handleNavigation(fctx, null, "target");   
        }  
    }

We add this method to the bindings of the page and create an event from it.
This is how the bindings should look like:

<methodAction id="handleEvent" InstanceName="EventDataControl.dataProvider"
                  DataControl="EventDataControl" RequiresUpdateModel="true"
                  Action="invokeMethod" MethodName="handleEvent"
                  IsViewObjectMethod="false">
      <NamedData NDName="payload" NDType="java.lang.Object"/>
      <events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
        <event name="handleEvent"/>
      </events>
   </methodAction>

The next thing we need to do is wire this event to the Parameter Form portlet event. This is done in the eventMap section of the bindings:

<eventMap xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
    <event name="ParameterFormPortlet1_1_Event">
      <producer region="*">
        <consumer region="" handler="handleEvent" handleCondition="">
          <parameters>
            <parameter name="payload" value="#{payLoad}"/>
          </parameters>
        </consumer>
      </producer>
    </event>
  </eventMap>

This way our custom handleEvent will get the payload of the event triggered by the portlet so we can use it.

As you can see from the source code of the handleEvent method we get the parameters values from the payload.
In order to pass this on to the next page we create a HashMap that contains the parameters and put it on the pageFlowScope so the other page can access the information:

 HashMap<String,String> paramMap = new HashMap<String,String>();
  paramMap.put("parameter1", p1);
  paramMap.put("parameter2", p2);
  paramMap.put("parameter3", p3);            
  Map<String, Object> pageFlowScope = ADFContext.getCurrent().getPageFlowScope();
  pageFlowScope.put("paramMap", paramMap);

The next thing we do is navigate to the target page by following a navigation rule defined in the faces-config.xml:

FacesContext fctx = FacesContext.getCurrentInstance();
 Application application = fctx.getApplication();
 NavigationHandler navHandler = application.getNavigationHandler();
 navHandler.handleNavigation(fctx, null, "target");

On the target page we pass the parameters on the parmeterMap attribute of the display portlet:

 

<portlet id="ParameterDisplayPortlet1_1"
             portletInstance="/oracle/adf/portlet/PortletExample/ap/Ei2default_354ce3c1_013d_1000_8002_c0a83801a490"
            class="oracle.adf.model.portlet.binding.PortletBinding"
             retainPortletHeader="false"
             listenForAutoDeliveredPortletEvents="true"
             listenForAutoDeliveredParameterChanges="true"
             xmlns="http://xmlns.oracle.com/portlet/bindings"
             parameterMap="#{pageFlowScope.paramMap}">
      <events>
        <event eventType="ParametersChange"
               name="ParameterDisplayPortlet1_1_Event"/>
      </events>
    </portlet>

Notice that I removed the parameters section on this portlet. If you leave them they will overwrite the values provided in the parameterMap attribute!

You can download the sample application here

ipc_01

ipc_02

Using this technique is really useful.
If you have a few portlets then you can also create a single generic eventHandler and add it to the page template. By adding a method binding in the page template, this will be available on all pages and by using a wildcard for the publisher you can also make sure that it will listen to all events with a specific name.
This makes the event handler generic and not tightly coupled to a specific page.

Comments

  1. Sushil Deshpande says:

    Hi Yannick,
    This article and your approach is nice. Currently we are facing the issue when we use IPC between the portlets placed on 2 different pages. We have one more constraints of using the Spaces. How to fit the ADF taskflow portlet which will use the PortletSessionScope or similar technique so that Portlet can be communicated via IPC when placed on different pages.

    Can you help us?
    SR ID: SR 3-10015525821

    The only way we can extend the spaces is through the technique mentioned in the following URL
    http://docs.oracle.com/cd/E29542_01/webcenter.1111/e27739/jpsdg_wcsres.htm#JPSDG14552

    Can we construct the bean structure to hold the parameters for Portlets events. One more limitations from our side; we wanted to develop ADF taskflows as JSR286 remote portlets.My guess is even we are setting parameters; we are not getting the right scope for doing the same.

    We also tried of using the Session scope between the portlets. It works when Portlets placed on same page but breaks when you placed them on different pages.

    Thanks

  2. Hi Yannick,

    Is it possible to achieve the same – IPC between pages – with the Spaces portal?

    Thanks,

    -Ray

Add Your Comment