Eclipse Business Intelligence and Reporting Tools (BIRT) is an open source reporting system that can be integrated with Web applications. As described in the Eclipse BIRT overview materials, BIRT enables you to add a rich variety of compelling reports to enhance your application:
- Lists: The simplest reports are lists of data. As the lists get longer, you can add grouping to organize related data together. If your data is numeric, you can easily add totals, averages, and other summaries.
- Charts: Numeric data is often much easier to understand when presented as a chart. BIRT offers a charting engine that lets you add pie charts, line and bar charts, and many more to your applications.
- Crosstabs: Also called a cross-tabulation or matrix, crosstabs show data in two dimensions; for example, sales per quarter or hits per Web page.
- Compound reports: Reports that can combine the above into a single document.
Reports consist of four main parts:
- Data: Databases, XML files, and Web services can supply data to your BIRT report. A single report can include data from any number of data sources.
- Data transforms: Reports present data sorted, summarized, filtered, and grouped to fit a user's needs. While databases can do some of this work, BIRT must do it for simple data sources such as flat files.
- Business logic: Real world data is seldom structured exactly as you'd like for a report. Many reports require business-specific logic to convert raw data into information that is useful for the user. If the logic is just for the report, you can script it using BIRT's JavaScript™ support. If your application already contains the logic, you can call into your existing code.
- Presentation: Once the data is ready, you have a wide range of options for presenting it: in tables, charts, text, and more.
BIRT has two main components:
- A report designer based on Eclipse.
- A run time component that you can add to Project Zero or WebSphere sMash applications.
This article illustrates how to integrate the BIRT report engine into a Project Zero or WebSphere sMash PHP application by showing you how to:
- Render BIRT reports using the BIRT Classic Models sample database.
- Efficiently load and invoke the BIRT Reporting Engine.
- Pass report parameters into BIRT from a Project Zero PHP application.
- Configure BIRT reports to pull images from Project Zero.
- Create data sets in PHP and render them in a BIRT report.
This article assumes a basic familiarity with WebSphere sMash or Project Zero and with the concepts of PHP. It is also assumed that you have a suitable Java Development Kit (JDK) installed on your machine. No particular knowledge of Java is required to understand this article.
To make the examples realistic, this article uses the Eclipse Classic Models database, which uses a MySQL database server. Instructions for installing the Classic Models sample database (which is distributed in a .zip file) are available from Eclipse. Figure 1 shows an example BIRT report that includes graphics, text, and tables. The table data is extracted from the Classic Models database when the report is generated.
Figure 1. BIRT report using the Classic Models sample database
The Eclipse Classic Models database can also be used with the Apache Derby.
Create and configure an application to use BIRT
-
Create a PHP application
Your first step is to download and install the latest version of Project Zero. This article uses the command-line interface, but the concepts in this article work equally well if you prefer to use the AppBuilder or Eclipse.
Create a PHP application called "demo," as detailed in the IBM WebSphere sMash Getting Started Guide. Figure 2 shows the console output from creating the demo application. Make sure you add the PHP dependency in the ivy.xml configuration file (see the Adding PHP Support section in the Getting Started Guide). In short, your config/ivy.xml dependency file must contain this entry:
<dependency name="zero.php" org="zero" rev="[1.0.0.0, 2.0.0.0["/>You must also run an update step after changing the ivy.xml configuration file. (Figure 2 shows output from an experimental Version 2 build. Your output will be similar but the version numbers will begin "1.0.0.0." This is expected behaviour.)
Figure 2. Output from creating a command-line Project Zero PHP application
The demo application directory structure is shown in Figure 3.
Figure 3. Project Zero PHP application directory structure
-
Download the Eclipse BIRT project
The BIRT runtime contains the minimum essential components (JAR files) to render reports in a Project Zero PHP application. (This article uses BIRT 2.3 which was the most recent stable version as of this writing.) Also download the all-in-one package that contains the Eclipse Report Designer, which provides an easy to use environment for creating and editing reports.
-
Configure the application to use BIRT
- Unzip the BIRT runtime download into the demo application directory
(Figure 4).
Figure 4. The demo directory including the BIRT runtime
- Copy the files from demo/birt-runtime-2_3_1/ReportEngine/lib to the
demo/lib directory (Figure 5).
Figure 5. Contents of the demo/lib directory after copying the files
- If you havenât yet created the Classic Models sample database, do so now. Instructions for installing the database are on the Eclipse BIRT Web site.
- The Classic Models sample .zip file contains the directories shown in
Figure 6. Copy the images directory into your demo/public directory.
Figure 6. Contents of the Classic Models sample ZIP file
- You will need a JDBC driver so that BIRT can communicate with the MySQL database. Download the Connector/J JDBC driver from the MySQL Web site. Be sure to match the Connector/J version with the version of MySQL server you have installed.
- Copy the MySQL Connector/J JAR file that you download to the demo/birt-runtime-2_3_1/ReportEngine/plugins/org.eclipse.birt.report.data.oda.jdbc_2.3.1.v20080827/drivers directory. (The directory name will be different if you are using a different version of BIRT.)
- Perform a
zero updatestep on the demo application by running the command shown in Figure 7. This adds the BIRT runtime to the demo application class path.
Figure 7. Running an update on the application
- Download the report definitions
included with this
article. Copy these
report definitions into the demo application under the demo/samples
directory (Figure 8).
Figure 8. The report definition samples
- Start the demo application by entering the
zero startcommand shown in Figure 9.
Figure 9. Starting the demo application
The demo application directory should now look like Figure 10.
Figure 10. The demo application directory
- Unzip the BIRT runtime download into the demo application directory
(Figure 4).
You have successfully configured a Project Zero application to use Eclipse BIRT.
Render reports using BIRT with PHP
Create a "Hello World" PHP script that renders a report using BIRT:
- Create a PHP file called "hello_world.php" in the demo/public directory.
- Copy and paste the PHP code shown in Listing 1 into the file and save it. The
code is a sample program that loads and renders a basic report to HTML. The code
is examined in more detail in the next section. (Check the version of BIRT that you unzipped. If it is not version 2_3_1,
then the second line of this script must be changed to match the
version you are using.)
Listing 1<?php $root_directory = zget("/config/root"); $report_engine = $root_directory."/birt-runtime-2_3_1/ReportEngine"; $log_directory = zget("/config/appLogDir"); $design_file = $root_directory."/samples/hello_world.rptdesign"; $generated_report_file = tempnam(sys_get_temp_dir(), 'report'); java_import("org.eclipse.birt.core.framework.Platform"); java_import("java.util.logging.Level"); java_import("org.eclipse.birt.report.engine.api.EngineConfig"); java_import("org.eclipse.birt.report.engine.api.IReportEngineFactory"); java_import("org.eclipse.birt.report.engine.api.HTMLRenderOption"); $config = new EngineConfig(); $config->setBIRTHome($report_engine); $config->setLogConfig($log_directory, Level::$FINEST); Platform::startup($config); $factory = Platform::createFactoryObject( IReportEngineFactory::$EXTENSION_REPORT_ENGINE_FACTORY); $engine = $factory->createReportEngine($config); $design = $engine->openReportDesign($design_file); $task = $engine->createRunAndRenderTask($design); $task->validateParameters(); $options = new HTMLRenderOption(); $options->setOutputFileName($generated_report_file); $options->setOutputFormat("html"); $options->setHtmlRtLFlag(false); $options->setEmbeddable(false); $task->setRenderOption($options); $task->run(); $task->close(); $engine->destroy(); echo file_get_contents($generated_report_file); unlink($generated_report_file); ?>
- From a Web browser, navigate to http://locahost:8080/hello_world.php. If the
result looks similar to Figure 11, then you have successfully rendered a BIRT
report in a Project Zero PHP application.
Figure 11. Web browser output from rendering the report
- If the report fails to render, make sure your ivy.xml file is properly configured, then retry.
Now, letâs go through the sample PHP code you ran above and see what is happening:
-
Configure directory
Listing 2<?php $root_directory = zget("/config/root"); $report_engine = $root_directory."/birt-runtime-2_3_1/ReportEngine"; $log_directory = zget("/config/appLogDir"); $design_file = $root_directory."/samples/hello_world.rptdesign"; $generated_report_file = tempnam(sys_get_temp_dir(), 'report');
The program first finds where the BIRT runtime has been installed (you copied it to demo/birt-runtime-2_3_1). The root directory of the current Project Zero application is available in the global context under /config/root. The code uses the PHP function
zgetto get the directory from the global context.You also need the fully qualified path name for the hello world report definition. You copied the report definitions provided into the demo/samples directory. When you render the report, you render it into a temporary file. The file will be echoed to the browser and then deleted. This is a convenient and easy way to use BIRT. The calls to tempnam and sys_get_temp_dir create a temporary file in the applicationâs temporary directory.
-
Import the BIRT classes
Listing 3java_import("org.eclipse.birt.core.framework.Platform"); java_import("java.util.logging.Level"); java_import("org.eclipse.birt.report.engine.api.EngineConfig"); java_import("org.eclipse.birt.report.engine.api.IReportEngineFactory"); java_import("org.eclipse.birt.report.engine.api.HTMLRenderOption");
The java_import function brings Java class definitions into the PHP runtime. The next code sections, below, make use of these classes. Having imported the EngineConfig class, the PHP code can create instances of that class using the
newoperator (for example,new EngineConfig). Likewise, by importing the Level class, you can access static members in the PHP code using the â::â syntax; for example:Level::$FINEST. -
Create a report engine
Listing 4$config = new EngineConfig(); $config->setBIRTHome($report_engine); $config->setLogConfig($log_directory, Level::$FINEST); Platform::startup($config); $factory = Platform::createFactoryObject( IReportEngineFactory::$EXTENSION_REPORT_ENGINE_FACTORY); $engine = $factory->createReportEngine($config);
This code starts up the BIRT runtime and creates a report engine. The report engine is the business end of BIRT and is responsible for rendering reports. The setBIRTHome method call is important because BIRT needs to be told where it was installed.
-
Render the report
Listing 5$design = $engine->openReportDesign($design_file); $task = $engine->createRunAndRenderTask($design); $task->validateParameters(); $options = new HTMLRenderOption(); $options->setOutputFileName($generated_report_file); $options->setOutputFormat("html"); $options->setHtmlRtLFlag(false); $options->setEmbeddable(false); $task->setRenderOption($options); $task->run(); $task->close(); $engine->destroy(); echo file_get_contents($generated_report_file); unlink($generated_report_file); ?>
This code renders the report to a temporary file. The code echoes the file contents to the browser using
file_get_contentsandecho. BIRT has many options to choose from when rendering reports, such as the output format (HTML, PDF or XML). After it has rendered the report, the code shuts down the BIRT runtime.
The sample PHP code above started BIRT, rendered a report, and shut BIRT down. The problem with this approach is that BIRT is intended to be started up once (and only once) per process and used multiple times. The engine is thread safe so it can be shared amongst multiple threads all rendering reports. So, how do you start BIRT just once and share it across threads?
This sounds suspiciously like a singleton pattern (or anti-pattern, if you prefer). The trouble is that process-wide singletons are hard to implement correctly in PHP. You might think the PHP code in Listing 6 would work:
Listing 6
<?php
class Singleton {
private static $birt = NULL;
static function getInstance() {
if (self::$birt == NULL) {
self::$birt = new BIRT(...);
}
return self::$birt;
}
}
?> |
But there are two problems with this code:
- At the end of the current request, all statics are cleared away by PHP so $birt is reset to NULL. You want to keep the BIRT runtime around forever (or at least until the process shuts down).
- On a multithreaded PHP runtime, like WebSphere sMash, each PHP request gets an independent set of static variables. If two requests are being processed, and each request calls getInstance, then they will both create an instance of the BIRT class.
The design intent in PHP is that each request looks like it is running in a single threaded PHP process, even though it is actually running in a multithreaded PHP process. But what you want is one BIRT runtime in a process that can be shared amongst all threads.
The solution presented here is to use global context, which is a storage area in WebSphere sMash where you can put just about anything. Different bits of the global context have different lifetimes. For example, the /request zone is only kept around for the current request. In the sample PHP code, the BIRT runtime is in the /tmp zone so that it is kept around for the lifetime of the process. The /tmp zone is also available to all threads running in the same process and is never persisted.
The final piece of the puzzle is to ensure only one thread actually creates the BIRT runtime. Dealing with multiple threads can be something of a headache, but the global context provides a solution with locking commands. Each thread does a lock, checks to see if the BIRT runtime has already been created -- if not, then it creates it and puts it in the /tmp zone -- and then unlocks.
PHP is a single threaded language. It does not provide any threading facilities to the PHP developer. Java, however, is a multi-threaded language. Furthermore, some Java libraries (like BIRT) have to be initialized once (per process) before being used. This combination is a little tricky to implement in PHP, which is non-threaded and stateless. The solution presented in the next section shows one possible solution that initializes the BIRT runtime and puts it in the /tmp zone in the global context. The use of locking commands to synchronize threads is unfamiliar to most PHP developers, but is a pragmatic way to ensure something happens once and only once.
Letâs go ahead and update the sample PHP code to load the BIRT runtime once:
- Create a new PHP script called startup.php in demo/app/scripts (the
/app/scripts directory is automatically put on the application include path).
This new PHP script handles the setup of the BIRT runtime. Once the report
engine has been created, it is stored in the global context under the
/tmp/factory path. Subsequent calls to the startup method in this script return
the existing report engine to the caller. Copy and paste the PHP code shown in Listing 7 into the file and save it.
Listing 7<?php function startup() { $engine = zget("/tmp/factory"); if ($engine != NULL) return $engine; // Serialize platform startup zpost("/app#lock", "/engine"); $engine = zget("/tmp/factory"); if ($engine != NULL) return $engine; // Did someone else get in first? $root_directory = zget("/config/root"); $report_directory = $root_directory."/birt-runtime-2_3_1/ReportEngine"; $log_directory = zget("/config/appLogDir"); java_import("org.eclipse.birt.core.framework.Platform"); java_import("java.util.logging.Level"); java_import("org.eclipse.birt.report.engine.api.EngineConfig"); java_import( "org.eclipse.birt.report.engine.api.IReportEngineFactory"); $config = new EngineConfig(); $config->setBIRTHome($report_directory); $config->setLogConfig($log_directory, Level::$FINEST); Platform::startup($config); $factory = Platform::createFactoryObject( IReportEngineFactory::$EXTENSION_REPORT_ENGINE_FACTORY); // Store in process wide non persistent zone $engine = $factory->createReportEngine($config); zput("/tmp/factory", $engine); // Unlock when we are finished! zpost("/app#unlock", "/engine"); return $engine; } ?> - Copy and paste the code in Listing 8 to the hello_world.php script. This code
should replace the existing contents of the script. This new
script includes the startup.php script and calls the startup method to get the
report engine. This solution is much more efficient. The first PHP script to
render a report causes the BIRT report engine to be created. All subsequent
reports use the same report engine instance.
Listing 8<?php require_once 'startup.php'; $engine = startup(); $root_directory = zget("/config/root"); $design_file = $root_directory."/samples/hello_world.rptdesign"; $design = $engine->openReportDesign($design_file); $task = $engine->createRunAndRenderTask($design); $task->validateParameters(); java_import("org.eclipse.birt.report.engine.api.HTMLRenderOption"); $generated_report_file = tempnam(sys_get_temp_dir(), 'report'); $options = new HTMLRenderOption(); $options->setOutputFileName($generated_report_file); $options->setOutputFormat("html"); $task->setRenderOption($options); $task->run(); $task->close(); echo file_get_contents($generated_report_file); unlink($generated_report_file); ?>
Passing the report name in the URL
A limitation of the PHP script is that the report definition is hardcoded, as shown in Figure 12.
Figure 12. Hardcoded name of the report definition
You can update the code to pass the report name as a URL argument to the script:
- Create a new file called render_report.php in demo/public.
- Copy and paste the code in Listing 9 into the file and save it. This script
expects a report name to be passed as a query string parameter in the URL. The
report name is turned into a fully qualified file name and is then rendered the
same as it was in Listing 8.
Listing 9<?php // Check the report name has been set if (isset($_GET["name"]) == FALSE) { die("No report name passed to script..!"); } require_once 'startup.php'; $report_name = $_GET["name"].".rptdesign"; $engine = startup(); $root_directory = zget("/config/root"); $design_file = $root_directory."/samples/".$report_name; $design = $engine->openReportDesign($design_file); $task = $engine->createRunAndRenderTask($design); $task->validateParameters(); java_import("org.eclipse.birt.report.engine.api.HTMLRenderOption"); $generated_report_file = tempnam(sys_get_temp_dir(), 'report'); $options = new HTMLRenderOption(); $options->setOutputFileName($generated_report_file); $options->setOutputFormat("html"); $task->setRenderOption($options); $task->run(); $task->close(); echo file_get_contents($generated_report_file); unlink($generated_report_file); ?> - From a Web browser, navigate to http://locahost:8080/render_report.php?name=hello_world.
The same report should get rendered as shown in Figure 11, but this time the report name is passed as an argument to the script through the URL. The report name in the URL does not need the file extension (.rptdesign), as the script appends this to the fully qualified file name.
Remember that the code shown here is for example purposes only. This line of code, if used as is, contains a potential security issue:
$report_name = $_GET["name"].".rptdesign";
A browser could pass something like: ../../secrets/myreport to the script. A production-ready script should validate the report name before opening the file. For simplicity, this validation code has been omitted from the example.
Try using some of the other example reports provided with this article. This will require the Classic Models sample database. The reports are set up to attach to the MySQL database, running on the local machine, with a user name of root and an empty password. To change this information:
- Start the BIRT Report Designer (included in the BIRT all-in-one download), shown in Figure 13.
- Open the product_catalog.rptdesign report definition, which is in the
demo/samples directory.
Figure 13. BIRT Report Designer
- Open the list of data sources by expanding Data Sources.
- Right click on MySQL and select Edit (Figure 14).
Figure 14. Edit the MySQL Data Source
- Edit the data source details to suit your MySQL configuration, using Figure 15
as a guide.
Figure 15. Editing the data source connection information
- Click OK, then File => Save and close the BIRT Report Designer.
- From a Web browser, navigate to
http://localhost:8080/render_report.php?name=product_catalog. The result should
be similar to Figure 16.
Figure 16. Report rendering of the product catalog
- To try the Annual Sales By Product Line report by navigating to
http://localhost:8080/render_report.php?name=cross_tab.rptdesign. The result
should be similar to Figure 17.
Figure 17. Report rendering of the annual sales
The sales_invoice.rptdesign report definition highlights the use of report parameters. You will now create a PHP script that renders the report and configures report parameters, which are passed to the PHP script in the URL.
- Open the report definition in BIRT Report Designer.
- Expand Report Parameters, right-click on OrderNumber and select
Edit (Figure 18).
Figure 18. Edit the order number report parameter
- The OrderNumber report parameter has a default value of 10100 (Figure 19).
This means that unless you provide a different value, this report will always
render the sales invoice with an identifier of 10100. By way of example,
navigate to http://localhost:8080/render_report.php?name=sales_invoice and you
will see the default order number on the report (Figure 20).
Figure 19. Edit the order number parameter information
Figure 20: Report rendering of the sales invoice
- To update the code so you can pass the order number as a URL argument to the script, create a new file called sales_invoice.php in the demo/public directory.
- Copy and paste the code in Listing 10 into the file and save it. Updates to
the code appear in bold type.
Listing 10<?php // Check the order number has been set if (isset($_GET["order"]) == FALSE) { die("No order number passed to script..!"); } require_once 'startup.php'; $engine = startup(); $order_number = $_GET["order"]; // This user input needs filtering! $root_directory = zget("/config/root"); $report_engine = $root_directory."/ReportEngine"; $design_file = $root_directory."/samples/sales_invoice.rptdesign"; $images_directory = $root_directory."/public/images/logos"; java_import("org.eclipse.birt.report.engine.api.HTMLRenderOption"); java_import( "org.eclipse.birt.report.engine.api.HTMLServerImageHandler"); $design = $engine->openReportDesign($design_file); $task = $engine->createRunAndRenderTask($design); $task->setParameterValue("OrderNumber", (int) $order_number); $task->validateParameters(); $generated_report_file = tempnam(sys_get_temp_dir(), 'report'); $options = new HTMLRenderOption(); $options->setOutputFileName($generated_report_file); $options->setOutputFormat("html"); $options->setImageDirectory($images_directory); $options->setBaseImageURL("/images/logos/"); $options->setImageHandler(new HTMLServerImageHandler()); $task->setRenderOption($options); $task->run(); $task->close(); echo file_get_contents($generated_report_file); unlink($generated_report_file); ?> - Now, navigate to http://locahost:8080/sales_invoice.php?order=10272. The
result should look like Figure 21, which displays the new order number.
Figure 21. Report rendering of the updated sales invoice
For this last sample report, you will render a report using data generated in a PHP script. This is a powerful technique, as it enables a Project Zero application to generate data, perhaps by calling a RESTful Web service, and then pass the results to BIRT to render a report.
- Create a new file called data_source.php in demo/public.
- Copy and paste the code in Listing 11 into the file and save it. The magic in
this example is performed through the global context. The PHP script populates
an array containing the report data. It stores this in the global context under
the path /request/report.
Listing 11<?php require_once 'startup.php'; $engine = startup(); $root_directory = zget("/config/root"); $report_engine = $root_directory."/ReportEngine"; $design_file = $root_directory."/samples/data_source.rptdesign"; $images_directory = $root_directory."/public/images/logos"; java_import("org.eclipse.birt.report.engine.api.HTMLRenderOption"); java_import( "org.eclipse.birt.report.engine.api.HTMLServerImageHandler"); $design = $engine->openReportDesign($design_file); $task = $engine->createRunAndRenderTask($design); $task->validateParameters(); $generated_report_file = tempnam(sys_get_temp_dir(), 'report'); $models[] = array("ANG Resellers", "1952 Alpine Renault 1300", "Red"); $models[] = array("AV Stores, Co.", "1969 Harley Davidson Ultimate Chopper", "Blue"); $models[] = array("Alpha Cognac", "1969 Ford Falcon", "Blue"); $models[] = array("Asian Shopping Network, Co", "1969 Dodge Charger", "Plum Crazy Purple"); $models[] = array("Asian Treasures, Inc.", "1969 Corvair Monza", "Red"); zput("/request/report", $models); $options = new HTMLRenderOption(); $options->setOutputFileName($generated_report_file); $options->setOutputFormat("html"); $options->setImageDirectory($images_directory); $options->setBaseImageURL("/images/logos/"); $options->setImageHandler(new HTMLServerImageHandler()); $task->setRenderOption($options); $task->run(); $task->close(); echo file_get_contents($generated_report_file); unlink($generated_report_file); ?>
- Navigate to http://localhost:8080/data_source.php. The result should be
similar to Figure 22.
Figure 22. Report rendering of the scripted data source
- This example also requires some scripting in the BIRT report. Open the
data_source.rptdesign report definition in BIRT Report Designer (Figure 23). In
this example, the report has some embedded JavaScript to open, close and fetch
rows of data. The JavaScript gets the array from the global context and passes
each row back to BIRT. BIRT has an embedded JavaScript engine that enables
snippets of JavaScript to be run at different phases of a report rendering. The
report uses the open call to access the data previously stored in the global
context. The snippet uses the JavaScript Packages keyword to access a fully
qualified Java class name.
Figure 23. Edit the scripted data source report definition
By following the steps described in this article, you:
- Created and configured a Project Zero application to use BIRT.
- Rendered a report using the BIRT runtime in a PHP script.
- Modified the source code to efficiently load the BIRT runtime.
- Created a PHP script that rendered report names passed in the URL.
- Passed report parameters to BIRT through the Web page URL.
- Used PHP to create a scripted data source for a report.
You should now be able to apply this information so you can add flexible reporting capabilities your own Project Zero PHP applications.
| Description | Name | Size | Download method |
|---|---|---|---|
| Report samples | Reports.zip | 16 KB | HTTP |
Information about download methods
Learn
-
Project Zero
-
WebSphere
sMash documentation
- Podcast:
Impact of WebSphere sMash on Web 2.0 development
-
IBM
developerWorks Web developement zone
-
IBM developerWorks Ajax resource center
-
IBM
developerWorks WebSphere sMash resources
Get products and technologies
Discuss
Ant Phillips is a Software Developer in IBM's Java Technology Centre in Hursley, United Kingdom. His current focus is on WebSphere sMash, a simple environment for creating dynamic Web applications. Before joining IBM, Ant was the technical lead at an innovative startup based in Newbury, UK. In previous lives, Ant worked for Sony® and Microsoft® and thoroughly enjoyed visiting Tokyo, Seattle, and several places in between. In his spare time, he plays as much sport as his wife and two children let him get away with.
Comments (Undergoing maintenance)





