Deploying BIRT in Your Existing Web Application

Posted on Monday, August 30, 2010

0


The Business Intelligence and Reporting Tools (BIRT) project is an open source, Eclipse-based reporting framework that enables the creation and deployment of enterprise reports. Development with BIRT can usually be thought of as a two-step process:

  1. The creation of the report designs within the Eclipse BIRT Report Designer
  2. Deployment of the designs and framework to an application for dissemination.

BIRT also offers three public APIs and a J2EE servlet-based viewer that can be used outside of Eclipse.

  1. Design Engine API (DE API) – The DE API is responsible for creating and modifying the XML report design format. This API is what the Eclipse BIRT Report Designer uses to create the report design
  2. Report Engine API (RE API) – The RE API is responsible for consuming the report design files and producing the report output.
  3. Chart Engine API (CE API) – The CE API can be used to create and render charts standalone or through the DE and RE APIs.

There are 2 major deployment formats that are used for web applications

  1. Deploy the BIRT Viewer to a J2EE application server.
  2. Create a servlet that wraps the RE API and deploy it to a J2EE application server.

Assuming that you already have a pre-existing web application and would like to introduce the BIRT component for reporting, the recommended format would be the latter i.e. “Create a servlet that wraps the RE API and deploy it to a J2EE application server.”

Existing Web Application with embedded BIRT RE

Advantages of this approach are that you can embed the runtime engine in the existing web application and use the RE for generating reports. There is no need to copy the design report files to a separate war for rendering. It is a clean approach to integrate with your existing web application.

The major drawback to deploying BIRT in this fashion is that native functions supplied by the BIRT Viewer are not available without the developer coding them into the servlet. These functions include TOC, paginated HTML, and export to CSV. This approach is generally used when the developer has a specific need, such as direct generation to PDF, and does not want the other functions available in the viewer. This approach is as good as writing a custom viewer for report rendering.

How to embed the Runtime Engine into the existing Web App? Assuming that you are using the eclipse platform, you would need the RE and related jars in your project so that you can use them at compile time. Unfortunately, the BIRT jars are not present on the maven repository. There are BIRT 2.3.2 jars which are present on JBoss but for the latest release we would have to manually install these jars in the local repository. If you have nexus, then you could install there jars there too. Use the following script to install the BIRT jars into the mvn repository

#!/bin/sh
BV=2.6 #Birt version
PK=jar   #package type

# For linux export BIRT_HOME=/path/to/birt

if  [ "x" = "x$BIRT_HOME" ]; then
echo "variable BIRT_HOME , containing lib/ and plugins/ must be set"
exit;
fi;

BASEDIR=$BIRT_HOME/ReportEngine/lib
BIRT_GROUP=org.eclipse.birt
EMF_GROUP=org.eclipse.emf.ecore
ACTUAL_DIR=`pwd`
cd $BASEDIR
#We Assume thar the 3 emf jars have the same version number , and take org.eclipse.emf.common for reference
# For 2.6 i changed the emf ecore xmi jar to have the same version as the common
EMFV=`ls org.eclipse.emf.common*.jar | sed -e 's/\.jar$//' | sed -e 's/.*_//'`

#BIRT APIs
for i in `ls *api.jar | sed -e 's/\.jar$//'` ;
do mvn install:install-file -DgroupId=$BIRT_GROUP -DartifactId=${i} -Dversion=$BV -Dpackaging=$PK -Dfile="$BASEDIR/${i}.$PK" -DgeneratePom=true ;
done;

#EMF ( Eclipse Modeling Framework ) . Assuming the 3 emf jars have the same version number!
for i in `ls org.eclipse.emf.*.jar | sed -e 's/\.jar$//' | sed -e 's/_.*//'` ;
do mvn install:install-file -DgroupId=${i} -DartifactId=${i} -Dversion=$EMFV -Dpackaging=$PK -Dfile="$BASEDIR/${i}_$EMFV.$PK" -DgeneratePom=true ;
done;

# Misc
mvn install:install-file -DgroupId=org.w3c -DartifactId=flute -Dversion=1.3 -Dpackaging=jar -Dfile="flute.$PK" -DgeneratePom=true
mvn install:install-file -DgroupId=org.w3c -DartifactId=sac -Dversion=1.3.0.v200805290154 -Dpackaging=jar -Dfile="org.w3c.css.sac_1.3.0.v200805290154.$PK" -DgeneratePom=true
mvn install:install-file -DgroupId=com.ibm -DartifactId=icu -Dversion=4.2.1.v20100412 -Dpackaging=jar -Dfile="com.ibm.icu_4.2.1.v20100412.$PK" -DgeneratePom=true
mvn install:install-file -DgroupId=com.lowagie -DartifactId=itext -Dversion=1.3 -Dpackaging=jar -Dfile="itext-1.3.$PK" -DgeneratePom=true
mvn install:install-file -DgroupId=org.mozilla.rhino -DartifactId=js -Dversion=1.6R7 -Dpackaging=jar -Dfile="js.$PK" -DgeneratePom=true
#
cd $ACTUAL_DIR

