ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Deploying BIRT

by Jason Weathersby
07/26/2006

The Business Intelligence and Reporting Tools (BIRT) project is an open source, Eclipse-based reporting framework that enables the creation and deployment of complex report designs. Development with BIRT can usually be thought of as a two-step process: the creation of the report designs within the Eclipse BIRT Report Designer, followed by the deployment of the designs and framework to an application for dissemination.

Some of the most common questions in the BIRT News Group are about deploying BIRT. The reason for this is not the complexity of deploying, but the many deployment options. In this article, we will delve into the options and discover what is available to the BIRT developer.

BIRT Deployment

BIRT is comprised of several components, but it is most often associated with the Eclipse IDE platform as a set of plugins for use during design time. Although this is valid, BIRT also offers three public APIs and a J2EE servlet-based viewer that can be used outside of Eclipse.

The APIs are the Design Engine API (DE API), the Report Engine API (RE API), and the Chart Engine API (CE 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 (rptdesign), library (rptlibrary), and template (rpttemplate) files. The RE API is responsible for consuming these files and producing the report output. The Report Designer Preview and Web Viewer servlet use this API to generate reports. The CE API can be used to create and render charts standalone or through the DE and RE APIs.

The BIRT Web Viewer is a web application, comprised of servlets and JSPs, that encapsulates the RE API to generate reports. In addition to generating reports, it supports HTML pagination, PDF, Table of Contents (TOC) functionality, and export to CSV.

Because the RE API is the main API responsible for generating the reports, this article will focus on the available options for its deployment. Some of the most common deployment options are listed below:

Wrapping the RE API within an existing Java application is described on the BIRT site. See Report Engine API for more details.

The remaining scenarios are described below, with source code provided for each.

Deploying the BIRT Viewer Servlet

This form of deployment is the most common and, in most cases, tends to be the easiest. Before describing how to deploy the Viewer, an introduction to the Viewer architecture is in order.

BIRT Viewer

One of the most significant changes for BIRT 2.0 is the addition of an Ajax-based Viewer. Prior to 2.0, the servlet that executed BIRT reports would run a report and display it in either PDF or HTML. The HTML results were presented as one long page, which made viewing large reports difficult.

To address this, the BIRT engine has been enhanced to support a two-phase report generation process. The first phase, called Generation, takes the report design (.rptdesign) and generates a report document (.rptdocument), which holds the report data and design. The second phase is the Presentation phase, in which the report document is rendered in HTML, paginated HTML, or PDF. The Viewer servlet wraps the engine functionality and exposes the newer functions to the end user. This architecture also allows the engine to support Table of Contents (TOC) functionality and CSV exporting, without rerunning the report. Figure 1 shows the BIRT Viewer in action.

BIRT Viewer
Figure 1. BIRT Viewer

The new viewer adds page navigation, TOC, parameter entry, export, and print controls to the toolbar.

The Viewer servlet implements two mappings. The first is "/run" which combines the Generation and Presentation phases into one operation. This mapping should be used when generating PDF documents, or when additional functions such as Page Navigation, TOC, Export to CSV, etc., are not needed. The second is "/frameset" which was added to facilitate the new Viewer functions, and divides the Generation and Presentation phases.

BIRT Viewer Deployment

Deploying the BIRT Viewer will depend on the Application Server you are using. The BIRT Viewer is available in the WebViewerExample directory of the Report Engine download. This directory contains the entire web application, with the exception of iText.jar file (used for PDF Generation). After adding iText as described on the BIRT website, the application can be placed in a war file and deployed as a normal web application. Deployment to Tomcat can be achieved by copying the entire WebViewerExample directory to the Tomcat webapps directory.

Creating A Servlet

If you do not wish to use the BIRT Viewer, wrapping the RE API in a servlet is possible. 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.

Creating a servlet to run the BIRT RE API is very simple. To illustrate, we will deploy an example servlet that generates an HTML Report. This example uses Tomcat, and the servlet is placed in the webapps directory. See the sample code (in the Resources section) for the complete example.

  1. Create a WebReport/WEB-INF/lib directory underneath the Tomcat webapps directory.
  2. Copy all the jars in the birt-runtime-2_1_0/ReportEngine/lib directory from the Report Engine download into your WebReport/WEB-INF/lib directory.
  3. Next, create a directory named platform in your WEB-INF folder.
  4. Copy the birt-runtime-2_1_0/Report Engine/plugins and birt-runtime-2_1_0/ReportEngine/configuration directories to the platform directory you just created. In this example the context is WebReport, so the folder structure is /webapps/WebReport/platform/plugins and webapps/WebReport/platform/configuration.
  5. Additionally, create directories below WebReport for image location and report location.

When completed, your directory structure should look like Figure 2.

Web Structure when deploying BIRT Engine as a servlet
Figure 2. Folder structure when deploying BIRT Engine as a servlet

This example application consist of three files that are archived into webreport.jar. If you are using Eclipse to build the servlet, make sure to add all the jars from the birt-runtime-2_1_0/ReportEngine/lib directory to your build path. You will also need servlet.jar from the Tomcat Eclipse plugin in your build path. Either build or copy webreport.jar from the example and place it in your WEB-INF/lib directory.

BirtConfig.properties

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.

BirtEngine.java

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 webapps/WebReport/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;
  }
}

