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.

15 comments:

Anonymous said...

can you upload the portlet plz

Jason Weathersby said...

What version of BIRT are you using? A few things have changed since this example was created.

Jason

Anonymous said...

Is it possible to add some input area to change dynamically my birt parameters?

Jason Weathersby said...

If you are using the portlet here than you will need to implement this yourself. If you are using BIRT in general there is a button in the viewer to change parameters. Optionally you could use the tag libs. See
http://birtworld.blogspot.com/2007/09/22-birt-tag-library-building-custom.html

Anonymous said...

I tried using "rResponse.getPortletOutputStream().write" method to add my form before the report but it seem that it's not possible.
Is there another method to do it?
(I'm beginner in java and birt)

Ian said...

does anyone know how to make this example connect to oracle db?

saravanan said...

I have deployed the jbossbirtportlet in jboss portal server 2.7.2 and using BIRT 2.5. But when I access the portlet it is showing as null in the screen. There is no error. But when I save it as html file in file system when accessing the portlet it is working. Can you help me on this?

Jason Weathersby said...

Can you set the log level for BIRT to WARNING and check the BIRT log?

saravanan said...

Thanks for your reply. It seems some issue with jboss portal 2.7.2 which prevents from rendering the report. The Birt report is working fine in Jboss portal 2.6.8 and 2.6.2 as well.

Jason Weathersby said...

did you get anything in the log?

saravanan said...

After setting it to WARNING, the log file is empty. When it is finest there are lot of log entries but it doesn't show me any error. I have posted the logs in the forum http://www.birt-exchange.org/forum/deploying-integrating-birt-report-engine-applications/17264-birt-jboss-portal.html

Jason Weathersby said...

Not sure whats wrong here but for some reason JBoss is not using the outputstream. I know its getting populated because I used an intermediate ByteArrayOutputStream and wrote it out to a file and it worked properly. I will post a work around on the birt exchange link.

saravanan said...

Thanks a lot Jason. It is working for me. I tried with ByteArrayOutputStream earlier, but something I have missed at that time.

PRADS said...

Guys,I need a small help..I have a BIRT report(tabular columns) which i have generated using HTMLRenderOption.
Now my problem is i have a column in the table which is a hyperlink and on clicking the entry in the table it should open a new browser corresponding to the link clicked.How can i do this?..Guys please its urgent..Reply asap....

Jason Weathersby said...

Are you using BIRT in a portlet or just the standard viewer? Did you add a hyperlink to the column data element in the report design?