How to use XLS emitter with BIRT runtime
 
By Shawn Q.

May 25, 2006


To get an initial thought for BIRT integration, please check out instructions on BIRT site.

Once you realized what you want, download the BIRT runtime package. It resides under the category "Report Engine" in the download page, the file name looks like "birt-runtime-xxx.zip".

Extract the downloaded file into disk. The folder structure may look like this:

- D:
    - birt-runtime-2_1_0rc3
       - ChartEngine
       - ReportEngine
       - WebViewerExample
(We will use this folder structure throughout the example, be sure to refer to your real path when practice)

Check out the instructions on BIRT site to make sure all 3rd party libraries are available, such as iText.

Ok, now download the XLS emitter plugin, extract to plugins folder under ReportEngine, now the folder structure looks like this:


- D:
    - birt-runtime-2_1_0rc3
       - ChartEngine
       - ReportEngine
          - plugins
             - org.uguess.birt.report.engine.emitter.xls_xxx
                - lib
       - WebViewerExample

Be sure to download and copy the POI libary into the lib folder. A tested and recommended version is poi-3.0-alpha1-20050704.jar.

Good, now we can start writing the sample program.
(the sample code is based on Mark Lorenz's handy report framework, thanks Mark and also check out here for other good tips)

Note the following sample program is written and only tested by Eclipse3.2 and BIRT_Runtime_2.1rc3.

----------------------------Report-[Start]---8<--------------------------------------------
package test;

import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.EngineConstants;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.api.HTMLCompleteImageHandler;
import org.eclipse.birt.report.engine.api.HTMLEmitterConfig;
import org.eclipse.birt.report.engine.api.HTMLRenderContext;
import org.eclipse.birt.report.engine.api.HTMLRenderOption;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.IReportEngineFactory;
import org.eclipse.birt.report.engine.api.IReportRunnable;
import org.eclipse.birt.report.engine.api.IRunAndRenderTask;

/**
 * The abstract report class
 */
public abstract class Report
{

    /**
     * A descriptive String set by my concrete subclasses at construction time.
     */
    protected String name;
    protected String format = PDF_FORMAT; // default as PDF
    public static final String PDF_FORMAT = HTMLRenderOption.OUTPUT_FORMAT_PDF;
    public static final String HTML_FORMAT = HTMLRenderOption.OUTPUT_FORMAT_HTML;
    public static final String XLS_FORMAT = "xls"; //$NON-NLS-1$
    protected static EngineConfig config;
    protected static IReportEngine engine;

    // reports

    /**
     * Constructor with required associations and/or state
     *
     * @pre name != null && name.length >0
     * @param name
     */
    public Report( String name )
    {
        super( );
        this.name = name;
    }

    /**
     * Set up for BIRT report generation. Subclasses will do the actual
     * generation based on their own designs, but using the one and only engine
     * I create.
     *
     * @return Returns the engine.
     */
    protected IReportEngine getEngine( )
    {
        if ( engine == null )
        {
            try
            {
                config = new EngineConfig( );
                config.setEngineHome( getReportEngineHome( ) );

                Platform.startup( config );
                IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
                engine = factory.createReportEngine( config );

                // If you are using old report engine sdk, the construction of ReportEngine may looks like this:
                // engine = new ReportEngine(config);

                // JRE default level is INFO, which may reveal too much internal
                // logging information.
                engine.changeLogLevel( Level.WARNING );

                // Create the emitter configuration.
                HTMLEmitterConfig hc = new HTMLEmitterConfig( );
                // Use the "HTML complete" image handler to write the files to
                // disk.
                HTMLCompleteImageHandler imageHandler = new HTMLCompleteImageHandler( );
                hc.setImageHandler( imageHandler );
                // Associate the configuration with the HTML output format.
                config.setEmitterConfiguration( HTML_FORMAT, hc );

                // setup XLS emitter configuration
                Map xlsConfig = new HashMap( );
                // Check out constants in XlsEmitterConfig.java for more configuration detail.
                xlsConfig.put( "fixed_column_width", new Integer( 50 ) ); //$NON-NLS-1$
                // Associate the configuration with the XLS output format.
                config.setEmitterConfiguration( XLS_FORMAT, xlsConfig );
            }
            catch ( Exception ex )
            {
                System.out.println( "Can't create report engine" ); //$NON-NLS-1$
            }
        }
        return engine;
    }

    /**
     * Create a formatted output file using a BIRT .rptdesign XML file.
     */
    public void generate( )
    {
        // ------ Run reports
        // --> IF params are needed for the report,
        // add logic at
        // http://eclipse.org/birt/index.php?page=deploy/engine.html

        // Output can be HTML or PDF. PDF is default.
        IReportRunnable report = null;
        try
        {
            System.out.println( getDesignFilepath( ) );
            report = getEngine( ).openReportDesign( getDesignFilepath( ) );
        }
        catch ( EngineException e )
        {
            e.printStackTrace( );
            return;
        }
        // Create a separate task for EACH report
        IRunAndRenderTask task = getEngine( ).createRunAndRenderTask( report );
        // Set up options.
        HTMLRenderOption options = new HTMLRenderOption( );
        // HTML (if specified) or PDF (default)
        options.setOutputFormat( format );
        String outputPath = getOutputPath( );
        // location
        outputPath += getOutputFilename( );
        options.setOutputFileName( outputPath );
        task.setRenderOption( options );
        HTMLRenderContext renderContext = new HTMLRenderContext( );
        renderContext.setImageDirectory( "./images" ); //$NON-NLS-1$
        HashMap appContext = new HashMap( );
        appContext.put( EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT,
                renderContext );
        task.setAppContext( appContext );
        // Set parameter values using a HashMap. Parameters are name/value
        // pairs.
        // The values must be Java objects of the correct type.
        HashMap params = getParameters( ); // each report has its own params
        task.setParameterValues( params );
        task.validateParameters( );

        // Run the report.
        try
        {
            task.run( );
            System.out.println( "Task finined successfully." ); //$NON-NLS-1$
        }
        catch ( EngineException e )
        {
            e.printStackTrace( );
        }
    }

    /**
     * Only destroy the BIRT engine when exiting.
     */
    public static void shutdown( )
    {
        Platform.shutdown( );
    }

    /**
     * @return Parameter values to use Key = param name, Value = param value
     */
    protected abstract HashMap getParameters( );

    /**
     * @post $result != null
     * @return The location of my BIRT .rptdesign file.
     */
    protected abstract String getDesignFilepath( );

    protected abstract String getOutputPath( );

    protected abstract String getReportEngineHome( );

    public void setFormat( String format ) throws InvalidParameterException
    {
        this.format = format;
    }

    /**
     * Return my name as the filename, with ".pdf" or ".html" as the extension
     * based on my current format.
     *
     * @return String my name as the filename
     */
    public String getOutputFilename( )
    {
        return getName( ) + "." + format; //$NON-NLS-1$
    }

    /**
     * @return String
     */
    public String getName( )
    {
        return name;
    }

    /**
     * Method toString.
     *
     * @return String
     */
    public String toString( )
    {
        return getName( );
    }
}
----------------------------Report-[End]---8<----------------------------------------------

Ok, now we try writing a concrete report class as this:

----------------------------MyReport-[Start]---8<------------------------------------------
package test;

import java.util.HashMap;

/**
 * A Concrete Report
*/
public class MyReport extends Report
{

    public MyReport( )
    {
        super( "My Report" ); //$NON-NLS-1$
    }

    protected String getDesignFilepath( )
    {
        return "d:\\test.rptdesign"; //$NON-NLS-1$
    }
   
    protected String getOutputPath( )
    {
        return "d:\\"; //$NON-NLS-1$
    }
   
    protected String getReportEngineHome( )
    {
        return "D:\\birt-runtime-2_1_0rc3\\ReportEngine"; //$NON-NLS-1$
    }

    protected HashMap getParameters( )
    {
        HashMap params = new HashMap( );
        return params;
    }

    public String toString( )
    {
        return "meaningful string, such as getName() + getParamString()"; //$NON-NLS-1$
    }

    public static void main( String[] args )
    {
        MyReport my = new MyReport( );

       // set pdf, html or xls here
        my.setFormat( "xls" ); //$NON-NLS-1$

        my.generate( );

        MyReport.shutdown( );
    }

}
----------------------------MyReport-[End]---8<--------------------------------------------

Perfect, now compile and run, enjoy:)
(Dont' forget to include all jars under directory "ReportEngine/lib" in class path to make the code compiled)