Once the jars are installed in the maven repository, you could include them in your pom.xml as

	<properties>
		<birt.version>2.6</birt.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.birt</groupId>
			<artifactId>chartengineapi</artifactId>
			<version>${birt.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.birt</groupId>
			<artifactId>coreapi</artifactId>
			<version>${birt.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.birt</groupId>
			<artifactId>dataadapterapi</artifactId>
			<version>${birt.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.birt</groupId>
			<artifactId>engineapi</artifactId>
			<version>${birt.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.birt</groupId>
			<artifactId>modelapi</artifactId>
			<version>${birt.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.birt</groupId>
			<artifactId>scriptapi</artifactId>
			<version>${birt.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.birt</groupId>
			<artifactId>dteapi</artifactId>
			<version>${birt.version}</version>
		</dependency>

		<dependency>
			<groupId>com.ibm</groupId>
			<artifactId>icu</artifactId>
			<version>4.2.1.v20100412</version>
		</dependency>

		<dependency>
			<groupId>org.mozilla.rhino</groupId>
			<artifactId>js</artifactId>
			<version>1.6R7</version>
		</dependency>

		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.4</version>
		</dependency>
		<!-- Added the extra w3c jars that we had included -->
		<dependency>
			<groupId>org.w3c</groupId>
			<artifactId>flute</artifactId>
			<version>1.3</version>
		</dependency>

		<dependency>
			<groupId>org.w3c</groupId>
			<artifactId>sac</artifactId>
			<version>1.3.0.v200805290154</version>
		</dependency>

	</dependencies>


Next step is create a platform directory under src/main/webapp/WEB-INF of your maven structure.  Copy the birt-runtime-2_6_0/Report Engine/plugins and birt-runtime-2_6_0/Report Engine/configuration directories to the platform directory you just created. These 3 steps complete the embed part of the BIRT RE into your web application.

How to call the RE for a report request?

There are three main components required to generate the report for an incoming request
  1. A servlet to handle the request and pass the details to the Report Engine

  2. Report Engine Manager to set up the report engine and start a BIRT Engine instance

  3. A configuration file for BIRT RE

The BirtConfig.properties file contains only two entries.logDirectory=c:/temp logLevel=FINEST The logDirectory specifies where the Report Engine will log entries. The logLevel sets the level for logging. As stated earlier, we would have to create a Report. java servlet which serves the requests for reports. The doGet() method of the servlet could be something like this

	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		resp.setContentType("application/pdf");

		// fetch the report name to be generated
		String reportName = req.getParameter("ReportName");
		ServletContext sc = req.getSession().getServletContext();
		this.birtReportEngine = BirtEngine.getBirtEngine(sc);

		// setup image directory
		HTMLRenderContext renderContext = new HTMLRenderContext();
		renderContext.setBaseImageURL(req.getContextPath() + "/images");
		renderContext.setImageDirectory(sc.getRealPath("/images"));

		HashMap contextMap = new HashMap();
		contextMap.put(EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT,
				renderContext);

		IReportRunnable design;
		try {
			// Open report design
			design = birtReportEngine.openReportDesign(sc
					.getRealPath("/Reports") + "/" + reportName);
			// create task to run and render report
			IRunAndRenderTask task = birtReportEngine
					.createRunAndRenderTask(design);
			task.setAppContext(contextMap);

			// set output options
			HTMLRenderOption options = new HTMLRenderOption();
			options.setOutputFormat(HTMLRenderOption.OUTPUT_FORMAT_PDF);
			options.setOutputStream(resp.getOutputStream());
			task.setRenderOption(options);

			task.run();
			task.close();
		} catch (Exception e) {

			e.printStackTrace();
			throw new ServletException(e);
		}
	}

The BirtEngine.java class is a singleton and is used to create one instance of the Report Engine for the servlet. It has a synchronized method for retrieving the instance, or creating and configuring it if it doesn’t exist. The BIRT Report Engine uses OSGi to load Eclipse plugins. These plugins provide functions for connecting to data sources and emitting PDF/HTML. The following lines instruct the Report Engine to look in the src/main/webapp/WEB-INF/platform directory when starting Eclipse plugins using OSGi.

config.setEngineHome("");
IPlatformContext context =
               new PlatformServletContext( sc );
config.setPlatformContext( context );

The rest of the code within this class is responsible for starting the platform and creating the report engine.

public static synchronized IReportEngine getBirtEngine(ServletContext sc) {
  if (birtEngine == null)
  {
                .
                .
        try
        {
                //Start up the OSGi framework
                Platform.startup( config );
        }
        catch ( BirtException e )
        {
                e.printStackTrace( );
        }

        IReportEngineFactory factory =
                (IReportEngineFactory) Platform.
                 createFactoryObject(
                 IReportEngineFactory.
                 EXTENSION_REPORT_ENGINE_FACTORY
                 );

        birtEngine =
                  factory.createReportEngine( config );
        }
        return birtEngine;
  }
}

Other Useful References:

Advertisements
Posted in: Architecture, Java