Sunday, December 18, 2005

TPTP and BIRT

The Eclipse Test & Performance Tools Platform (TPTP) Project team has produced a very nice example of using BIRT to create reports based on profile information. You can see it here.

The report uses the new Chart Builder that was introduced in M3 as well as the new XML Data Source.

Wednesday, December 07, 2005

Eclipse BIRT 2.0 Milestone 3 Released

The Eclipse Business Intelligence and Reporting Tools (BIRT) team has released Milestone 3 of BIRT 2.0. This milestone showcases some of the more lofty features of the 2.0 release, including library and template support, a new charting wizard, chart SVG output, a new XML data source and improved PDF and Report generation performance.

In addition to the features showcased in M3, progress on report paging, event debugging and scripting properties have made real progress. I have been building some examples to illustrate scripting and debugging and it is very cool.

The BIRT team is very interested in getting your feedback and suggestions on improvements.

To read about the feature set in BIRT 2.0 M3 take a look at and let us know what you think.
Notable BIRT 2.0 Milestone 3

Friday, December 02, 2005

Using a supplied connection with BIRT

In an earlier post, I said that I would try to use the new setAppContext feature within BIRT to implement connection pooling. Well in truth, I didn’t implement connection pooling but instead built an ODA that uses a supplied connection in place of the one BIRT would normally establish on its own. To understand where this would be useful, imagine that you have a J2EE application built that already has a connection pool set up. Ideally when your application calls a BIRT report, it would be nice if BIRT would use a connection that is already created. This is where the setAppContext method is very helpful.

A snippet of ReportRunner.java, which is part of the engine API and shows how to execute a report using the API, is shown below.


HTMLRenderContext renderContext = new HTMLRenderContext();

renderContext.setImageDirectory("image"); //$NON-NLS-1$



HashMap contextMap = new HashMap();

contextMap.put( HTMLRenderContext.CONTEXT_NAME, renderContext );



//Get a connection from the pool
testConn = setupJdbcConnection();
contextMap.put("org.eclipse.birt.report.data.oda.subjdbc.SubOdaJdbcDriver", testConn);

task.setContext( contextMap );


I have added two lines to this code, which are bolded. The first retrieves a java.sql.Connection object. In my example I created this connection directly. In other applications this would be returned from a connection pool call. The next line adds this Connection object to the context map. As the key for this map entry I am using the name of the intended recipient plug-in.

Similar lines of code would be added to an application that is using the engine APIs to call BIRT. Also note that task.setContext will change to task.setAppContext( Map) in BIRT 2.0 M3 and later.

Now my connection is within BIRT. The next step is to configure BIRT to use it. By default the setAppContext feature was created to allow application contextual information to be passed to the data layer. This normally would require an ODA to be built to use it. However there are other ways to get at and use this context. One way is to extend an existing ODA. The ideal choice is the JDBC ODA.

The JDBC ODA uses the OdaJdbcDriver class as the entry point to the ODA. So you may want to create a new Plug-in that extends this class.


public class SubOdaJdbcDriver extends OdaJdbcDriver

private java.sql.Connection passedInConnection;


public void setAppContext( Object context ) throws OdaException
{
HashMap ctx = (HashMap)context;
passedInConnection = (java.sql.Connection)ctx.get ("org.eclipse.birt.report.data.oda.subjdbc.SubOdaJdbcDriver");

}

public IConnection getConnection(String connectionClassName) throws OdaException
{
if( passedInConnection != null){
return new appContextDBConnection();
}else{
return new org.eclipse.birt.report.data.oda.jdbc.Connection();
}
}
.
.
.

The only methods we are concerned about are the setAppContext and getConnection methods.

As you can see the setAppContext method just retrieves the Connection set earlier in the caller code and stores it.

The getConnection method checks to see if the passedInConnection is not null. If it is null we want the BIRT framework to make the connection using the existing JDBC Connection class. This allows the report designer to continue to use standard BIRT connections at design time and while in run time it retrieves the connection from the calling applicaton.

The appContextDBConnection class is just an extended version of the JDBC ODA Connection class. This can be added as an inner class to the SubOdaJdbcDriver class.

private class appContextDBConnection extends org.eclipse.birt.report.data.oda.jdbc.Connection


In this class the only thing we are really interested in is overriding the open and close methods.


public void open(Properties connProperties) throws OdaException
{
jdbcConn = passedInConnection;

}

public void close( ) throws OdaException
{
if ( jdbcConn == null )
{
return;
}
jdbcConn.close();
jdbcConn = null;
}


The jdbcConn variable holds the java.sql.Connection object that is used to make the queries. So in the open method we just set that variable equal to the one that is passed in. The close method just nulls the connection, although ideally it would return the connection to the pool.

There is only one issue. The variable jdbcConn in the existing JDBC ODA is private. You have two choices, make a local version of jdbcConn and override all the methods that use this variable or change the BIRT source for the JDBC ODA to make jdbcConn protected. Post BIRT M3 this variable will be made protected.

If you choose not to change the BIRT source the following methods will have to be added to the appContextDBConnection class in addition to the open and close methods.

commit
getNaxQueries
getMetaData
isOpen
newQuery
rollback

The code for these methods should be exact copies of the methods in org.eclipse.birt.report.data.oda.jdbc.Connection class.

Remember to add the local copy of jdbcConn to the appContextDBConnection class.


private java.sql.Connection jdbcConn = null;


For this new plug-in copy the plugin.xml from the JDBC plugin and modify the driverClass line to point to the new SubOdaJdbcDriver class.



<datasource id="org.eclipse.birt.report.data.oda.subjdbc" odaversion="3.0" driverclass="org.eclipse.birt.report.data.oda.subjdbc.SubOdaJdbcDriver" defaultdisplayname="Example Context JDBC Data Source" setthreadcontextclassloader="false">



Also change the name, change the library tag and add the JDBC plug-in to the requires tag.


<plugin class="org.eclipse.birt.report.data.oda.subjdbc.plugin.SubjdbcPlugin" id="org.eclipse.birt.report.data.oda.subjdbc" name="Subjdbc Plug-in" version="1.0.0">

<runtime>
<library name="subjdbc.jar">
<export name="*">
</library>
</runtime>

<requires>
.
.
<import plugin="org.eclipse.birt.report.data.oda.jdbc"/>
</requires>




You will need to create a plug-in for the ui as well. This plug-in requires no code, just the plugin.xml

Copy the plugin.xml for the JDBC ui plug-in and change the odaDataSourceUI tag to point to the subjdbc plug-in.


<extension point="org.eclipse.birt.report.designer.ui.odadatasource">
<odadatasourceui id="org.eclipse.birt.report.data.oda.subjdbc">


Change the name of this plug-in as well. This plug-in exports no jar file and only need the JDBC ui plug-in to work.


<plugin class="" id="org.eclipse.birt.report.data.oda.subjdbc.ui" name="Ui Plug-in" version="1.0.0">

<requires>
<import plugin="org.eclipse.birt.report.data.oda.jdbc.ui"/>
</requires>


Export the two plug-ins and add them to your designer. Finally, add the subjdbc plug-in to the viewer plug-in and you are ready to develop reports that leverage supplied connections.