WebReport.java

The WebReport.java class contains the servlet to process requests for report generation.

The servlet's init method calls the BirtEngine.initBirtConfig() method to read the BIRT configuration file discussed earlier.

public void init() throws ServletException {
        BirtEngine.initBirtConfig();
}

The destroy method of the servlet shuts down the Report Engine and platform. This should only be called when the servlet is shutting down.

public void destroy() {
      super.destroy(); 
      //call engine and platform shutdown 
      BirtEngine.destroyBirtEngine();
}

In this example we only support GET commands. The servlet doGet method retrieves the Report Engine using the singleton described above and executes the report specified as ReportName in the URL. One of the key lines in this function is:

options.setOutputStream(resp.getOutputStream());

This particular option instructs the Report Engine to render the report to the servlet Output stream. The rest of the code in the doGet method sets up the proper directories, loads the engine, opens, and executes the report.

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

    resp.setContentType("text/html");

    //get report name and launch the engine             
    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< String, HTMLRenderContext > contextMap 
           = new HashMap< String, HTMLRenderContext >();

    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_HTML);
        options.setOutputStream(
               resp.getOutputStream());
        task.setRenderOption(options);

        //run report
        task.run();
        task.close();
        
    }catch (Exception e){
        e.printStackTrace();
        throw new ServletException( e );
    }

Copy the testWebReport.rptdesign from the example source code to the webapps/WebReport/Reports directory. Copy the web.xml from the example source code to the webapps/WebReport/WEB-INF directory. Launch a browser and enter the URL to the WebReport application. For example, http://localhost:8080/WebReport/run?ReportName=testWebReport.rptdesign.

Adding the BIRT Viewer to an RCP Application

The BIRT Viewer is also wrapped as an Eclipse plugin and is located under the eclipse/plugins/org.eclipse.birt.report.viewer_2.1.0 directory within the BIRT Designer install. When BIRT reports are previewed, this is the plugin that is used for generation and viewing. RCP developers can make use of this plugin to embed report generation and viewing capabilities within their applications.

To deploy BIRT in this fashion, first modify your plugin dependencies to include the BIRT viewer, as illustrated in Figure 3.

Dependencies needed to include BIRT Viewer
Figure 3. Dependencies needed to include the BIRT Viewer

The BIRT Viewer depends on several BIRT and Eclipse plugins, such as the Tomcat plugin. For a complete list of required plugins, see the launch configuration (RCP Example.launch) within the example source code. Also note that additional plugins that are not in the dependency list may be necessary. These are usually determined by the type of report that is being generated and include plugins such as the XML ODA driver, which is used to retrieve data from an XML stream or file.

Next, you will need to add code to your application to run and render the report. BIRT's Viewer plugin makes use of the Eclipse Tomcat plugin to run and render the reports. The BIRT Viewer is a servlet, which runs in the Tomcat plugin. BIRT supplies a utility class, WebViewer, which can be used to launch the application.

The WebViewer class supplies two variants of a method named display. This method launches the BIRT Viewer, runs, and then renders your report. Both take report name and format as parameters.

public static void display( String report, 
                            String format, 
                            boolean allowPage )

The first variant of display takes allowPage as a boolean parameter. If this parameter is true, the "/frameset" servlet mapping is used. If the parameter is false, the "/run" servlet mapping is used. As described above, the "/frameset" mapping separates running and rendering of the report, and supports operations such as TOC, Export to CSV, and paginated HTML. The "/run" mapping generates a single HTML page or PDF document with the report output.

Using display in this manner renders the report in a separate browser window. This may not be desirable. It is possible to render the BIRT reports to an SWT Browser Composite. The second variant of display takes a Browser as a parameter. In the example source, a Browser is added to an Eclipse UI ViewPart, and the report is rendered to this Browser.

public static void display( String report, 
                            String format, 
                            Browser browser, 
                            String servletName )

The servletName parameter accepts "run" or "frameset", which allows access to the full capabilities of the BIRT Viewer. The BIRT Viewer allows parameters to be entered. In BIRT 2.1, if your report definition has parameters that are required, and no default value is specified, a parameter entry dialog will be displayed before running the report.

It is also possible to use the WebViewer.startup method to launch the BIRT Viewer and then use the Browser.setUrl command to run and render the report. See the source code for an example of this option. Figure 4 shows the result.


//option 1 display with no parameters in url.  
//Parameter box will display if

//required in a separate window.  
//This uses the /run mapping

