Showing posts with label annotation. Show all posts
Showing posts with label annotation. Show all posts

Patterns in ProcessEvent annotation

In the ProcessEvent annotation entry i built a sample portlet to demonstrate how we can use @ProcessEvent annotation.

When the processEvent() method in GenericPortlet gets invoked, first it checks if there there is exact match of event name and the value of name attribute of @ProcessEvent annotation. If it does not find exact match it tries to find the longest possible match using following rule

"If the local part of the event name has a wildcard at the end (“.”) the GenericPortlet will try to match the received event either to the same wildcard event name or to the longest matching event name for this wildcard. E.g. if an event with the local part of the event name of "a.b.c.d" is being received and there are methods annotated for handling "a.b." and "a.b.c." events in this portlet, the GenericPortlet will dispatch the event to the method annotated with "a.b.c."

Lets say this is how portelt.xml of my target portlet looks like


<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>ProcessEventAnnotationPortlet</portlet-name>
<display-name>Process Event Annotation Portlet</display-name>
<portlet-class>com.webspherenotes.portlet.jsr286.ProcessEventAnnotationPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
</supports>
<portlet-info>
<title>Process Event Annotation Portlet</title>
<short-title>Process Event Annotation Portlet</short-title>
<keywords>Process Event Annotation Portlet</keywords>
</portlet-info>
<supported-processing-event>
<name>com.webspherenotes.events.contact</name>
</supported-processing-event>
</portlet>
<default-namespace>http://wpcertification.blogspot.com</default-namespace>
<event-definition>
<name>com.webspherenotes.events.contact</name>
<value-type>com.webspherenotes.portlet.events.Contact</value-type>
</event-definition>


</portlet-app>


The ProcessEventAnnotationPortlet can consume com.webspherenotes.events.contact event. So these are some of the @ProcessEvent combination that i can use to handle this event


  1. @ProcessEvent(qname="{http://wpcertification.blogspot.com}com.webspherenotes.events.contact"): Fully qualified event name

  2. @ProcessEvent(qname="{http://wpcertification.blogspot.com}com.webspherenotes."): Fully qualified event name, so this method can handle all the events starting with com.webspherenotes.*, it could be com.webspherenotes.events.contact,com.webspherenotes.events.hello

  3. @ProcessEvent(name = "com.webspherenotes."): Means it can handle all the events where local name starts with com.webspherenotes.*

ProcessEvent annotation

This is how the default implementation of the processEvent() method in GenricPortlet looks like,

public void processEvent(EventRequest request, EventResponse response) throws PortletException, IOException {
String eventName = request.getEvent().getQName().toString();
try {
// check for exact match
Method eventMethod = processEventHandlingMethodsMap.get(eventName);
if (eventMethod != null) {
eventMethod.invoke(this, request, response);
return;
} else {
// Search for the longest possible matching wildcard annotation
int endPos = eventName.indexOf('}');
int dotPos = eventName.lastIndexOf('.');
while (dotPos > endPos) {
String wildcardLookup = eventName.substring(0, dotPos + 1);
eventMethod = processEventHandlingMethodsMap.get(wildcardLookup);
if (eventMethod != null) {
eventMethod.invoke(this, request, response);
return;
}
if (dotPos == 0) {
break;
}
dotPos = eventName.lastIndexOf('.', dotPos - 1);
}
}
} catch (Exception e) {
throw new PortletException(e);
}
// if no event processing method was found just keep render params
response.setRenderParameters(request);
}


First it tries to figure out if there is a method with @ProcessEvent annotation, that matches current event, if yes it forwards control to that method if not it sets the current render parameters as new render parameters.

You can annotate a method use ProcessEvent annotation using two different approaches

  • Using QName: You can specify the fully qualified name of the event using "{" + Namespace URI + "}" + local part format. Ex. @ProcessEvent(qname="{http://wpcertification.blogspot.com}hello")

  • Using local Name:For using only the local part of the event name and leverage the default namespace defined in the portlet deployment descriptor with the default-namespace element the following alternative is provided: @ProcessEvent (name=), where the event name is only the local part.



You can download the sample portlet that i built to demonstrate how to use @ProcessEvent from here


package com.webspherenotes.portlet.jsr286;

