Tuesday Dec 08, 2009

Client-side Eventing Example

In the previous blog, Eventing in AJAX portlets, we saw an overview of client side eventing mechanism in OSPC and WebSpace, which works like a simple yet powerful extension of JSR-286 eventing.

Lets look at an example to see how this all works.

EventGeneratorAjaxPortlet in this example, takes a zip code through a text field and has a button "Get GeoCode".

<input name="getbtn" id="getbtn" value="Get Geocode" type="button" onclick="<portlet:namespace/>PortletObj.processAction('<portlet:resourceURL/>')"/>

The onClick of the button calls processAction method on a javacript object called PortletObj which is prefixed with portlet's namespace. So first define a processAction javascript function just like one would have done while overriding the GenericPortlet's processAction method in the Portlet's Java class on server side.

<portlet:namespace/>PortletObj = { portletReq : null, processAction : function(updateURL) { portletReq = new XMLPortletRequest("<portlet:namespace/>"); portletReq.onreadystatechange = function() { <portlet:namespace/>PortletObj.render(); }; //collect information and prepare data to POST to server ... ... portletReq.open("POST", updateURL, true); portletReq.send(data); }, render : function() { var result = JSON.parse(portletReq.responseText, null); // process the response and modify the DOM ... ... var qName = {uri: "http://www.example.com/clientevents", name : "MyEvent" }; var eventPayload = {}; eventPayload.x = result.x; eventPayload.y = result.y; portletReq.setEvent(qName, eventPayload); } };

The processAction method, uses XMLPortletRequest which is initialized using the portlet's namespace. Portlet container automatically makes this available by adding the javascript file to the portlet during deployment. It is only required to include the script in the jsp from under <context-path>/js/ like this,

<script type="text/javascript" src="<%=renderRequest.getContextPath()%>/js/XMLPortletRequest.js"> </script>

In the example above, function names processAction and render are only a matter of following the JSR-286 pattern. The names of these functions could have been anything. More important is to note how "setEvent" is called on the XMLPortletRequest and an arbitrary event payload can be passed as a JSON object.

It is possible to use qName without the uri. The new RFE makes it possible to call setEvent with a string as an event name instead of requiring QName.

Show/Hide JSP

The serveResource method of the portlet computes latitude and longitude for the zip code, using Yahoo geocode service and returns a JSON object from its serveResource method.

Show/Hide serveResource

EventConsumerAjaxPortlet in this example, consumes the event generated by EventGeneratorAjaxPortlet, by implementing a PortletObj.processEvent javascript function. This function is called in response to the XMLPortletRequest.setEvent call in the processAction of event generator.

The wiring is taken care by Portlet Container at runtime as we will see later, but is done in portlet.xml by the developer as shown below.

<portlet> <portlet-name>EventGeneratorAjaxPortlet</portlet-name> ... ... <supported-publishing-event xmlns:x='http://www.example.com/clientevents'> <qname>x:ZipEvent</qname> </supported-publishing-event> </portlet> <portlet> <portlet-name>EventConsumerAjaxPortlet</portlet-name> ... ... <supported-processing-event xmlns:x='http://www.example.com/clientevents'> <qname>x:ZipEvent</qname> </supported-processing-event> </portlet> <event-definition xmlns:x='http://www.example.com/clientevents'> <qname>x:ZipEvent</qname> <value-type>com.sun.portlet.ClientEvent</value-type> </event-definition>

The portlets could have been packaged separately and may have separate portlet.xml. Also, since this is same as server-side eventing, it is possible to use tools like NetBeans PortalPack, and to use the eventing storyboard to wire the portlets visually instead of handcoding the xml.

Typically, this is how a consumer is implemented.

<portlet:namespace/>PortletObj = { portletReq : null, processEvent : function(eventObj) { portletReq = new XMLPortletRequest("<portlet:namespace/>"); portletReq.onreadystatechange = function() { <portlet:namespace/>PortletObj.render(); }; //process the event payload and prepare data to POST to server ... portletReq.open("POST", '<portlet:resourceURL/>', true); portletReq.send(data); }, render : function() { var response = JSON.parse(portletReq.responseText, null); // process response and modify the DOM ... } };

The namespaced PortletObj.processEvent function is mandatory and is the only forced naming convention required to be followed. It is not required to implement render function. It was possible to include all render code, instead of a call to render, in the callback function. But again, as a matter of convention and pattern, its much cleaner to implement render.

Show/Hide JSP

The processEvent function gets zip, latitude and longitude in the event payload. It in turn calls serveResouce of the portlet to get all the pictures recently uploaded in 10 mile radius of the given latitude/longitude, using Flickr's REST service.

Show/Hide serveResource

How it works
Portlet Container reads portlet.xml during deployment and recognizes client-side events from the special marker class com.sun.portlet.ClientEvent. While rendering the portlet, it generates a namespaced javascript event queue, <portlet:namespace/>EventQueue, for the generator and populates it with a list of consumers on the page. When XMLPortletRequest.setEvent is called, it in turn calls setEvent on the generator's event queue. Generator's event queue loops through consumer list and calls <portlet:namespace/>PortletObj.processEvent on each. This is why it is mandatory to implement processEvent function to be able to consume events.

About

Prashant Dighe

Search

Categories
Archives
« December 2009
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today