//WebViewer.display(reportName , 
//                  WebViewer.HTML, 
//                  false);     

                        

//option 2 display BIRT Viewer in separate 
//Browser with /frameset mapping

//WebViewer.display(reportName , 
//                  WebViewer.HTML, 
//                  true);

                        

//option 3 display in SWT Browser 
//using /frameset mapping.

//WebViewer.display(reportName, 
//                  WebViewer.HTML, 
//                  browser, 
//                  "frameset");

                        

//option 4 display in SWT Browser
// using /run mapping.

WebViewer.display(reportName, 
                  WebViewer.HTML, 
                  browser,
                  "run");       



//option 5 display with parameters 
//encoded in the URL

//- Note this required adding 
//additional functions

//Use the TopNPercent.rptdesign 
//with this example

//WebViewer.startup(null);

//String format = "html";

//String preParameters = 
//        createURL( "run", reportName, format );

//Make sure to encode your parameters

//String parm1NameString =  
//        URLEncoder.encode( "Top Count", "utf-8" );

//String parm1ValueString =  
//        URLEncoder.encode( "5", "utf-8" );

//String parm2NameString =  
//        URLEncoder.encode( "Top Percentage", "utf-8" );

//String parm2ValueString =  
//        URLEncoder.encode( "25", "utf-8" );

//String finalUrl = preParameters +
// "&" + parm1NameString + "=" + parm1ValueString;

//finalUrl += "&" + parm2NameString 
//+ "=" + parm2ValueString;

//browser.setUrl( finalUrl );

BIRT Viewer embedded in RCP application example
Figure 4. RCP Application with BIRT Viewer embedded

Adding the BIRT Report Engine to an RCP Application

The Report Engine download comes with the jars necessary to use the Report Engine API. In addition, it comes with plugins and configuration directories used to launch BIRT plugins that use OSGi. When building an RCP application that needs to run and render reports, using the Report Engine download is not necessary. The Report Engine API is already in plugin format within the Framework download. To use the API in your application, add the dependencies shown in Figure 5.

Dependencies needed to include BIRT Engine in RCP Application
Figure 5. Dependencies needed to include BIRT Engine

These dependencies are enough to compile Report Engine API code. To run the application, see the attached launch configuration for the needed plugins.

The attached example is very similar to the previous example. We use an SWT Browser to display the results of the run operation

The factory.createReportEngine method retrieves the report engine handle using the currently created Eclipse Platform. The engine is then used to run and render the report. You will notice that in this example the output is rendered to an output stream. This output stream is then used in the Browser.setText method to display the report, as shown in Figure 6.

private void previewReport() 
             throws EngineException {
    EngineConfig config = new EngineConfig();
    // Create the report engine
    IReportEngineFactory factory = 
              (IReportEngineFactory) Platform
          .createFactoryObject( 
                IReportEngineFactory.
                EXTENSION_REPORT_ENGINE_FACTORY );
    IReportEngine engine = factory.
                         createReportEngine( config );
    IReportRunnable design = null;

    try {
       // Open a report design - 
       // use design to 
       // modify design, retrieve
       // embedded images etc.
       String report = reportLocation.getText();
       FileInputStream fs = new 
                       FileInputStream(report);
       design = engine.openReportDesign(fs);
       IRunAndRenderTask task = engine.
                     createRunAndRenderTask(design);

       // Set Render context to handle url 
       // and image locations
       HTMLRenderContext renderContext = 
                            new HTMLRenderContext();    

       renderContext.setImageDirectory(
                                  "c:/test/image");     
       HashMap< String, HTMLRenderContext > 
            contextMap = new 
            HashMap< String, HTMLRenderContext >();
       contextMap.put( EngineConstants.
                 APPCONTEXT_HTML_RENDER_CONTEXT, 
                 renderContext );                       
       task.setAppContext(contextMap);

       // Set rendering options - 
       // such as file or stream output,
       // output format, whether it is 
       // embeddable, etc
       // Render report to 
       // Byte Array       
       IRenderOption options;
       options = new HTMLRenderOption( );
       ByteArrayOutputStream bos = 
                   new ByteArrayOutputStream();
                           
       options.setOutputStream(bos);
       options.setOutputFormat("html");

       task.setRenderOption(options);

       // run the report and destroy the engine

       task.run();
       task.close();

       //set Browser text accordingly
       browser.setText(bos.toString());
       engine.destroy();
   } catch (Exception e) {
       e.printStackTrace();
   }
}

Example RCP Application with BIRT Engine Embedded
Figure 6. Example RCP Application with BIRT Engine Embedded

Conclusion

The intent of this article is to illustrate deployment models available to the BIRT developer. The BIRT project thrives on community feedback, so please feel free to post your comments or questions in the BIRT news group.

Resources

Jason Weathersby is the BIRT Evangelist at Actuate Corporation and a member of the Eclipse Business Intelligence and Reporting Tools (BIRT) Project Management Committee (PMC).


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.