Many applications we built have to provide reporting to allow end users to monitor results, progress, etc. In most cases a simple query and an Excel export is more than sufficient. In cases where more advanced reports are required we often look at projects like JasperReports or if that doesn't suffice maybe even Crystal Reports. As I'll try to explain in this blog, it might be worth your time to take a look at Eclipse BIRT.

The Eclipse “Business Intelligence and Reporting Tools” is one of those Eclipse projects I've been tracking for some time now. Its current release features a report designer based on our favorite IDE (also available as standalone download) which allows non-technical users to create reports with charts, tables, etc. It includes a web based report viewer application. It can of course execute the report designs, but also provides export capabilities (with AJAX column selection) and a range of other features.

For a more thorough overview of the features of BIRT, take a look at their website at http://eclipse.org/birt/phoenix. Features I found interesting:

  • Standalone designer (based on the Eclipse IDE)
  • Support for various datasources (including XML)
  • Different output formats (with similiar layout)
    Currently it supports PDF and HTML, but there is also an (experimental) XLS emitter available. Look for "tribix" here: http://qauck.blogspot.com

In most cases we want to include the reporting directly in our applications (to be able to provide security, caching, etc.). Since most of our applications are built using the Spring framework it would be convenient if we could use BIRT by instantiating a few beans. As it turns out we can, but it took me some time reading (sometimes) badly documented source code and googling a lot. The remainder of this blog describes the steps I took to integrate BIRT in a sample Spring web application.

Installation of the BIRT runtime
The BIRT project provides a download of just the components needed to execute and render reports in JEE environments named appropriately the "BIRT Runtime". For the example application I used the 2.1RC6 version. You can download it from Eclipse.org and extract it to ${eclipse.birt.home} (we're using this property later on).

Example project
I've attached a Maven 1.x example project to this page which includes all the code and necessary configuration. Since the BIRT libraries are not (yet) available on any public repository, you will need to copy the required libraries from the BIRT runtime (see ${eclipse.birt.home}/WebViewExample/WEB-INF/lib) and rename them according to the project.xml.

Firing up BIRT
Before we can start calling the BIRT API to execute and render reports, the BIRT platform itself must first be started using the appropriate configuration objects. Doing this from inside a Spring application context using just the BIRT classes turned out be difficult, so I've created two utility classes:

BirtConfiguration
Besides the getters and setters for properties this class provides the getEngineConfig() method, which creates an instance of EngineConfig used to configure BIRT upon startup. The "birtInstallation" property by itself is enough to start it and render reports, but the HTML export will use file:// uri's to reference any images you've included. We can solve this by telling BIRT to use the HTMLServerImageHandler and set the appropriate configuration (see BIRT spring configuration). In addition we can also specify the loglevel here.

public EngineConfig getEngineConfig() {
    if (engineConfig == null) {
      if (log.isDebugEnabled()) {
        log.debug("Creating new instance of EngineConfig");
      }

      engineConfig = new EngineConfig();
      engineConfig.setEngineHome(birtRuntimeLocation);
      engineConfig.setLogConfig(logLocation, logLevel);

      HTMLEmitterConfig htmlConfig = new HTMLEmitterConfig();
      htmlConfig.setActionHandler(new HTMLActionHandler());
      
      // This allows images to be referenced by url
      htmlConfig.setImageHandler(new HTMLServerImageHandler());

      engineConfig.getEmitterConfigs().put("html", htmlConfig);
    }

    return engineConfig;
  }

BirtPlatformListener
This class uses the EngineConfig (as created by the BirtConfiguration class) to start and initalize the platform, and allows the creation of an IReportEngine which in turn can be used to execute and render reports.

  public void start() {
    try {
      Platform.startup(engineConfig);
    } catch (BirtException be) {
      throw new IllegalArgumentException("Failure starting BIRT platform", be);
    }
  }

  public void shutdown() {
    if (reportEngine != null) {
      reportEngine.shutdown();
    }

    // Just call shutdown
    Platform.shutdown();
  }

  public IReportEngine getReportEngine() {
    if (reportEngine == null) {
      IReportEngineFactory factory = (IReportEngineFactory)
      Platform.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);

      reportEngine = factory.createReportEngine(engineConfig);
    }

    return reportEngine;
  }

Bean definitions
Once we have these two classes, it's simply a matter of configuring beans to be able to use BIRT whenever we want to. The following snippet from the spring context shows all the required definitions to start and stop the platform along with the application and create an IReportEngine instance.

	<!-- BIRT configuration -->
	<bean id="birtConfig"
		class="com.xebia.springbirt.util.BirtConfiguration">
		<property name="birtRuntimeLocation"
			value="${eclipse.birt.home}/ReportEngine" />
		<property name="logLocation" value="${eclipse.birt.logdir}" />
	</bean>

	<bean id="platformListener"
		class="com.xebia.springbirt.util.BirtPlatformListener"
		init-method="start" destroy-method="shutdown">
		<property name="engineConfig">
			<bean factory-bean="birtConfig"
				factory-method="getEngineConfig" />
		</property>
	</bean>

	<bean id="reportEngine" factory-bean="platformListener"
		factory-method="getReportEngine" />

	<!-- Render context beans -->
	<bean id="pdfRenderContext"
		class="org.eclipse.birt.report.engine.api.PDFRenderContext">
		<property name="supportedImageFormats" value="JPG;PNG;BMP;SVG" />
	</bean>

	<bean id="htmlRenderContext"
		class="org.eclipse.birt.report.engine.api.HTMLRenderContext">
		<property name="supportedImageFormats" value="JPG;PNG;BMP" />
		<property name="baseImageURL" value="${springbirt.url}/reportImage.html?id="/>
		<property name="imageDirectory" value="${eclipse.images.dir}"/>
	</bean>

Adding some controllers
I've created two Spring MVC controllers for the sample application. The ReportImageController is responsible for receiving requests for images included in the reports. It simply tries to open the file and writes back the contents (and deletes the files afterwards).

The ReportController is responsible for calling BIRT and executing our example report. The sample application includes a report design that uses the Slashdot RSS feed as datasource. The following code shows how to execute and render this report:

protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String format = RequestUtils.getStringParameter(request, "format", "html");

    IReportRunnable runnable = reportEngine.openReportDesign(new ClassPathResource("BirtExample.rptdesign").getInputStream());
    IRunAndRenderTask runAndRenderTask = reportEngine.createRunAndRenderTask(runnable);

    // Although it's called HtmlRenderOption, it is used for all formats
    HTMLRenderOption htmlRenderOption = new HTMLRenderOption();
    htmlRenderOption.setOutputFormat(format);
    htmlRenderOption.setOutputStream(response.getOutputStream());

    runAndRenderTask.setAppContext(renderOptions);
    runAndRenderTask.setRenderOption(htmlRenderOption);
    runAndRenderTask.run();

    return null;
  }

Accessing the application (by default http://localhost:8080/springbirt/) should result in a HTML page which looks similar to this (altough probably with new entries :-)):
Screenshot of Slashdot Birt report

If you'd rather take a look at the PDF version, just append ?format=pdf to the URL. You should now get a nice PDF in the same format as the HTML version.

The example application is simple. It only allows viewing an entire report without any interaction (which in most cases is enough), but it could be extended to provide similar features to the web applicatation that BIRT provides...

Conclusion
Although BIRT still has some flaws (e.g. the designer that keeps mixing up formatting of my charts), it's a powerful platform that I'm certainly going to be using for reporting more often.