Wednesday, August 20, 2008

Deploying BIRT to a JBoss Portlet


BIRT uses OSGi plugins to implement most of its functionality. This works great in most cases, but when deploying to servers, configuration can be problematic especially if you are new to OSGi. If you have used BIRT’s Report Engine API before you are probably familiar with setting BIRT_HOME or using the EngineConfig.setBirtHome method. This method essentially just points to the location of the BIRT plugins.

The BIRT EngineConfig class also has a method for setting the PlatformContext (setPlatformContext). This method takes an instance of a class that implements the IPlatformContext interface. This interface is fairly simple in that it has only one method, named getPlatform which returns the path that contains the BIRT plugins. BIRT provides two default implementations of this interface (PlatformServletContext and PlatformFileContext). If the setPlatformContext is never called, BIRT defaults to a PlatformFileContext, which looks for a BIRT home location that is either set using:

System.setProperty(“BIRT_HOME”, locationtobirtplugins);
Or
EngineConfig.setBIRTHome(locationtobirtplugins);

The PlatformServletContext class is used when deploying BIRT to a web application and uses resource based operations for locating the plugins which can be included in the application. For an example of using the PlatformServletContext go
here.

This brings us the intent of this post “Deploying BIRT to a JBoss Portlet”. Within the BIRT wiki is an example of deploying BIRT to a portlet. The example is located
here.

This example works fine, but not with JBoss’s Portal Server. The reason for this is in that example the ServletContext is used to locate the BIRT plugins. Within a JBoss Portlet, I found no ready way of retrieving the ServletContext. It may be possible, but instead of going that route I decided to implement my own version of the IPlatformContext interface, which would use the PortalContext to locate the BIRT plugins.

In the example I built, I just copied the PlatformServletContext class from the BIRT source and modified it to take a PortletContext instead of a ServletContext. Being that it was only two small changes I will not post it here, but it is in the example. I then modified the example BirtEngine.java class from the portlet example above to create an instance of my new PlatformPortletContext class and set it in the engine config with the following code:



IPlatformContext context = new PlatformPortletContext( pc );
config.setPlatformContext( context );

try
{
Platform.startup( config );
}
catch ( BirtException e )
{
}


I also modified the example above to use the PortletContext to set image directories and base image url. The Portlet code looks like this:



package org.eclipse.birt.examples;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.UnavailableException;
import java.io.IOException;
import java.io.PrintWriter;

//+++++++++++++BIRT
import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.core.framework.IPlatformContext;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.report.engine.api.IReportEngineFactory;
import java.util.logging.Level;
import org.eclipse.birt.report.engine.api.EngineConstants;
import org.eclipse.birt.report.engine.api.HTMLRenderOption;
import org.eclipse.birt.report.engine.api.IReportRunnable;
import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.HTMLServerImageHandler;
import java.util.logging.*;
public class JbossBirtPortlet extends javax.portlet.GenericPortlet {
/**
*
*/
private static final long serialVersionUID = 1L;

/**
* Constructor of the object.
*/
private IReportEngine birtReportEngine = null;
protected static Logger logger = Logger.getLogger( "org.eclipse.birt" );
public JbossBirtPortlet() {
super();
}
/**
* Destruction of the portlet.
*/
public void destroy() {
super.destroy();
BirtEngine.destroyBirtEngine();
}
protected void doView(RenderRequest rRequest, RenderResponse rResponse) throws PortletException, IOException, UnavailableException {
rResponse.setContentType("text/html");
this.birtReportEngine = BirtEngine.getBirtEngine(rRequest.getPortletSession().getPortletContext());
logger.log( Level.FINE, "image directory " + rRequest.getPortletSession().getPortletContext().getRealPath("/images"));
IReportRunnable design;
try
{
//Open report design
design = this.birtReportEngine.openReportDesign( rRequest.getPortletSession().getPortletContext().getRealPath("/Reports/TopNPercent.rptdesign"));
//create task to run and render report
IRunAndRenderTask task = birtReportEngine.createRunAndRenderTask( design );
//set output options
HTMLRenderOption options = new HTMLRenderOption();
options.setOutputFormat(HTMLRenderOption.OUTPUT_FORMAT_HTML);
options.setImageHandler( new HTMLServerImageHandler() );
options.setOutputStream(rResponse.getPortletOutputStream());
options.setBaseImageURL(rRequest.getContextPath()+"/images");
options.setImageDirectory(rRequest.getPortletSession().getPortletContext().getRealPath("/images"));
task.setRenderOption(options);
//run report
task.run();
task.close();
}catch (Exception e){
e.printStackTrace();
throw new javax.portlet.PortletException( e );
}
}

/**
* Initialization of the portlet.
*
* @throws PortletException if an error occure
*/
public void init() throws javax.portlet.PortletException {
BirtEngine.initBirtConfig();
}
}






A couple of things to note: Doing it this way requires that the BIRT plugins be included as part of the Portlet WAR. This can create big war files. A better approach may be to deploy the BIRT plugins to a hard location on the system and use the system property and the default PlatformFileContext class to set the BIRT home. I also had to up the PermGen space for the JBoss AS. If you wish to download the example it is located here. Be sure to read the readme for instructions on building the WAR. The example is based off of the JBoss sample HelloWorld portlet and is located
here.