Friday, July 24, 2009

Portlet IPC using JBOSS Portal


Portlet 2.0 is the recently finalized latest version of the Java portlet
specification. One of the shortcomings of the previous version of the spec
(JSR168)was lack of support for inter-portlet communication.As a result,
developers had to resort to all kinds of fancy workarounds using Dojo Toolkit

I am not going to explain what is JSR286 , but try to
achieve IPC for JBOSS Portal

Defining events

To define an event, you would have to declare it in
portlet.xml, as shown below

<event-definition>
<qname
xmlns:mycomp='http://www.something.com/jsr286'>
something:AppEvent</qname>
<value-type>com.ceon.ocp.portlet.WrapperEvent</value-type>
</event-definition>

Publishing an Event

<portlet>
....
<portlet-name>pubEventPortlet</portlet-name>
<supported-publishing-event>
<qname
xmlns:something='http://www.something.com/jsr286'>
something:AppEvent</qname>
</supported-publishing-event>
....
</portlet>

Processing an Event

<portlet>
....
<portlet-name>subEventPortlet</portlet-name>
<supported-processing-event>
<qname xmlns:something='http://www.something.com/jsr286'>
something:AppEvent</qname>
</supported-processing-event>
....
</portlet>

Each portlet definition can have both publishing and processing tags
and you could define multiple events within each portlet definition

Coding events

An Example event class

@XmlRootElement
public class WrapperEvent implements Serializable {
...
public static final QName QNAME = new QName(
"http://www.something.com/jsr286", "AppEvent");
private String name;
private String zipcode;
...
}

An event class must implement the Serializable interface and be annotated with
@XmlRootElement, I am using Java 5,so I used jaxb-api-2.1.jar from the JBoss portlet
container 2.0 lib directory

subscriber ( receiving portlet )

@Override
protected void doView(RenderRequest request, RenderResponse renderResponse)
throws PortletException, IOException {
renderResponse.setContentType(request.getResponseContentType());
// setting the attributes for JSP
request.setAttribute(QUOTE_ATTR, quoteId);
getPortletContext().getRequestDispatcher(VIEW_JSP).include(request,renderResponse);
}
@Override
public void processEvent(EventRequest eventRequest, EventResponse response)
throws PortletException, IOException {
Event event = eventRequest.getEvent();
if(event.getQName().equals(WrapperEvent.QNAME))
{
com.ceon.ocp.portlet.WrapperEvent
wrapEvent=(com.ceon.ocp.portlet.WrapperEvent)event.getValue();
quoteId = wrapEvent.getQuoteID();

}
}

Publisher( trigger event portlet)

@Override
protected void doView(RenderRequest request, RenderResponse renderResponse)
throws PortletException, IOException {
renderResponse.setContentType(request.getResponseContentType());
getPortletContext().getRequestDispatcher(VIEW_JSP)
.include(request,renderResponse);
}

@Override
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException {
String quoteParam = request.getParameter("QuoteID");
if ( quoteParam == null || quoteParam.trim().equals("")) {
quoteParam = "No Quote ID Specified";
}
com.ceon.ocp.portlet.WrapperEvent event
= new com.ceon.ocp.portlet.WrapperEvent();
event.setQuoteID(quoteParam);
response.setEvent(com.ceon.ocp.portlet.WrapperEvent.QNAME, event);
}

And simple JSPs for both subscriber and publisher.

reciever.jsp

<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<portlet:defineObjects />
Here are the details of Quote State:
QuoteState: <%= renderRequest.getAttribute("QuoteID")%>

publisher.jsp

<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<portlet:defineObjects />
Please enter Quote ID and click submit,you should
see the Quote State in OCP Portlet

<form method='post' action="<portlet:actionURL />" >
QuoteID: <input type=text name=QuoteID>
<input type=submit>
</form>

One final step is go to

After you have installed and configured the container,look at its core Web
application, called simple-portal.This Web application holds the framework that
displays the portlets. You could either add your own page by modifying
layouts/nav/main.jsp, or modify one of the existing pages. I modified one of the
existing pages, demo/demo.jsp, by removing its portlets and adding my own custom
portlets. The JBoss portlet container uses a very simple tag library to include
portlets, which requires the portlet name as defined in portlet.xml and the
context name of the portlet Web application

<xportal:portlet name="pubEventPortlet" applicationName="JbossSample"/>
<xportal:portlet name="subEventPortlet" applicationName="JbossSample"/>

As you can see, it's quite easy for a portlet to receive events from another portlet,
whether the two portlets reside in the same or separate Web applications.
You just have to make sure to include the common event classes in all portlet Web
applications.If you would like to deploy this application to an other Portlet 2.0
container, make sure to check that container's requirements for web.xml,as some
containers expect specific elements in the web.xml files of Portlet 2.0 applications

How to configure a Jboss Datasource?

Configuring a Datasource in Jboss very easy
Step 1: copy the original oracle-ds.xml from $jboss-home\docs\examples\jca\oracle-ds.xml to
$jboss-home\server\default\deploy\
Step 2: modify the XML file as follows
<datasources>
<local-tx-datasource>
<jndi-name>OracleDS</jndi-name>
<connection-url>jdbc:oracle:thin:@IP:PORT:SID</connection-url>
<use-java-context>false</use-java-context>
<!-- this is required if u want to connect to DB remotely from Main method-- >
<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
<user-name>user</user-name>
<password>password</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>100</max-pool-size>
<query-timeout>60</query-timeout>

<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>Oracle9i</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>

Step 3. Drop the JDBC driver jar into Jboss classpath (easiest approach - drop the jar file in to /server/default/lib)

Invoking Datasource Remotely for JBOSS

DataSources are not exposed remotely by default.

In JBoss4 and above, you can set the following attribute to false in the datasource
definition: in oracle-ds.xml to lookup datasource remotely ( typically from external
java class from main method as shown below) <use-java-context>false</use-
java-context> if datasource is not invoked remotely then you can
comment the above line in oracle-ds.xml


 public static void main(String[] args) {
try {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.factory.url.pkgs"
,"org.jboss.naming");
props.setProperty("java.naming.provider.url",
"127.0.0.1:1099");

InitialContext ctx = new InitialContext(props);
MyBeanRemote bean=(MyBeanRemote)ctx.lookup("MyBean/remote");
bean.doSomething();
DataSource ds = (DataSource)ctx.lookup("OracleDS");
Connection con = ds.getConnection();
ResultSet rs = con.createStatement()
.executeQuery("SELECT NAME FROM EMPLOYEE");
while (rs.next()) {
System.out.println("I got " + rs.getString(1));
}
rs.close();
con.close();
} catch (Exception e) {
.......
}
}

Note : if the datasource is not looked-up
remotely then the look-up might change to ctx.lookup("java:OracleDS") or
ctx.lookup("java:/OracleDS") depends on whatever DS is bound to , check the server
start up log

Create ElasticSearch cluster on single machine

I wanted to figure out how to create a multi-node ElasticSearch cluster on single machine. So i followed these instructions First i did...