Deploying BIRT Report Engine API with Stripes Framework
Posted onA long time has passed since my previous BIRT example - Deploying BIRT Report Engine API with Apache Struts. Now that I preferably use Stripes Framework over Apache Struts, I’ve decided to port my last example to this framework.
Even though BIRT now supports HTML, Paginated HTML, PDF, Excel, Word, PowerPoint, and PostScript formats, images and charts are not embedded in Excel output. Tribix XLS Emitter 2.5.2 supports this and was used to generate Excel reports.
I’ve tried to follow some good practices that I think are important to use in a production application:
- There is a significant cost associated with creating an engine instance, due primarily to the cost of loading extensions. Therefore, each application should create just one ReportEngine instance and use it to run multiple reports. In this example the engine is started in the context listener and the same instance is always used.
- All texts in the report should be loaded from the resources so the application can be fully localizable and fully internationalized.
- You should use a JDBC data set to preview your report with BIRT designer but you must swap the data set in runtime to use data from your business logic.
- You should use predefined styles instead of custom styles as much as you can.
- Not a good practice but often a requirement, hide the master page when generating a HTML report, and change the visibility of elements so they are visible only to specified outputs.
Integrating BIRT Report Engine with your application is as easy as before:
- Copy all the jars inside the
birt-runtime-2_5_2/ReportEngine/lib
folder from the Report Engine download into/WEB-INF/lib
. - Next, create a folder named
platform
inside/WEB-INF
. - Copy
birt-runtime-2_5_2/Report Engine/plugins
andbirt-runtime-2_5_2/ReportEngine/configuration
folders into the folder/WEB-INF/platform
that you’ve just created.
BirtEngine.java
BIRT Report Engine configuration.
/**
* BIRT Report Engine configuration.
*
* @author Samuel Santos
* @version $Revision$
*/
public class BirtEngine {
private static final Logger logger = LoggerFactory.getLogger(BirtEngine.class);
private static IReportEngine birtEngine = null;
private static Level level = Level.OFF;
private static String logDirectory = null;
/**
* Gets the BIRT Report Engine.
*
* @return the BIRT Report Engine
*/
public static IReportEngine getBirtEngine() {
return birtEngine;
}
/**
* Initialize BIRT Report Engine configuration.
*
* @param servletContext the ServletContext
*/
@SuppressWarnings("unchecked")
public static synchronized void initBirtEngine(ServletContext servletContext) {
if (birtEngine == null) {
loadEngineProps();
IPlatformContext context = new PlatformServletContext(servletContext);
EngineConfig config = new EngineConfig();
HashMap<String, Object> appContext = config.getAppContext();
appContext.put(EngineConstants.APPCONTEXT_CLASSLOADER_KEY, BirtEngine.class.getClassLoader());
config.setLogConfig(logDirectory, level);
config.setEngineHome("");
config.setPlatformContext(context);
config.setResourcePath(BirtEngine.class.getResource("/").getPath());
config.setAppContext(appContext);
try {
Platform.startup(config);
} catch (BirtException e) {
logger.error(e.getMessage(), e);
}
IReportEngineFactory factory = (IReportEngineFactory) Platform
.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
birtEngine = factory.createReportEngine(config);
birtEngine.changeLogLevel(Level.WARNING);
// setup XLS emitter configuration
Map<String, Comparable> xlsConfig = new HashMap<String, Comparable>();
// Check out constants in XlsEmitterConfig.java for more configuration detail.
xlsConfig.put("fixed_column_width", new Integer(30));
xlsConfig.put("show_grid_lines", new Boolean(false));
// Associate the configuration with the XLS output format.
config.setEmitterConfiguration("xls", xlsConfig);
}
}
/**
* Destroys the BIRT Report Engine.
*/
public static synchronized void destroyBirtEngine() {
if (birtEngine == null) {
return;
}
birtEngine.destroy();
Platform.shutdown();
birtEngine = null;
}
/**
* Creates and returns a copy of this object.
*
* @return a clone of this instance.
* @exception CloneNotSupportedException if the object's class does not support the <code>Cloneable</code>
* interface. Subclasses that override the <code>clone</code> method can also throw this exception to
* indicate that an instance cannot be cloned.
*/
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* Loads the Engine properties.
*/
private static void loadEngineProps() {
try {
// Config File must be in classpath
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Properties configProps = new Properties();
InputStream in = null;
in = cl.getResourceAsStream("BirtConfig.properties");
configProps.load(in);
in.close();
String logLevel = configProps.getProperty("logLevel");
if ("SEVERE".equalsIgnoreCase(logLevel)) {
level = Level.SEVERE;
} else if ("WARNING".equalsIgnoreCase(logLevel)) {
level = Level.WARNING;
} else if ("INFO".equalsIgnoreCase(logLevel)) {
level = Level.INFO;
} else if ("CONFIG".equalsIgnoreCase(logLevel)) {
level = Level.CONFIG;
} else if ("FINE".equalsIgnoreCase(logLevel)) {
level = Level.FINE;
} else if ("FINER".equalsIgnoreCase(logLevel)) {
level = Level.FINER;
} else if ("FINEST".equalsIgnoreCase(logLevel)) {
level = Level.FINEST;
} else if ("ALL".equalsIgnoreCase(logLevel)) {
level = Level.ALL;
}
logDirectory = configProps.getProperty("logDirectory");
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
WebReportActionBean.java
Actions related to the reports generation.
/**
* Actions related to the reports generation.
*
* @author Samuel Santos
* @version $Revision$
*/
public class WebReportActionBean implements ActionBean {
private ActionBeanContext context;
public ActionBeanContext getContext() {
return context;
}
public void setContext(ActionBeanContext context) {
this.context = context;
}
/**
* Generates the html report.
*
* @return forward to the jsp page
* @throws EngineException when opening report design or reunning the report
* @throws SemanticException when changing properties of DesignElementHandle
*/
@DefaultHandler
public Resolution htmlReport() throws EngineException, SemanticException {
ByteArrayOutputStream reportOutput = new ByteArrayOutputStream();
// set output options
IHTMLRenderOption options = new HTMLRenderOption();
options.setOutputFormat(IHTMLRenderOption.OUTPUT_FORMAT_HTML);
options.setOutputStream(reportOutput);
options.setEmbeddable(true);
options.setBaseImageURL(context.getRequest().getContextPath() + "/images");
options.setImageDirectory(context.getServletContext().getRealPath("/images"));
options.setImageHandler(new HTMLServerImageHandler());
options.setMasterPageContent(false);
generateReport(options);
return new StreamingResolution("text/html", new ByteArrayInputStream(reportOutput.toByteArray()));
}
/**
* Generates the PDF report.
*
* @return PDF file with the report output
* @throws EngineException when opening report design or reunning the report
* @throws SemanticException when changing properties of DesignElementHandle
*/
public Resolution pdfReport() throws EngineException, SemanticException {
ByteArrayOutputStream reportOutput = new ByteArrayOutputStream();
// set output options
IPDFRenderOption options = new PDFRenderOption();
options.setOutputFormat(IPDFRenderOption.OUTPUT_FORMAT_PDF);
options.setOutputStream(reportOutput);
generateReport(options);
return new StreamingResolution("application/pdf", new ByteArrayInputStream(reportOutput.toByteArray()))
.setFilename("WebReport.pdf");
}
/**
* Generates the Excel report.
*
* @return Excel file with the report output
* @throws EngineException when opening report design or reunning the report
* @throws SemanticException when changing properties of DesignElementHandle
*/
public Resolution xlsReport() throws EngineException, SemanticException {
ByteArrayOutputStream reportOutput = new ByteArrayOutputStream();
// set output options
IExcelRenderOption options = new EXCELRenderOption();
options.setOutputFormat("xls");
options.setOutputStream(reportOutput);
generateReport(options);
return new StreamingResolution("application/vnd.ms-excel", new ByteArrayInputStream(reportOutput.toByteArray()))
.setFilename("WebReport.xls");
}
/**
* Generates the Word report.
*
* @return Excel file with the report output
* @throws EngineException when opening report design or reunning the report
* @throws SemanticException when changing properties of DesignElementHandle
*/
public Resolution docReport() throws EngineException, SemanticException {
ByteArrayOutputStream reportOutput = new ByteArrayOutputStream();
// set output options
IRenderOption options = new RenderOption();
options.setOutputFormat("doc");
options.setOutputStream(reportOutput);
generateReport(options);
return new StreamingResolution("application/vnd.ms-word", new ByteArrayInputStream(reportOutput.toByteArray()))
.setFilename("WebReport.doc");
}
/**
* Generates the Powerpoint report.
*
* @return Excel file with the report output
* @throws EngineException when opening report design or reunning the report
* @throws SemanticException when changing properties of DesignElementHandle
*/
public Resolution pptReport() throws EngineException, SemanticException {
ByteArrayOutputStream reportOutput = new ByteArrayOutputStream();
// set output options
IRenderOption options = new RenderOption();
options.setOutputFormat("ppt");
options.setOutputStream(reportOutput);
generateReport(options);
return new StreamingResolution("application/vnd.ms-powerpoint", new ByteArrayInputStream(reportOutput
.toByteArray())).setFilename("WebReport.ppt");
}
/**
* Generates the Postscript report.
*
* @return Excel file with the report output
* @throws EngineException when opening report design or reunning the report
* @throws SemanticException when changing properties of DesignElementHandle
*/
public Resolution psReport() throws EngineException, SemanticException {
ByteArrayOutputStream reportOutput = new ByteArrayOutputStream();
// set output options
IRenderOption options = new RenderOption();
options.setOutputFormat("postscript");
options.setOutputStream(reportOutput);
generateReport(options);
return new StreamingResolution("application/postscript", new ByteArrayInputStream(reportOutput.toByteArray()))
.setFilename("WebReport.ps");
}
/**
* Generates the report output.
*
* @throws EngineException when opening report design or reunning the report
* @throws SemanticException when changing properties of DesignElementHandle
*/
@SuppressWarnings("unchecked")
private void generateReport(IRenderOption options) throws EngineException, SemanticException {
// this list simulates a call to the application business logic
List<WebReportEntity> webReportEntities = new ArrayList<WebReportEntity>();
webReportEntities.add(new WebReportEntity(new Double(2), "Product1"));
webReportEntities.add(new WebReportEntity(new Double(4), "Product2"));
webReportEntities.add(new WebReportEntity(new Double(7), "Product3"));
// get the engine
IReportEngine birtReportEngine = BirtEngine.getBirtEngine();
// open the report design
IReportRunnable design = birtReportEngine.openReportDesign(context.getServletContext().getRealPath("/reports")
+ "/webReport.rptdesign");
// create task to run and render report
IRunAndRenderTask task = birtReportEngine.createRunAndRenderTask(design);
Map<String, Object> appContext = task.getAppContext();
DesignElementHandle reportChart = design.getDesignHandle().getModuleHandle().findElement("reportChart");
appContext.put("webReportEntities", webReportEntities);
reportChart.setProperty("dataSet", "Scripted Data Set"); // change the chart dataset
task.setLocale(context.getLocale());
task.setRenderOption(options);
task.setAppContext(appContext);
// run report
task.run();
task.close();
}
}
If you checkout the source code from the source code repository, you need to rename build.properties-sample
to build.properties
.
Edit both build.properties
and BirtConfig.properties
files to correctly reflect your environment.
Resources
- Stripes BIRT WebReport project Download the WAR file or get the source code from the SVN repository.
- Report Engine API Contains further information on BIRT RE API.
Comments
comments powered by Disqus