Thursday, March 19, 2009

JSR 286 IPC with JSF

In case you are portlets developer, you know what JSR 286 is. It is the holy grail for the portlets which should enable the portlets to communicate with each others. At least you would think it is the holy grail, until you read the specs and look upon the Suns minimalistic examples.
It seems to me, this spec is out for it's own sake, and forwards the developers in the ice age of pure html, which is unthinkable for someone who has got used to API's such as JSF or Struts. Vendors as JBoss or ICEFaces have yet to deliver a bridge which will support JSR 286 with an AJAX push.

The sad news is that JSR 286 works only with out AJAX push, and you have to do some work around to get the grips of it in JSF Portal environment.

The first thing to do is to define the event you want to publish. This is done in the portlet.xml. There are three declaration there:
  1. One Global declaration of your event
  2. Supported publishing event for your pulbishing portlet
  3. Supported processing event for our consumer portlet


The global event declaration looks as follow:



x:EventName
java.lang.String



In the Publishing portlet, put the following:



x:EventName



And atlast, in the consumer portlet:



x:EventName


Now that you have configured your event that it is only a simple String, you should set it. This can be done in two way's:

  1. Either extend javax.portlet.faces.GenericFacesPortlet and override the

    public void processAction(ActionRequest request, ActionResponse);
  2. Or set the event in one of your Faces action methods.
If you prefer the first way, than you have to think that your to pass your full class name in the portlet.xml for portlet instantiation which would look like this:


org.mycomp.IPCPublisher



If you prefer the JSF action method way, than your portlet.xml will look as usual:

javax.portlet.faces.GenericFacesPortlet



In you decide to override the method of the GenericFacesPortlet, than you will set the event similar to this:


public void processAction(ActionRequest request, ActionResponse response)
throws PortletException,IOException {

QName qname = new QName("http://www.mycomp.com/myevent" , "EventName");
response.setEvent(qname, "Hallo IPC Communication.");
}


If you decide you would like to publish this event from inside of your JSF action method, you can do that as follow:

public void publishIPCEvent(ActionEvent event){
Object response = FacesContext.getCurrentInstance()
.getExternalContext()
.getResponse();

if(response instanceof ActionResponse){
ActionResponse aResponse = (ActionResponse)response;
QName qname = new QName("http://www.mycomp.com/myevent" , "EventName");
aResponse.setEvent(qname, "Hallo IPC Communication.");
}
}



This method will be called as usual in JSF:








What is left is to read out this event in the Consumer portlet.
You should extend the Generic Faces Portlet and override the processEvent method. By doing that, don't forget to put the class definition in the portlet.xml as follow:

org.mycomp.IPCConsumer

Override the method as follow:

public void processEvent(EventRequest request, EventResponse response) {
Event event = request.getEvent();
if(event.getName().equals("EventName")){
String ipc = event.getValue();
response.getPortletSession().setAttribute("ipc",ipc);
}
}


As you can see, I read out the event and compare it's name if it is my event I want to handle. Than I read it's value and set it in the portlet session as attribute.

To get this value inside of JSF, you should do something like this:

Object ipc = FacesContext.getCurrentInstance()
.getExternalContext().getSession(false)
.getAttribute("ipc);



Some say that one can annotate the certain method, which should be responsible for processing the event. This should look something like this:

@ProcessEvent(qname="{http://www.mycomp.com/myevent}EventName")
public void myEventProcessingMethod(EventRequest request, EventResponse response){
Event event = request.getEvent();
if(event.getName().equals("EventName")){
String ipc = event.getValue();
response.getPortletSession().setAttribute("ipc",ipc);
}
}



But this method annotation did not work for me inside of JSF. The same should work with java.util.HashMap instead of java.lang.String as well.

Anyways, this IPC approach is not that straightforward as some would like it to be, but it fullfils it's purpose.

4 comments:

  1. Hi,

    Imagine that you want to make some drag-and-drop functionality from one portlet to another different portlet. Do you thinks that this is possible using JSR 286 Eventing?

    Thanks

    ReplyDelete
  2. Hi,

    I have never did such thing yet. If it is possible, I would imagine that the hard part would be how to find out the target.

    Let me elaborate. From the Protlet you like to drag some Doc, you can easily drag something.

    The first problem appears, how to fire the JSR 286 Event at the start of the dragging.

    If you handle this somehow, it is easy to publish the id and all the other properties of the Doc via JSR 286 Eventing.

    You will receive this Doc Data in the Target Portlet, how ever, you still don't know the target, where you want your Doc to be droped.

    So two things are clearly missing for this task:

    1) Firing of the JSR 286 on start dragging event.

    2) You don't know the drop target.

    For the drop target, I would imagine, some target map could be implemented (fix targets) thus at firng of JSR 286 along with the other Doc Data you can give the ID of the target.

    What I currently don't konw, is if there is any possibility to fire JSR 286 Event at start of an Dragging event.

    To fire JSR 286 Event you need ActionRequest Type of Request. The Dragging in RichFaces was an AJAX supported, thus it would be possible at least in PortletBridge 1.0CR2 that the Request at this point is not a ActionRequest you need, thus, this won't be possible.

    ReplyDelete
  3. Hi,

    Thanks for your answer, it help me see better the overall problem.

    Although I think I could be a future feature I also don't think I works for now. Note also that the current JSR Eventing is a Publish Subcribe mechanism, this means a 1 to N mechanism. So the drag and drop have to be a particular case from the eventing mechanism.

    Another problem is. Imagine that the same object in the portlet fires 2 different events. One type for each type of destination Portlet. So in this case it would be required to make some kind of binding between the object, events and perhaps event the destination portlet.

    Regards
    HervĂȘ

    ReplyDelete
  4. The JSR 328 describes that one of the new features are the Events (IPC).

    If this is a new feature in the JSR 328 how it's possible to do it with the JSR 301?

    ReplyDelete