Thursday, April 17, 2008

Testing and Debug of Reports

At EclipseCon this year I did a two hours tutorial on BIRT integration. I think it went fairly well. I went a little long, but covered all the best bits. I would like to do a series of posts on this site that go over some of the same material.

The stuff I really want to talk about is a technique that I have developed for debugging / stepping through your Java Event Handler classes. My method requires a fair amount of setup, but in the end it makes it far easier to step through your event handlers. In version 2.3 of the product, this process is simplified. That said many people are on version 2.2 and will continue to be on version 2.2 for a while.

So if you intend to do any Java event handler development in BIRT 2.2. this is the post for you.

Background

The first thing that we need to do is to understand the report test framework that I use on BIRT projects. This is a really simple format that allows me to batch run a large number of pre-defined BIRT reports. I am not going to dive into the details of how all of this works, but you will have all of the source code, and it is relatively simple to figure out.

All of the code for these examples is stored on my subversion server at:
http://longlake.minnovent.com/repos/birt_example

You will find a lot of projects there, they are all available under EPL. The ones that you will need to follow these examples are:
birt_api_example
birt_runtime_lib_222
script.lib

First a couple of words about the projects. If you take a look at these projects you will notice that they are plug-in projects (look under builders). I use the plug-in mechanism to associate the projects together. You do not need to do this, but I have found that it allows people to download them into their workspace and they will just run.

The birt_api_example project contains the majority of the code that you will work with. The REAPI package contains code showing how to run reports. The DEAPI package contains code showing how to create report designs through code. The Reports folder contains report designs that are used by both the REAPI and DEAPI. In addition, Reports contains reports that use the DEAPI.

The birt_runtime_lib_222 project contains the BIRT runtime libraries. It also acts as BIRT_HOME for both the REAPI and DEAPI examples. The runtime lib takes all of the libs and plug-ins from the and exposes them as a plug-in project. I will return to the what exactly the runtime_lib does in a later post. First, I want to get to the fun stuff.

The final project is the script.lib project which contains java event handler code that you want to run or debug. If all goes well, I will tie all of this back to that project at the end of this post.

So lets start with the birt_api_example project. In large BIRT report development projects, you often have multiple reports that need to be tested under a variety of conditions. Typically, we build a relatively small number of rptdesigns that can create a large number of rptdocuments based on the parameters used when the report runs.

This technique is great since it dramatically reduces the amount of code that needs to be maintained. The problem is that one rptdesign may have a large number of test conditions that need to be run. Manually running each condition by entering the required parameters is going to be difficult at best.

What we wanted was a relatively simple solution where we could store report run configurations and then run them in batch. This is the role of the tester package. There is a single java class TestAllReports.java which can be run as a java application.

When you run the TestAllReports project the program will look in the tester_run folder for any .properties files. For each properties file the tester will try to run according to the configuration information in the file. The format of the properties file is very simple.

file_name=customers.rptdesign (relative path from ./Reports directory)
param1_name=param1_Value
param2_name=param1_Value

Currently I have added support for Strings and java.sql.Date parameter data types. It is trivial to modify to support Integers or Booleans if you require these data types.

Once the tester has opened your properties file it will run the report design using Run then Render tasks to build PDF files. You can modify the code in the testReport method to enable output to HTML, Excel, or Word formats (the code is there but commented out). Reports are run into a new folder that will have the format:

tester_output_20080417_133034

Using the tester you can batch test large number of reports. The tester does not have a way to automate the comparison of rendered files, but there are a number of tools that can be used if your processes require this level of automation.

Debugging Java Event Handlers

Now that we have the tester running, it is time to go ahead and look at stepping through event handler code. The 'official' technique for debugging BIRT event handlers is to create a runtime workspace that runs your reports as defined here. This process is relatively straight-forward, but there is one problem, MemoryWoes. I have a two gig laptop and running MaxPermSize I still run into memory issues when running the Runtime workbench.

What I wanted was a method where I could run my reports through the debugger, without having to load the runtime workbench. Well it turns out that I can use my tester program to do just that.

The starting place for this is the script.lib project. This project is where I am going to place all of my event handlers. I then have an ant build file that I can use to compile my event handlers into a jar file. Back in the tester project, when it loads up the EngineConfig, I add that jar file:

    EngineConfig config = new EngineConfig();
config.setProperty(EngineConstants.WEBAPP_CLASSPATH_KEY, getScriptLibFileNames());

...

/*
* The engine needs to see a list of each jar file concatenated as a string
* using the standard file system separator to divide the files
*/
private static String getScriptLibFileNames() {
File scriptHome = new File(SCRIPT_LIB);

if (!scriptHome.exists()) {
scriptHome.mkdir();
}

File[] dirFile = scriptHome.listFiles(new JarFilter());
StringBuffer scriptlibClassPath = new StringBuffer(); //$NON-NLS-1$
for (int i = 0; i < dirFile.length; i++) {
if (scriptlibClassPath.length() > 0) {
scriptlibClassPath.append(File.pathSeparatorChar);
}
scriptlibClassPath.append(dirFile[i].getAbsolutePath());
}

return scriptlibClassPath.toString();

}


Now, I know what you are thinking. Why did you have to add the file as a jar file, why not just add the folder to the classpath. The short answer is that I don't know how to add folder structures to the classpath, and I didn't bother to learn. This is not due to laziness, my thought is that when I deploy to my application, I am going to deploy the jar file. By using the jar when I run the tester, I have a better test environment.

Testing It Out

Now that you have an explanation of what we are doing, lets test it out. If you go back to the birt_api_example project and look in the tester_save folder you will find a dyn_table.properties file. Copy this file to the tester_run folder. Examining the file you will see that I am running the report found in:

Reports/EventHandlerReports/a_dyn_table_columns.rptdesign

Opening this design directly and run it and you will see that a three column table generates a report with eight columns. How does that happen? If you look on the properties for the table, you will see that I am using an event handler named TableMagic, which is in the script.lib project. (I have pictures coming, but blogger is having issues with images right now)

Now put a breakpoint in the TableMagic.java class. (try using from the Java perspective). If you have copied the dyn_table.properties file to the tester_run folder, and you execute the tester in debug mode, it will take you directly to the break point.

How cool is that? You are now debugging your java classes without launching a runtime workspace. The best part is using this type of debugging, I can walk you through the DEAPI calls that I have made to dynamically modify my report based on data from the database.

Now I feel like Howie Mandel on Deal or No Deal. Just when I get to the good stuff, I am going to have to take a break. Next week, I will go through the TableMagic event handler to discuss how you can use the DEAPI to create data driven report designs.


5 comments:

Peter said...

I really like the post, too bad the svn server is not running....

Scott Rosenbaum said...

The server seems to be live for me. My company recently changed our DNS hosting. Could try connecting to:

http://64.122.237.54/repos/birt_example

?

Anonymous said...

I also can not get access to this server. If we are supposed to access via svn can you give the exact svn command with login/password. I'm very interested in this testing example

Gustavo Peiretti said...

Gracias. Me fue util..
Thank you. I was useful..

Anonymous said...

\m/