import java.io.IOException;

import javax.portlet.Event;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.ProcessEvent;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import com.webspherenotes.portlet.events.Contact;

public class ProcessEventAnnotationPortlet extends GenericPortlet {

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.doView()");
response.setContentType("text/html");
Contact contact = (Contact) request.getPortletSession().getAttribute(
"contact");
if (contact != null) {
response.getWriter().println(
"First Name " + contact.getFirstName() + "
Last Name "
+ contact.getLastName() + "
Email "
+ contact.getEmail());
} else {
response.getWriter().println("Contact not found in session ");
}
System.out.println("Exiting ProcessActionAnnotationPortlet.doView()");
}

//@ProcessEvent(name = "hello")
@ProcessEvent(qname="{http://wpcertification.blogspot.com}hello")
public void handleContactEvent(EventRequest request, EventResponse response)
throws PortletException, IOException {
System.out
.println("Entering ProcessActionAnnotationPortlet.handleContactEvent()");
Event event = request.getEvent();
System.out.println("Event Name " + event.getName());
System.out.println("Event Value " + event.getValue());
Contact contact = (Contact) event.getValue();
System.out.println("Contact First Name " + contact.getFirstName());
System.out.println("Contact Last Name " + contact.getLastName());
System.out.println("Contact Email " + contact.getEmail());
request.getPortletSession().setAttribute("contact", contact);
System.out
.println("Entering ProcessActionAnnotationPortlet.handleContactEvent()");
}

}


The ProcessEventAnnotationPortlet can act as target of hello event. It has a handleContactEvent method that can be used for handling the hello event. I can annotate it using either only local name like this @ProcessEvent(name = "hello") or using fully qualified name like this @ProcessEvent(qname="{http://wpcertification.blogspot.com}hello")

This is the portlet.xml for my Sample portlet

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>ProcessEventAnnotationPortlet</portlet-name>
<display-name>Process Event Annotation Portlet</display-name>
<portlet-class>com.webspherenotes.portlet.jsr286.ProcessEventAnnotationPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
</supports>
<portlet-info>
<title>Process Event Annotation Portlet</title>
<short-title>Process Event Annotation Portlet</short-title>
<keywords>Process Event Annotation Portlet</keywords>
</portlet-info>
<supported-processing-event>
<name>hello</name>
</supported-processing-event>

</portlet>
<default-namespace>http://wpcertification.blogspot.com</default-namespace>
<event-definition>
<name>hello</name>
<value-type>com.webspherenotes.portlet.events.Contact</value-type>
</event-definition>

</portlet-app>

RenderMode annotation

The doDispatch method of GenericPortlet class is modified in the Portlet Specification 2.0, so that now when it gets control first it checks if there is any method in your Portlet class which has @RenderMode annotation that matches the portlet mode of incoming request, if yes it forwards control to that method, if not then the doDispatch method will forward control to following methods

  • doView() for handling VIEW mode

  • doEdit() for handling EDIT mode

  • doHelp() for handling HELP mode



This is how the doDispatch() method in GenericPortlet class looks like

protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException,
java.io.IOException {
WindowState state = request.getWindowState();
if (!state.equals(WindowState.MINIMIZED)) {
PortletMode mode = request.getPortletMode();
// first look if there are methods annotated for
// handling the rendering of this mode
try {
// check if mode is cached
Method renderMethod = renderModeHandlingMethodsMap.get(mode.toString());
if (renderMethod != null) {
renderMethod.invoke(this, request, response);
return;
}
} catch (Exception e) {
throw new PortletException(e);
}

// if not, try the default doXYZ methods
if (mode.equals(PortletMode.VIEW)) {
doView(request, response);
} else if (mode.equals(PortletMode.EDIT)) {
doEdit(request, response);
} else if (mode.equals(PortletMode.HELP)) {
doHelp(request, response);
} else {
throw new PortletException("unknown portlet mode: " + mode);
}
}


I wanted to see how @RenderMode annotation works so i built a sample portlet like this, you can download it from here


package com.webspherenotes.portlet.jsr286;

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderMode;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class RenderModeAnnotationPortlet extends GenericPortlet{

@RenderMode(name="view")
public void handleViewMode(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.handleViewMode()");
response.setContentType("text/html");
response.getWriter().println("<h3>View Mode Response</h3>");
System.out.println("Exiting ProcessActionAnnotationPortlet.handleViewMode()");
}

@RenderMode(name="edit")
public void handleEditMode(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.handleEditMode()");
response.setContentType("text/html");
response.getWriter().println("<h3>Edit Mode Response</h3>");
System.out.println("Exiting ProcessActionAnnotationPortlet.handleEditMode()");
}

@RenderMode(name="help")
public void handleHelpMode(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.handleHelpMode()");
response.setContentType("text/html");
response.getWriter().println("<h3>Help Mode Response</h3>");
System.out.println("Exiting ProcessActionAnnotationPortlet.handleHelpMode()");
}
}


If you want to use @RenderMode annotation, then you will have to do two things, first create a method with following signature


void (RenderRequest, RenderResponse) throws
PortletException, java.io.IOException;


Mark that method with @RenderMode(name="<modename>") attribute where value of name equals to name of the PortletMode that this method should handle

ProcessAction annotation

The JSR 286 interface introduces concept of ProcessAction annotation that you can use for marking a method that can be used for handling particular action. I built a simple portlet to demonstrate how you can use @ProcessAction annotation, you can download the sample portlet from here

This is how my ProcessActionAnnotationPortlet.java looks like

package com.webspherenotes.portlet.jsr286;

import java.io.IOException;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.ProcessAction;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class ProcessActionAnnotationPortlet extends GenericPortlet{

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ProcessActionAnnotationPortlet.doView()");
response.setContentType("text/html");

getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting ProcessActionAnnotationPortlet.doView()");
}

@ProcessAction(name="action1")
public void handleAction1(ActionRequest request, ActionResponse response)throws PortletException,IOException{
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction1()");
System.out.println("Value of ACTION_NAME " + request.getParameter(ActionRequest.ACTION_NAME));
response.setRenderParameter("action", "action1");
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction1()");
}

@ProcessAction(name="action2")
public void handleAction2(ActionRequest request, ActionResponse response)throws PortletException,IOException{
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction2()");
System.out.println("Value of ACTION_NAME " + request.getParameter(ActionRequest.ACTION_NAME));
response.setRenderParameter("action", "action2");
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction2()");
}

@ProcessAction(name="action3")
public void handleAction3(ActionRequest request, ActionResponse response)throws PortletException,IOException{
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction3()");
System.out.println("Value of ACTION_NAME " + request.getParameter(ActionRequest.ACTION_NAME));
response.setRenderParameter("action", "action3");
System.out.println("Entering ProcessActionAnnotationPortlet.handleAction3()");
}

}


As you can see i have three methods that are marked with @ProcessAction annotation, the handleAction1, is marked with @ProcessAction(name="action1"), which means that whenever there is processAction call with value of javax.portlet.action parameter equal to action1, it will call handleAction1 method.

The Annotations are implemented using GenericPortlet For a received action the processAction method in the GenericPortlet class tries to dispatch to methods annotated with the tag @ProcessAction(name=<action name>), where the action name must be set on the ActionURL as value of the parameter javax.portlet.action (or via the constant ActionRequest.ACTION_NAME), and following signature:

void <methodname> (ActionRequest, ActionResponse) throws
PortletException, java.io.IOException;

A portlet that wants to leverage this action dispatching needs to set the parameter ActionRequest.ACTION_NAME on the action URL.


This is how my JSP looks like

<%@page language="java" contentType="text/html; %>
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<portlet:defineObjects />
Current Action - <%=renderRequest.getParameter("action") %> <br/>
<portlet:actionURL var="action1Url">
<portlet:param name="javax.portlet.action" value="action1" />
</portlet:actionURL>
<a href='<%=action1Url %>' >Action1 URl</a><br/>

<portlet:actionURL var="action2Url">
<portlet:param name="javax.portlet.action" value="action2" />
</portlet:actionURL>
<a href='<%=action2Url %>' >Action2 URl</a><br/>

<portlet:actionURL var="action3Url">
<portlet:param name="javax.portlet.action" value="action3" />
</portlet:actionURL>
<a href='<%=action3Url %>' >Action3 URl</a><br/>