Chapter 20. Developing an ODA Extension

BIRT uses the Eclipse Data Tools Platform (DTP) open data access (ODA) API to build a driver that connects to a data source and retrieves data for a report. This API defines interfaces and classes that manage the following tasks:

• Connecting to a data source

• Preparing and executing a query

• Handling data and metadata in a result set

• Mapping between the object representation of data and the data source

Eclipse DTP also provides tools and support for SQL development, locales, logging, and other special types of processing. For more information about the Eclipse DTP project, see http://www.eclipse.org/datatools.

The ODA framework is a key component of the DTP. ODA presents the Java developer with a robust architecture to extend the capabilities of BIRT by being able to report on custom data sources. The framework provides new project wizards to create plug-in projects for ODA run-time and designer extensions. The generated plug-in projects include class templates and default implementation. These plug-in projects support expediting the development of customized ODA data source extensions.

This chapter shows how to develop an ODA extension using examples that extend the org.eclipse.datatools.connectivity.oda.dataSource extension point to provide access to the following data sources:

• CSV file

Uses the new DTP ODA wizards to create a plug-in project that accesses a CSV data source. DTP ODA interfaces are similar to JDBC interfaces with extensions that support retrieving data from relational and non-relational database sources.

• Relational database

Uses Hibernate Core for Java, an object-oriented software system for generating SQL and handling JDBC result sets. Hibernate Query Language (HQL) provides a SQL-transparent extension that makes the DTP ODA extension portable to all relational databases. Hibernate also supports developing a query in the native SQL dialect of a database.

Hibernate is free, open-source software licensed under the GNU Lesser General Public License (LGPL). For more information about Hibernate, see http://www.hibernate.org/.

Understanding an ODA extension

A BIRT report design specifies the type of data access and data transformations required to generate a report. All data comes from an external data source. The BIRT data engine supports the DTP ODA framework. The DTP ODA framework provides access to standard and custom data sources using an open API.

Using the DTP ODA framework makes it possible to create a plug-in driver to any external data source. BIRT uses DTP ODA extension points for the report designer and report generation environments.

A DTP ODA extension adds a new data source driver to the BIRT framework by implementing the following extension points:

• ODA data source

org.eclipse.datatools.connectivity.oda.dataSource supports the extension of BIRT design-time and run-time data source access. The XML schema file, org.eclipse.datatools.connectivity.oda/schema/dataSource.exsd, describes this extension point.

• ODA user interface

org.eclipse.datatools.connectivity.oda.design.ui.dataSource supports optionally adding an integrated user interface for an ODA driver to BIRT Report Designer. The plug-in can provide user interface support used by a report designer to specify the data source and edit the data set. The XML schema file, org.eclipse.datatools.connectivity.oda.design.ui/schema/dataSource.exsd, describes this extension point.

• ODA connection profile

org.eclipse.datatools.connectivity.oda.connectionProfile supports optionally adding different types of connection profiles to an ODA driver user interface for BIRT Report Designer. A connection profile can define a category or set of configuration types such as JDBC connection profiles. This user interface defines a corresponding newWizard element used to create the resource. The XML schema file, org.eclipse.datatools.connectivity/schema/connectionProfile.exsd, describes this extension point.

• ODA connection properties page

org.eclipse.ui.propertyPages supports optionally adding a page used to edit the properties of a connection profile. The XML schema file, org.eclipse.ui.propertyPages/schema/propertyPages.exsd, describes this extension point.

For more information about the DTP ODA APIs, see the Javadoc for the org.eclipse.datatools.connectivity.oda package hierarchy. The Javadoc is in the DTP Software Development Kit (SDK) available from the Eclipse Data Tools Platform project at http://www.eclipse.org/datatools.

Developing the CSV ODA driver extensions

Eclipse DTP provides two ODA plug-in template wizards, one for ODA data source runtime driver, another for ODA data source designer. Each wizard creates a new plug-in project, generating implementations of related ODA extension points. The auto-generated Java classes implement method stubs that support a single result set and input parameters. The classes have hard-coded result set data so that an ODA extension developer can immediately verify that the generated ODA driver plug-ins work with an ODA consumer, for example, BIRT.

After the ODA wizard generates the plug-ins, modify the TODO tags in the generated source code to customize data source behavior.

To develop the CSV ODA extensions, perform the following tasks:

• Download the required BIRT source code from the Eclipse CVS repository.

• Create two new projects using the ODA wizards in the Eclipse PDE to implement the following plug-ins:

• CSV ODA driver to access the data source

• CSV ODA user interface to select the data file and available data columns in BIRT Report Designer

• Extend the source code in the CSV ODA plug-in projects by adding new functionality at the defined extension points.

• Test and deploy the extensions in the run-time environment.

You can download the source code for the CSV ODA driver extension examples at http://www.actuate.com/birt/contributions.

About the CSV ODA plug-ins

The CSV ODA extensions require the following two plug-ins:

• org.eclipse.birt.report.data.oda.csv

The CSV ODA data source plug-in extends the functionality defined by the extension point, org.eclipse.datatools.connectivity.oda.dataSource, to create the CSV ODA driver. The first row of the CSV input file contains the column names. The remaining rows, separated by new line markers, contain the data fields, separated by commas. The org.eclipse.birt.report.data.oda.csv plug-in contains the database classes and data structures, such as data types, result set, metadata result set, and query used to handle data in a BIRT report.

The org.eclipse.datatools.connectivity.oda.dataSource extension point is in the Eclipse DTP project and is part of the org.eclipse.datatools.connectivity.oda plug-in. This plug-in is available from the CVS repository in /home/datatools.

• org.eclipse.birt.report.data.csv.ui

The CSV ODA user interface plug-in extends the functionality defined by the org.eclipse.datatools.connectivity.connectionProfile, org.eclipse.ui.propertyPages, and org.eclipse.datatools.connectivity.oda.design.ui.dataSource extension points. The user interface consists of the following two pages:

• The data source page, which specifies and validates the path and name of the CSV file.

• The data set page, which shows the selected data file and columns available in the file. By default, the user interface selects all the columns in the data set.

Downloading BIRT source code from the CVS repository

The CSV ODA driver plug-in, org.eclipse.birt.report.data.oda.csv, requires the following plug-ins:

• org.eclipse.core.runtime

• org.eclipse.datatools.connectivity.oda

• org.eclipse.datatools.connectivity.oda.profile

The CSV ODA user interface extension, org.eclipse.birt.report.data.csv.ui, requires the following plug-ins:

• org.eclipse.birt.report.data.oda.csv

• org.eclipse.core.runtime

• org.eclipse.datatools.connectivity.oda.design.ui

• org.eclipse.ui

For the org.eclipse.birt.report.data.oda.csv plug-in, extend only the Java classes in the org.eclipse.datatools.connectivity.oda plug-in. For the org.eclipse.birt.report.data.csv.ui plug-in, extend the Java classes in the org.eclipse.datatools.connectivity.oda.design.ui plug-in.

Eclipse makes source code available to the developer community in the CVS repository. Compiling does not require the source code for the plug-ins. You can configure the system to use the JAR files in the eclipseplugins folder. To debug, you may need the source code for all the required BIRT and DTP plug-ins.

Implementing the CSV ODA driver plug-in

This section describes how to implement an ODA driver plug-in, using the CSV ODA driver plug-in as an example. To create an ODA driver plug-in, perform the following tasks:

• Create the ODA driver plug-in project.

• Define the dependencies.

• Specify the run-time archive.

• Declare the ODA extension points.

You can create the CSV ODA driver plug-in project, org.eclipse.birt.report.data.oda.csv, in the Eclipse PDE. This section describes how to create the plug-in project using the New Plug-in Project wizard.

How to create the CSV ODA driver plug-in project

1 From the Eclipse PDE menu, choose File→New→Project.

2 In New Project—Select a wizard, open Business Intelligence and Reporting Tools, and select ODA Runtime Driver Plug-in Project as shown in Figure 20-1. Choose Next. New Plug-in Project appears.

Figure 20-1 Specifying the CSV ODA Runtime Driver Plug-in Project

image

3 In Plug-in Project, modify the settings as shown in Table 20-1.

Table 20-1 Settings for CVS ODA Plug-in Project options

image

Choose Next. Plug-in Content appears.

4 In Plug-in Content, modify the settings as shown in Table 20-2.

Table 20-2 Settings for CSV ODA Plug-in Content options

image

Choose Next. Templates appears.

5 In Templates, choose ODA Data Source Runtime Driver, as shown in Figure 20-2.

Figure 20-2 Specifying the CSV ODA Data Source Runtime Driver template

image

Choose Next. ODA Data Source Runtime Driver appears.

6 In ODA Data Source Runtime Driver, specify values for the following options used to generate the ODA plug-in, as shown in Table 20-3.

Table 20-3 Settings for CSV ODA Data Source Runtime Driver options

image

Choose Finish. The CSV ODA driver plug-in project appears in the Eclipse PDE workbench. The project created by the wizard appears as shown in Figure 20-3. The wizard creates all the plug-in files and the main functional Java classes. Customize the default settings as needed and add code to the Java class stubs to implement the desired functionality.

Figure 20-3 Viewing project package structure

image

7 Choose plugin.xml in Package Explorer and double-click to open PDE Manifest Editor, as shown in Figure 20-4. Using PDE Manifest Editor, you can review and edit all the plug-in settings.

Figure 20-4 PDE Manifest Editor—CSV ODA Overview

image

Understanding the ODA data source extension points

In this step you review the extension points added by the wizard. Click the Extensions tab in PDE Manifest Editor to open the Extensions pane in the editor. The PDE Manifest Editor automatically adds the following two extension points:

• org.eclipse.datatools.connectivity.oda.dataSource

• org.eclipse.datatools.connectivity.oda.connectionProfile

Understanding dataSource extension point properties

The ODA data source extension point supports extending design-time and run-time data source access for an application. The extension must implement the ODA Java run-time interfaces defined in the org.eclipse.datatools.connectivity.oda plug-in. Figure 20-5 shows the ODA data source extension points used in the CSV ODA plug-in example.

Figure 20-5 PDE Manifest Editor—CSV ODA Extensions

image

The extension point, org.eclipse.datatools.connectivity.oda.dataSource, specifies the following properties that identify the extension in the run-time environment:

• ID

Optional identifier of the extension instance.

The wizard added a reference, %oda.data.source.id, to the extension point ID that you specified in the wizard. The plugin.properties file provides definitions of all the localized variables. The value for the ID, shown in Listing 20-1, is as follows:

org.eclipse.birt.report.data.oda.csv

Listing 20-1 plugin.properties


############################################################
# Copyright (c) 2009 <<Your Company Name here>>
#
############################################################
# Plug-in Configuration
#
oda.data.source.id=org.eclipse.birt.report.data.oda.csv
#
############################################################
# NLS String
############################################################
#
plugin.name=CSV ODA Runtime Driver
data.source.name=CSV ODA Data Source
data.set.name=CSV ODA Data Set

• Name

Optional name of the extension instance. Fully qualified identifier of the extension.

The extension point defines the extension elements and extension element details for the CSV ODA driver.

The dataSource extension element defines the ODA data source extension type to use at design time and run time. It contains the following properties:

• id

Fully qualified identifier of an ODA data source extension. The wizard references the externalized id with the notation %oda.data.source.id.

• driverClass

Java class that implements the org.eclipse.datatools.connectivity.oda.IDriver interface. This interface provides the entry point for the ODA run-time driver extension.

• odaVersion

Version of the ODA interfaces. Specify version 3.1 for an ODA driver developed for BIRT release 2.6.

• defaultDisplayName

Display name of the ODA data source extension. To externalize the value, use the plugin.properties mechanism.

• setThreadContextClassLoader

Indicates whether the consumer of the ODA run-time extension plug-in must set the thread context class loader before calling an ODA interface method.

The OSGi class loader that loads the ODA run-time plug-in is not designed to load additional classes. To load additional classes, an ODA run-time plug-in must provide its own java.net.URLClassLoader object and switch the thread context class loader as required.

The dataSource element also specifies a property, containing the following extension element details:

• name

Unique name of a property group. Type HOME for a property name.

• defaultDisplayName

Default display name of a property group. To localize the value, use the plugin.properties mechanism.

For the default display name, type “CSV File Full Path”.

• type

Data type of the property. The default is String.

• canInherit

Flag indicating whether the property extension element can inherit properties. Select true.

• defaultValue

Default value of the property extension element.

• isEncryptable

Flag indicating whether the property is encrypted. Select false.

• allowsEmptyValueAsNull

Flag indicating whether an empty value of this property can be treated as a null value. The default value is true.

The dataSet extension element describes the following properties:

• id

Required identifier of the ODA data set extension.

• defaultDisplayName

Display name of the ODA data set extension.

To localize the value, use the plugin.properties file. The default display name is CSV ODA Data Set, as you can see from Listing 20-1.

The dataSet element also specifies a complex data type, dataTypeMapping, which defines a sequence of data type mappings containing the following properties:

• nativeDataTypeCode

Integer value that must match one of the data type codes returned in the implementation for the ODA driver interface.

• nativeDataType

String value specifying the data source native data type.

• odaScalarDataType

ODA scalar data type that maps to the native type. Supported ODA data types include Date, Double, Integer, String, Time, Timestamp, Decimal, and Boolean. The default supported types set by the wizard are shown in Table 20-4.

Table 20-4 Settings for CSV ODA dataTypeMapping elements

image

Understanding ConnectionProfile properties

This extension point supports creating database connections using connections profiles. The CSV ODA plug-in uses the following settings created by the plug-in wizard:

• CSV ODA Data Source (category)

• CSV ODA Data Source Connection Profile (connectionProfile)

• ODA Connection Factory (connectionFactory)

Understanding the dependencies for the CSV ODA driver extension

In Figure 20-6, the Dependencies page shows a list of plug-ins that must be available on the classpath of the CSV ODA driver extension to compile and run.

Figure 20-6 The CSV ODA Dependencies page

image

The ODA Runtime driver wizard adds the following dependencies to your plug-in:

• org.eclipse.datatools.connectivity.oda

• org.eclipse.datatools.connectivity.oda.profile

You can run Organize Manifest wizard at the end of your work to optimize the dependencies settings. Figure 20-7 shows an example of what settings to use when running the wizard.

Figure 20-7 Organize Manifests Wizard

image

The link to this wizard is on the Overview page of the Manifest Editor. The next wizard page displays the changes in the plug-in manifest file. You can review the proposed changes and accept or decline the modifications.

Understanding the sample CSV ODA driver extension

BIRT Data Engine supports the Eclipse DTP ODA framework. The DTP ODA framework supports creating an extension that can plug any external data source into BIRT Report Engine.

The DTP ODA API specifies the interfaces for a run-time driver. BIRT Data Engine uses the data source and data set definitions in a report design to access the ODA run-time driver to execute a query and retrieve data.

The DTP ODA interfaces are similar to JDBC interfaces having extensions that support retrieving data from non-RDBMS sources. An extended ODA driver can implement these interfaces to wrap the API for another data source, such as a CSV file, to retrieve a result set containing data rows.

The CSV ODA driver extension described in this chapter is a simplified example that illustrates how to create an ODA plug-in using the Eclipse PDE. The package for the CSV ODA extension example, org.eclipse.birt.report.data.oda.csv, uses the following classes to implement the ODA plug-in interfaces:

• Driver

Implements the IDriver interface. Instantiates the connection object for the CSV ODA driver and sets up the log configuration and application context.

• Connection

Implements the IConnection interface. Opens and closes the connection to the CSV file and instantiates the IQuery object.

• Query

Implements the IQuery interface. Handles the processing that performs the following operations:

• Sets up the java.io.File object, containing the file and path names

• Fetches the data rows from the data file, using the internal class, CSVBufferReader

• Trims the column data, removing extraneous characters such as commas and quotes

• Prepares the result set metadata, containing the table and column names

• ResultSet

Implements the IResultSet interface. Handles the processing that transforms the String value for a column to the specified data type.

• ResultSetMetaData

Implements the IResultSetMetaData interface. Describes the metadata for each column in the result set.

• DataSetMetaData

Implements the IDataSetMetaData interface. Describes the features and capabilities of the data set.

• Messages

Defines the exception messages for the CSV ODA driver.

• CommonConstant

Defines the constants used in the package, such as the driver name, ODA version, query keywords, and delimiters.

Understanding Driver

The Driver class instantiates the connection object for the CSV ODA driver by calling the getConnection( ) method as shown in Listing 20-2.

Listing 20-2 The getConnection( ) method


public IConnection getConnection( String dataSourceType )
      throws OdaException
{
   return new Connection( );
}

Understanding Connection

The Connection class opens and closes the connection to the CSV file and calls the newQuery( ) method to instantiate the Query object. Listing 20-3 shows the newQuery( ) method.

Listing 20-3 The newQuery( ) method


public IQuery newQuery( String dataSetType )
      throws OdaException
{
   if( !isOpen( ) )
      throw new OdaException(
         Messages.getString(
            "common_CONNECTION_HAS_NOT_OPENED" ) );
   return new Query( this.homeDir, this);
}

Understanding Query

The Query class constructor sets up a java.io.File object, containing the file and path names. The constructor supports an application submitting the home directory parameter, homeDir, as a file name as well as a path. Query( ) configures the data source property based on the value of the HOME property specified in the report design, as shown in Listing 20-4.

Listing 20-4 The Query class constructor


Query ( String homeDir, IConnection host )
   throws OdaException
{
   if ( homeDir == null || host == null )
      throw new OdaException(Messages.getString
         ("Common.ARGUMENT_CANNOT_BE_NULL"));
   File file = new File(homeDir);
   if (file.isDirectory( )
      this.homeDirectory = homeDir;
   else if (file.isFile( )
      this.homeDirectory = file.getParent( );
   this.connection = host;
}

The Query class prepares and executes a query, then retrieves the data. Query implements the following additional methods:

• prepare( ) performs the following operations:

• Generates query and column information by calling splitQueryText( )

• Validates the connection by calling validateOpenConnection( )

• Formats the query String, eliminating redundant spaces and converting all keywords to uppercase, by calling formatQueryText( )

• Validates the query by calling validateQueryText( )

• Prepares the metadata required for the execution of the query and retrieval of the query results by calling prepareMetaData( )

Listing 20-5 shows the prepare( ) method.

Listing 20-5 The prepare( ) method


public void prepare( String queryText )
   throws OdaException
{
   if ( queryText != null )
   {
      String query = splitQueryText(queryText)[0] ;
      String colInfo = splitQueryText( queryText )[1];
      validateOpenConnection( );
      String formattedQuery = formatQueryText( query );
      validateQueryText( formattedQuery );
      prepareMetaData( formattedQuery, colInfo );
   }
   else
      throw new OdaException( Messages.getString(
         "common_NULL_QUERY_TEXT" ) );
}

• prepareMetaData( ) acquires the following metadata:

• Table name

• Actual column names read from data file

• Query column names

• Query data types

prepareMetaData( ) then instantiates and configures the ResultSetMetaData object. Listing 20-6 shows the prepareMetaData( ) method.

Listing 20-6 The prepareMetaData( ) method


private void prepareMetaData( String query,
      String savedSelectedColInfo )
   throws OdaException
{
   String[ ] queryFragments =
      parsePreparedQueryText( query );
   String tableName = queryFragments[2];
   String[ ] allColumnNames =
      discoverActualColumnMetaData( tableName,NAME_LITERAL );
   String[ ] allColumnTypes =
      createTempColumnTypes( allColumnNames.length );
   String[ ] queryColumnNames = null;
   String[ ] queryColumnTypes = null;
   String[ ] queryColumnLables = null;

   queryColumnNames = allColumnNames;
   queryColumnTypes = allColumnTypes;
   queryColumnLabels = allColumnNames;

   this.resultSetMetaData =
      new ResultSetMetaData( queryColumnNames,
         queryColumnTypes, queryColumnLabels );
   this.currentTableName = tableName;
}

• executeQuery( ) performs the following operations:

• Fetches the data from the file to a Vector object

• Transfers the data from the Vector to a two-dimensional String array

• Returns the data rows and metadata in a single ResultSet object

Listing 20-7 shows the executeQuery( ) method.

Listing 20-7 The executeQuery( ) method


public IResultSet executeQuery( ) throws OdaException
{
   Vector v = fetchQueriedDataFromFileToVector( );
   String[ ][ ] rowSet =
      copyDataFromVectorToTwoDimensionArray( v );
return new ResultSet( rowSet, this.resultSetMetaData );
}

• The internal class, CSVBufferReader, fetches the data rows from the data file. Listing 20-8 shows the readLine( ) method.

Listing 20-8 The readLine( ) method


public String readLine( ) throws IOException
{
   if ( isLastCharBuff( ) && needRefillCharBuff( ) )
      return null;
   if ( needRefillCharBuff( ) )
   {
      charBuffer = newACharBuff( );
      int close = reader.read( charBuffer );
      if ( close == -1 )
         return null;
      if ( close != CHARBUFFSIZE )
         this.eofInPosition = close;
      this.startingPosition = 0;
   }

   String candidate = "";
   int stopIn = CHARBUFFSIZE;
   if ( isLastCharBuff( ) )
   {
      stopIn = this.eofInPosition;
   }

   for ( int i = this.startingPosition; i < stopIn; i++ )
   {
      if ( this.charBuffer[i] == ' ' )
      {
         return readALine( candidate, stopIn, i );
      }
   }

   if ( isLastCharBuff( ) )
   {
      return readLastLine( candidate );
   }
   return readExtraContentOfALine( candidate );
}

Understanding ResultSet

The ResultSet class performs the following operations:

• Provides the cursor processing that fetches forward into the buffered result set rows

• Transforms the String value for a column to the specified data type

ResultSet implements the following methods:

• ResultSet( ), the constructor, sets up a two-dimensional array that contains the table data and metadata, as shown in Listing 20-9.

Listing 20-9 The ResultSet( ) constructor


ResultSet( String[ ][ ] sData, IResultSetMetaData rsmd )
{
   this.sourceData = sData;
   this.resultSetMetaData = rsmd;
}

• getRow( ) returns the cursor, indicating the position of the row in the result set, as shown in Listing 20-10.

Listing 20-10 The getRow( ) method


public int getRow( ) throws OdaException
{
   validateCursorState( );
   return this.cursor;
}

• next( ) increments the cursor to point to the next row, as shown in Listing 20-11.

Listing 20-11 The next( ) method


public boolean next( ) throws OdaException
{
   if ( (this.maxRows <= 0? false:cursor >=
      this.maxRows - 1) || cursor >=
      this.sourceData.length - 1 )
   {
      cursor = CURSOR_INITIAL_VALUE;
      return false;
   }
   cursor++;
   return true;
}

getString( ) returns the value for a column in the row at the column position specified in the result set, as shown in Listing 20-12.

Listing 20-12 The getString( ) method


public String getString( int index )
   throws OdaException
{

validateCursorState( );
String result = sourceData[cursor][index - 1];
if( result.length( ) == 0 )
      result = null;
   this.wasNull = result == null ? true : false;
   return result;
}

Understanding ResultSetMetaData

The ResultSetMetaData class describes the metadata for a column in the result set, including the following information:

• Column count in the result set

• Display length

• Label

• Name

• Data type

• Precision

• Scale

• Permits null

getColumnName( ) returns the column name for a column at the row, column position specified in the result set, as shown in Listing 20-13.

Listing 20-13 The getColumnName( ) method


public String getColumnName( int index ) throws OdaException
{
   if ( index > getColumnCount( ) || index < 1 )
      throw new OdaException( Messages.getString(
         "resultSetMetaData_INVALID_COLUMN_INDEX" ) + index );

   return this.columnNames[index - 1].trim( );
}

Understanding DataSetMetaData

The DataSetMetaData class describes the features and capabilities of the data set, including the following:

• Indicating whether the data set supports multiple result sets

• Providing information about the sort mode for columns

• Returning a reference to the data source connection

getConnection( ) returns a reference to a data source connection, as shown in Listing 20-14.

Listing 20-14 The getConnection( ) method


public IConnection getConnection( ) throws OdaException
{
   return m_connection;
}

Understanding Messages

The Messages class defines the exception messages for the CSV ODA driver.

getString( ) returns a message from the resource bundle using the key value, as shown in Listing 20-15.

Listing 20-15 The getString( ) method


public static String getString(String key) {
   try {
      return RESOURCE_BUNDLE.getString(key);
   }
   catch (MissingResourceException e) {
      return '!' + key + '!';
   }
}

Understanding CommonConstants

The CommonConstants class defines the constants used in the package, such as the driver name, ODA version, query keywords, and delimiters. Listing 20-16 shows these definitions.

Listing 20-16 The CommonConstants class


final class CommonConstants
{
   public static final String DELIMITER_COMMA = ",";
   public static final String DELIMITER_SPACE = " ";
   public static final String DELIMITER_DOUBLEQUOTE = """;
   public static final String KEYWORD_SELECT = "SELECT";
   public static final String KEYWORD_FROM = "FROM";
   public static final String KEYWORD_AS = "AS";
   public static final String KEYWORD_ASTERISK = "*";
   public static final String DRIVER_NAME =
      "ODA CSV FILE DRIVER";

   public static final int MaxConnections = 0;
   public static final int MaxStatements = 0;
   public static final String CONN_HOME_DIR_PROP = "HOME";
   public static final String CONN_DEFAULT_CHARSET = "UTF-8";
   public static final String PRODUCT_VERSION = "3.0";
}

Developing the CSV ODA user interface extension

The data source extension point, org.eclipse.datatools.connectivity.oda.design.ui.dataSource, supports adding a new data source to a user interface, such as BIRT Report Designer. For each data source, the extension implements the following optional components:

• A wizard for creating the data source

• A set of pages for editing the data source

• The list of data sets that the data source supports

For each data set, the extension implements the following optional components:

• A wizard for creating the data set

• A set of pages for editing the data set

The data source editor page must implement the extension point, org.eclipse.ui.propertyPages, by extending the abstract class, org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSourceEditorPage.

The data set editor page must implement the extension point, org.eclipse.ui.propertyPages, by extending the abstract class, org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSourceEditorPage.

The ODA data source and data set user interface extensions extend these base classes to create customized property pages with page control and other behavior.

This section describes how to implement a BIRT ODA user interface plug-in, using the CSV ODA driver plug-in as an example.

To create an ODA driver plug-in, perform the following tasks:

• Create the CSV ODA user interface plug-in project.

• Define the dependencies.

• Specify the run-time archive.

• Declare the ODA user interface extension points.

Creating the CSV ODA user interface plug-in project

You can create the CSV ODA user interface plug-in project, org.eclipse.birt.report.data.oda.csv.ui, using the Eclipse PDE. The Eclipse PDE provides a wizard to assist you in setting up a plug-in project and creating the framework for a plug-in extension.

The New Plug-in Project wizard simplifies the process of specifying a plug-in project, automatically adds the required extension points, and sets the dependencies. The wizard also generates the plug-in manifest file, plugin.xml, and optionally, the Java plug-in run-time class.

The wizard creates all the implementation Java classes. It also puts TODO tags in the generated source code for customizing code along with guiding comments.

After using the wizard to create the plug-in, review the settings, make adjustments to the settings, and add the code to the Java class stubs to implement the desired functionality.

The following section describes how to create the plug-in project using the New Plug-in Project wizard.

How to create the CSV ODA user interface plug-in project

1 From the Eclipse menu, choose File→New→Project. New Project appears.

2 On New Project—Select a wizard, open Business Intelligence and Reporting Tools, and select ODA Designer Plug-in Project as shown in Figure 20-8.

Figure 20-8 Specifying the CSV ODA Designer Plug-in Project

image

3 Choose Next. New Plug-in Project appears.

4 In Plug-in Project, modify the settings as shown in Table 20-5. Choose Next. Plug-in Content appears.

Table 20-5 Settings for CSV ODA user interface Plug-in Project options

image

5 In Plug-in Content, modify the settings, where needed, as shown in Table 20-6. Choose Finish.

Table 20-6 Settings for CSV ODA user interface Plug-in Content options

image

6 In Templates, choose ODA Data Source Designer as shown in Figure 20-9. Choose Next. ODA Data Source Designer appears.

Figure 20-9 Specifying the CSV ODA Data Source Designer template

image

The wizard warns you to create ODA Data Source Runtime Driver first, then create the designer user interface. The wizard also lists the extension points that are automatically included in the default implementation.

• org.eclipse.datatools.connectivity.connectionProfile

• org.eclipse.datatools.connectivity.ui.connectionProfileImage

• org.eclipse.ui.propertyPages

• org.eclipse.datatools.connectivity.oda.design.ui.dataSource

7 In ODA Data Source Designer, specify values for the following options used to generate the ODA plug-in, as shown in Table 20-7. Choose Finish.

Table 20-7 Settings for CSV ODA UI Plug-in Content options

image

The CSV ODA user interface plug-in project appears in Eclipse Package Explorer, as shown in Figure 20-10.

Figure 20-10 Viewing the CSV ODA user interface plug-in project

image

Double-click plugin.xml to open it in PDE Manifest Editor. The plug-in Overview page looks like the one shown on Figure 20-11.

Figure 20-11 Viewing the CSV ODA user interface plug-in

image

Understanding the ODA data source user interface extension points

In this next step, you review the extension points added by the wizard to implement the CSV ODA user interface extension and add the extension element details. Figure 20-12 shows the list of CSV ODA user interface extension points.

Figure 20-12 CSV ODA user interface extension points

image

The CSV ODA user interface plug-in extends the functionality defined by the following extension points:

• org.eclipse.datatools.connectivity.connectionProfile Provides support for adding a connection profile

• org.eclipse.datatools.connectivity.connectionProfileImage The connection profile image extension point supports adopters using multiple icons from their main connection profile extension into a separate plug-in for user interface code and resources.

• org.eclipse.ui.propertyPages Adds a property page that displays the properties of an object in a dialog box

• org.eclipse.datatools.connectivity.oda.design.ui.dataSource Extends the ODA Designer user interface framework to support creating a dialog page so a user can specify an ODA data source and a related data set

The extension points specify the following properties that identify the extensions in the run-time environment:

• ID

Optional identifier of the extension instance

• Name

Optional name of the extension instance

Understanding the ConnectionProfile extension point

The connectionProfile extension point specifies the newWizard, which creates the connection profile.

Understanding the propertyPages extension point

The propertyPages extension point specifies the following extension elements:

• page

Defines a property page. Specifies properties such as id, display name, category, icon, object class, filter, and category. The id and the display name are localized in the plugin.properties file, as shown in Listing 20-17.

Listing 20-17 plugin.properties


########################################################
Copyright (c) 2009 <<Your Company Name here>>
#######################################################
# Plug-in Configuration
#
oda.data.source.id=org.eclipse.birt.report.data.oda.csv
#
#######################################################
# NLS strings
#
plugin.name=CSV File ODA
data.source.name=CSV File Data Source
connection.profile.name=CSV File Data Source Connection Profile
newwizard.name=CSV File Data Source
newwizard.description=Create a CSV File Data Source
   connection profile
wizard.window.title=New CSV File Data Source Profile
wizard.data.source.page.title=CSV File Data Source
profile.propertypage.name=CSV File Data Source Connection
   Properties
wizard.data.set.window.title=New CSV File Data Set
wizard.data.set.page.title=Query

The ODA user interface framework provides a default implementation that creates a text control for each property value, which is why there is no need to provide a custom implementation. By default, the wizard sets the page.class in the following org.eclipse.ui.propertyPages extension point:

page.class=org.eclipse.datatools.connectivity.oda.design.ui
   .pages.impl.DefaultDataSourcePropertyPage

• filter

Specifies an action filter that evaluates the attributes of each object in a current selection. If an object has the specified attribute state, a match occurs. Each object must implement the org.eclipse.ui.IActionFilter interface.

Understanding the dataSource extension point

The dataSource extension point specifies the following extension elements:

• dataSourceUI

Adds user interface support for specifying an extended data source.

• dataSetUI

The dataSetUI extension element defines the following extension elements and details:

• id

Fully qualified name of the data set, such as org.eclipse.birt.report.data.oda.csv.dataSet. This name must be the same as the name for the ODA extension driver data set.

• dataSetWizard

Wizard class used to specify a data set in the BIRT Report Designer user interface. This class must use or extend org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSetWizard. Localize the window title, %wizard.data.set.window.title, in the plug-in properties file.

• dataSetPage

Specifies an editor page to add to the editor dialog for a data set. The data set user interface adds editor pages to a dialog in the order in which the pages are defined. This class must use or extend org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSetWizardPage. The implementation code is in org.eclipse.birt.report.data.oda.csv.ui.impl.CustomDataSetWizardPage. The wizard provides a default implementation code.

The page display name, %wizard.data.set.page.title, is externalized in the plugin.properties tile.

Understanding the sample CSV ODA user interface extension

The CSV ODA user interface extension described in this chapter illustrates how to create an ODA user interface plug-in using the Eclipse PDE. The following section describes the code-based extensions a developer must make to complete the development of the CSV ODA user interface extension, after defining the plug-in framework in the Eclipse PDE.

The CSV ODA user interface plug-in contains the following packages:

• org.eclipse.birt.report.data.oda.csv.ui

Contains the UiPlugin class, which is automatically generated by the PDE Manifest Editor when you create the plug-in project.

• org.eclipse.birt.report.data.oda.csv.ui.i18n

Contains the Messages class and the properties file, messages.properties, to generate the messages displayed in the user interface. The localized versions for these messages are in files that use the following naming syntax:

messages_<locale>.msg

• org.eclipse.birt.report.data.oda.csv.ui.impl

Contains the CustomDataSetWizardPage class, an automatically generated implementation of an ODA data set designer page, which supports creating or editing an ODA data set.

• org.eclipse.birt.report.data.oda.csv.ui.wizards

The wizards package contains the classes that create the user interface pages used to choose a data source and data set in BIRT Report Designer.

Implementing the ODA data source and data set wizards

In BIRT release 2.1, BIRT Report Designer adopted the Eclipse Data Tools Platform (DTP) ODA design-time framework. BIRT release 2.6 further extends the DTP ODA design-time framework by adding new wizards to automatically generate customizable implementations. The DTP ODA framework defines two of the three extension points used in the CSV ODA user interface plug-in:

• Connection profile

Defined in org.eclipse.datatools.connectivity.connectionProfile

• Data source and data set wizards

Defined in org.eclipse.datatools.connectivity.oda.design.ui.dataSource

The CSV ODA user interface plug-in also must implement the extension point for property pages defined in org.eclipse.ui.propertyPages.

The CSV ODA user interface plug-in uses the following abstract base classes in the org.eclipse.datatools.connectivity.oda.design.ui.wizards package to create the wizards that specify the data source and data set pages. An ODA user interface plug-in must extend these classes to provide the wizard pages with page control and related behavior:

• DataSourceEditorPage

Provides the framework for implementing an ODA data source property page

• DataSourceWizardPage

Provides the framework for implementing an ODA data source wizard page

• DataSetWizardPage

Provides the framework for implementing an ODA data set wizard page

Understanding the org.eclipse.birt.report.data.oda.csv.ui.impl package

This customizable page provides a simple Query Text control for user input. The page extends org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSetWizardPage in the DTP ODA design-time framework to support updating an ODA data set design instance using query metadata.

Understanding the org.eclipse.birt.report.data.oda.csv.ui.wizards package

The org.eclipse.birt.report.data.oda.csv.ui.wizards package in the CSV ODA user interface extension example implements the following classes:

• Constants

Defines the constants for the data source connection properties defined in the run-time drive implementation, org.eclipse.birt.report.data.oda.csv.

• CSVFilePropertyPage

Extends DataSourceEditorPage. This class creates and initializes the editor controls for the property page used to specify the ODA data source. The class updates the connection profile properties with the values collected from the page.

• CSVFileSelectionPageHelper

Specifies the page layout and sets up the control that listens for user input and verifies the location of the CSV data source file.

• CSVFileSelectionWizardPage

Extends DataSourceWizardPage. This class creates and initializes the controls for the data source wizard page. The class sets the select file message and collects the property values.

• FileSelectionWizardPage

Extends DataSetWizardPage. This class creates and initializes the controls for the data set wizard page and specifies the page layout. The class connects to the data source, executes a query, retrieves the metadata and result set, and updates the date-set design.

Understanding Constants

The Constants class defines the following variables for the data source connection properties defined in org.eclipse.birt.report.data.oda.csv:

• ODAHOME specifies the CSV ODA file path constant, HOME

• ODA_DEFAULT_CHARSET specifies the default character set as 8-bit Unicode Transformation Format (UTF-8)

• DEFAULT_MAX_ROWS sets the default maximum number of rows that can be retrieved from the data source

Listing 20-18 shows the code for the Constants class.

Listing 20-18 The Constants class


public class Constants {

   public static String ODAHOME="HOME";
   public static String ODA_DEFAULT_CHARSET = "UTF-8";
   public static int DEFAULT_MAX_ROWS = 1000;
}

Understanding CSVFilePropertyPage

CSVFilePropertyPage extends the DataSourceEditorPage class, implementing the following methods to provide page editing functionality for the CSV ODA data source property page:

• createAndInitCustomControl( ) method performs the following tasks:

• Instantiates a CSVFileSelectionPageHelper object

• Specifies the page layout and sets up the editing control by calling CSVFileSelectionPageHelper.createCustomControl( ) and initCustomControl( ) methods

Listing 20-19 shows the code for the createAndInitCustomControl( ) method.

Listing 20-19 The createAndInitCustomControl( ) method


protected void createAndInitCustomControl
   ( Composite parent, Properties profileProps )
{
   if( m_pageHelper == null )
      m_pageHelper =
         new CSVFileSelectionPageHelper( this );
   m_pageHelper.createCustomControl( parent );
   m_pageHelper.initCustomControl( profileProps );
   if( ! isSessionEditable( ) )
      getControl( ).setEnabled( false );
}

• collectCustomProperties( ) updates the connection profile properties with the values collected from the page by calling CSVFileSelectionPageHelper.collectCustomProperties( ) method, as shown in Listing 20-20.

Listing 20-20 The collectCustomProperties( ) method


public Properties collectCustomProperties
   ( Properties profileProps )
{
   if( m_pageHelper == null )
      return profileProps;
   return m_pageHelper.collectCustomProperties
      ( profileProps );
}

Understanding CSVFileSelectionPageHelper

CSVFileSelectionPageHelper provides auxiliary processing for the CSVFilePropertyPage and CSVFileSelectionWizardPage classes. CSVFileSelectionPageHelper implements the following methods:

• createCustomControl( ) performs the following tasks:

• Sets up the composite page layout

• Calls the setupFileLocation( ) method that sets up a control to listen for user input and verify the location of the CSV data source file

Listing 20-21 shows the code for the createCustomControl( ) method.

Listing 20-21 The createCustomControl( ) method


void createCustomControl( Composite parent )
{
   Composite content = new Composite( parent, SWT.NULL );
   GridLayout layout = new GridLayout( 2, false );
   content.setLayout(layout);
   setupFileLocation( content );
}

• setupFileLocation( ) performs the following tasks:

• Sets up the label and the grid data object in the page layout

• Sets up the control that listens for user input and verifies the location of the CSV data source file

Listing 20-22 shows the code for the setupFileLocation( ) method.

Listing 20-22 The setupFileLocation( ) method


private void setupFileLocation( Composite composite )
{
   Label label = new Label( composite, SWT.NONE );
   label.setText( Messages.getString
      ( "label.selectFile" ) );
   GridData data = new GridData( GridData.FILL_HORIZONTAL );

   fileName = new Text( composite, SWT.BORDER );
   fileName.setLayoutData( data );
   setPageComplete( false );

   fileName.addModifyListener(
      new ModifyListener( )
          {
            public void modifyText( ModifyEvent e )
            {
                  verifyFileLocation();
            }
            } );
}

• collectCustomProperties( ) sets the data source directory property in the connection profile, as shown in Listing 20-23.

Listing 20-23 The collectCustomProperties( ) method


Properties collectCustomProperties( Properties props )
{
   if( props == null )
      props = new Properties( );
      props.setProperty( CommonConstants.CONN_HOME_DIR_PROP,
         getFolderLocation( ) );
   return props;
}

• initCustomControl( ) initializes the data source wizard control to the location of the data source file, as shown in Listing 20-24.

Listing 20-24 The initCustomControl( ) method


void initCustomControl( Properties profileProps )
{
   if( profileProps == null || profileProps.isEmpty( ) ||
      fileName == null )
   return;
   String folderPath = profileProps.getProperty(
      CommonConstants.CONN_HOME_DIR_PROP );
   if( folderPath == null )
      folderPath = EMPTY_STRING;
      fileName.setText( folderPath );
      verifyFileLocation( );
}

Understanding CSVFileSelectionWizardPage

The CSVFileSelectionWizardPage class extends the DataSourceWizardPage class, implementing the following methods to provide the functionality for the CSV ODA data source wizard page:

• The createPageCustomControl( ) method performs the following tasks:

• Instantiates a CSVFileSelectionPageHelper object

• Specifies the page layout and sets up the wizard page control by calling CSVFileSelectionPageHelper.createCustomControl( ) method

• Calls CSVFileSelectionPageHelper.initCustomControl( ) to initialize the control to the location of the data source file

Listing 20-25 shows the code for the createPageCustomControl( ) method.

Listing 20-25 The createPageCustomControl( ) method


public void createPageCustomControl( Composite parent )
{
   if( m_pageHelper == null )
         m_pageHelper =
            new CSVFileSelectionPageHelper( this );
      m_pageHelper.createCustomControl( parent );
      m_pageHelper.initCustomControl( m_csvFileProperties );
}

• The collectCustomProperties( ) method instantiates a Properties object to contain the CSV data source properties information, as shown in Listing 20-26.

Listing 20-26 The collectCustomProperties( ) method


public Properties collectCustomProperties( )
{
   if( m_pageHelper != null )
      return m_pageHelper.collectCustomProperties(
         m_csvFileProperties );
   return ( m_csvFileProperties != null ) ?
      m_csvFileProperties : new Properties( );
}

Understanding FileSelectionWizardPage

The FileSelectionWizardPage class extends the DataSetWizardPage class, implementing the following methods to provide the functionality for the CSV ODA data set wizard page:

• The createPageControl( ) method performs the following tasks:

• Specifies the page layout and sets up the wizard page control

• Gets the data source properties

• Calls populateAvailableList( ) to update the data set design

Listing 20-27 shows the code for the createPageControl( ) method.

Listing 20-27 The createPageControl( ) method


private Control createPageControl( Composite parent )
{
   Composite composite = new Composite( parent, SWT.NULL );
   FormLayout layout = new FormLayout( );
   composite.setLayout( layout );
   FormData data = new FormData( );
   data.left = new FormAttachment( 0, 5 );
   data.top = new FormAttachment( 0, 5 );
   fileName = new Text( composite, SWT.BORDER );
   fileName.setLayoutData( data );
   Properties dataSourceProps =
      getInitializationDesign( ).getDataSourceDesign( )
      .getPublicProperties( );
   fileName.setText( ( String )(
      dataSourceProps.getProperty( Constants.ODAHOME )));
   data = new FormData( );
   data.top = new FormAttachment(
      fileName, 10, SWT.BOTTOM );
   data.left = new FormAttachment( 0, 5 );
   data.right = new FormAttachment( 47, -5 );
   data.bottom = new FormAttachment( 100, -5 );
   data.width = DEFAULT_WIDTH;
   data.height = DEFAULT_HEIGHT;
   m_availableList = new List( composite,
      SWT.MULTI | SWT.BORDER |
      SWT.H_SCROLL | SWT.V_SCROLL );
   m_availableList.setLayoutData( data );
   m_selectedFile =
      new File(( String )(
      dataSourceProps.getProperty( Constants.ODAHOME )));
   populateAvailableList( );
   return composite;
}

• getQuery( ) method builds the query for the data set by performing the following tasks:

• Gets the table name from the file object

• Appends the table name to a query that selects all the columns using a wildcard

• Appends the column list then the table name to a query that selects specific columns

• Returns the query text

Listing 20-28 shows the code for the getQuery( ) method.

Listing 20-28 The getQuery( ) method


private String getQuery( )
{
   String tableName = null;
   StringBuffer buf = new StringBuffer( );
   File file = m_selectedFile;
   if(file != null)
   {
      tableName = file.getName( );
   }
   if(tableName != null)
   {
      if(m_availableList.getItemCount( ) == 0)
      {
         buf.append("select * from ").append(tableName);
      }
      else
      {
         buf.append("select ");
         String[ ] columns = m_availableList.getItems( );
         for(int n = 0; n < columns.length; n++)
         {
            buf.append(columns[n]);
            if(n < columns.length - 1)
            {
               buf.append(", ");
            }
         }
      buf.append(" from ").append(tableName);
      }
   }
return buf.toString( );
}

• getQueryColumnNames( ) method performs the following tasks:

• Instantiates the CSVFileDriver

• Prepares the query and gets the results set metadata using the CSV ODA run-time driver and data source connection properties settings

• Gets the column count

• Iterates through the metadata results to get the column names and return the results

Listing 20-29 shows the code for the getQueryColumnNames( ) method.

Listing 20-29 The getQueryColumnNames( ) method


private String[ ] getQueryColumnNames(
   String queryText, File file )
{
   IDriver ffDriver = new CSVFileDriver( );
   IConnection conn = null;

   try
   {
      conn = ffDriver.getConnection( null );

      IResultSetMetaData metadata =
         getResultSetMetaData( queryText, file, conn );
      int columnCount = metadata.getColumnCount( );

      if( columnCount == 0 )
         return null;
      String[ ] result = new String[columnCount];

      for( int i = 0; i < columnCount; i++)
         result[i] = metadata.getColumnName( i + 1 );
      return result;
   }
   catch( OdaException e )
   {
      setMessage( e.getLocalizedMessage( ), ERROR );
      disableAll( );
      return null;
   }
   finally
   {
      closeConnection( conn );
   }
}

• getResultSetMetaData( ) method performs the following tasks:

• Sets up the Properties object with the location of the data source file

• Opens the connection to the data source

• Sets up a Query object and prepares the query text

• Executes the query

• Returns the metadata

Listing 20-30 shows the code for the getResultSetMetaData( ) method.

Listing 20-30 The getResultSetMetaData( ) method


private IResultSetMetaData getResultSetMetaData(
   String queryText, File file, IConnection conn )
   throws OdaException
{
   java.util.Properties prop = new java.util.Properties( );
   prop.put( CommonConstants.CONN_HOME_DIR_PROP,
      file.getParent( ) );
   conn.open( prop );
   IQuery query = conn.newQuery( null );
   query.setMaxRows( 1 );
   query.prepare( queryText );
   query.executeQuery( );
   return query.getMetaData( );
}

• setResultSetMetaData( ) method updates the data set page design with metadata returned by the query by performing the following tasks:

• Calls the DesignSessionUtil.toResultSetColumnsDesign( ) method to convert the run-time metadata to a design-time ResultSetColumns object

• Obtains a ResultSetDefinition object from the design factory to use in populating the data set page design with the metadata definitions

• Calls the resultSetDefn.setResultSetColumns( ) method to set the reference to ResultSetColumns object, containing the metadata content

• Assigns the result set definition to the data set design

Listing 20-31 shows the code for the setResultSetMetaData( ) method.

Listing 20-31 The setResultSetMetaData( ) method


private void setResultSetMetaData(
   DataSetDesign dataSetDesign,IResultSetMetaData md )
   throws OdaException
{
   ResultSetColumns columns =
      DesignSessionUtil.toResultSetColumnsDesign( md );
   ResultSetDefinition resultSetDefn =
      DesignFactory.eINSTANCE.createResultSetDefinition( );
   resultSetDefn.setResultSetColumns( columns );
   dataSetDesign.setPrimaryResultSet( resultSetDefn );
   dataSetDesign.getResultSets().setDerivedMetaData( true );
}

• savePage( ) method performs the following tasks:

• Instantiates the CSVFileDriver

• Gets the result set metadata

• Updates the data set design with the metadata

• Closes the connection

Listing 20-32 shows the code for the savePage( ) method.

Listing 20-32 The savePage( ) method


private void savePage( DataSetDesign dataSetDesign )
{
   String queryText = getQuery( );
   dataSetDesign.setQueryText( queryText );
   IConnection conn = null;
   try
   {
      IDriver ffDriver = new CSVFileDriver( );
      conn = ffDriver.getConnection( null );
      IResultSetMetaData metadata =
         getResultSetMetaData( queryText, m_selectedFile,
         conn );
      setResultSetMetaData( dataSetDesign, metadata );
   }
   catch( OdaException e )
   {
      dataSetDesign.setResultSets( null );
   }
   finally
   {
      closeConnection( conn );
   }
}

Testing the CSV ODA user interface plug-in

On PDE Manifest Editor, in Overview, the Testing section contains links to launch a plug-in as a separate Eclipse application in either Run or Debug mode.

How to launch the CSV ODA user interface plug-in

1 From the Eclipse SDK menu, choose Run—Run Configurations. In Run Configurations, right-click Eclipse Application. Choose New.

2 Create a configuration to launch an Eclipse application by performing the following tasks:

1 In Name, type:

CSV ODA Test

2 In Main, in Location, type:

C:Test

Run Configurations appears, as shown in Figure 20-13.

Figure 20-13 Creating a configuration to launch an Eclipse application

image

3 Choose the Plug-ins tab to select the list of plug-ins that you want to launch with the Run configuration.

4 In Launch with, choose

plug-ins selected below only

from the drop-down list, as shown in Figure 20-14.

Figure 20-14 Including the ODA plug-ins in the launch configuration

image

5 In Plug-ins check the following plug-ins

org.eclipse.birt.data.oda.csv
org.eclipse.birt.data.oda.csv.ui

The plug-ins selected below only option supports configuring an environment that uses a subset of the plug-ins from the workspace and the target platform. The target platform consists of all the plug-ins that are explicitly checked on the Window > Preferences... > Plug-in Development > Target Platform preference page.

When using the plug-ins selected below only option you must ensure that the list of selected plug-ins is up-to-date when you add or remove a plug-in from the workspace. There are several buttons available to help you select plug-ins, including an Add Required Plug-ins button. The Add Required Plug-ins button should be used often to ensure the selected subset of plug-ins is complete.

6 Choose Run to launch the run-time workbench.

7 In the run-time workbench, choose the Report Design perspective.

8 In Report Design, create a new report project and create a new blank report.

How to create a report design

1 In Report Design, choose File→New→Project.

2 Expand Business Intelligence and Reporting Tools and choose Report Project. Choose Next. New Report Project appears.

3 In Report Project, perform the following tasks:

1 In Project name, type:

testCSVODA

2 Select Use default location. Choose Finish. In Navigator, testCSVODA appears.

4 In Navigator, right-click testCSVODA and choose File→New→Report. New Report appears.

5 In New Report, perform the following tasks:

1 In Enter or select the parent folder, select testCSVODA.

2 In file name, type:

new_report.rptdesign

Choose Next.

3 In Report Templates, select Blank Report. Choose Finish. In Navigator, new_report_1.rptdesign appears in the testCSVODA project folder.

6 Right-click new_report.rptdesign and choose Open. new_report.rptdesign appears in Report Design, as shown in Figure 20-15.

Figure 20-15 new_report.rptdesign in the report design environment

image

How to specify a data source

1 In Report Design, choose Data→New Data Source. On New Data Source, choose CSV ODA Data Source, as shown in Figure 20-16. Choose Next.

Figure 20-16 Choosing CSV ODA Data Source

image

2 In Select File, enter the path and file name of the directory that contains the CSV data source file, as shown in Figure 20-17. Choose Finish. Report Design appears.

Figure 20-17 Specifying path to the CSV ODA data source file directory

image

How to select a new data set

1 In Report Design, choose Data→New Data Set. New Data Set appears, as shown in Figure 20-18. Choose Next. Query appears.

Figure 20-18 New Data Set for the CSV ODA data source

image

2 In Query, select all the columns, as shown in Figure 20-19. Choose Finish.

Figure 20-19 Selecting columns

image

Edit Data Set appears, as shown in Figure 20-20.

Figure 20-20 Edit Data Set

image

3 Choose Preview Results. Preview Results appears as shown in Figure 20-21. Choose OK.

Figure 20-21 Data preview

image

4 In Data Explorer, expand Data Sources and Data Sets. Data Explorer appears as shown in Figure 20-22.

Figure 20-22 Data Set in Data Explorer

image

How to run a report design using CSV ODA user interface and driver extensions

1 To build the report, drag Data Set from Data Explorer to the layout editor. Layout appears as shown in Figure 20-23.

Figure 20-23 Report design in the layout editor

image

2 To run the report design, choose Preview. new_report_1.rptdesign runs, displaying the data set from the CSV data source, as shown in Figure 20-24.

Figure 20-24 Preview of the data set from the CSV ODA data source

image

Developing a Hibernate ODA extension

To develop the Hibernate ODA extension create two new projects in the Eclipse PDE. These two projects implement the following plug-ins:

• org.eclipse.birt.report.data.oda.hibernate

The Hibernate ODA driver accesses a relational data source using HQL. The Hibernate ODA data source plug-in extends the functionality defined by the org.eclipse.datatools.connectivity.oda.dataSource extension point to create the Hibernate ODA driver.

• org.eclipse.birt.report.data.oda.hibernate.ui

The Hibernate ODA user interface plug-in for BIRT Report Designer selects a Hibernate data source and supports creation of an HQL statement to retrieve data from the available tables and columns. The Hibernate ODA user interface plug-in extends the functionality defined by the org.eclipse.datatools.connectivity.oda.design.ui.dataSource, org.eclipse.ui.propertyPages, and org.eclipse.datatools.connectivity.connectionProfile extension points.

The user interface consists of the following pages:

• Data source page

Includes the Hibernate data source in the list of available data sources. The Hibernate ODA driver contains the preconfigured Hibernate configuration and mapping files that connect to the MySQL version of the BIRT demonstration database, ClassicModels.

• Data set page

Creates an HQL statement that selects the data set and embeds the HQL statement in the report design.

In BIRT Report Designer, the Hibernate ODA data source wizard supports selecting a Hibernate ODA driver containing preconfigured Hibernate configuration and mapping files. The Hibernate ODA driver searches for these configuration and mapping files in the plug-in’s hibfiles directory.

The Hibernate ODA driver also searches in the hibfiles directory for JAR and ZIP files and the org.eclipse.birt.report.data.oda.jdbc plug-in for JDBC drivers to add to the classpath. This approach prevents the need to copy drivers to multiple locations. Note that changing the configuration causes the Hibernate ODA driver plug-in to rebuild the Hibernate SessionFactory, which is a machine-intensive operation.

Once the Hibernate ODA driver creates the data source configuration, a data set can be created. The Hibernate data set wizard allows the user to enter HQL statements. The Hibernate ODA user interface example supports only simple queries, such as the following types of statements:

From Customer

or:

Select ord.orderNumber,cus.customerNumber, cus.customerName
   from Orders as ord, Customer as cus
   where ord.customerNumber = cus.customerNumber and
   cus.customerNumber = 363

In the Hibernate ODA plug-in, there is an exampleconfig directory. This directory contains a sample Hibernate configuration file, mapping files, and Java classes that connect to the BIRT sample MySQL database. Test the plug-in using these files as follows:

• Modify the hibernate.cfg.file to connect to your database configuration.

• Copy these files to the hibfiles directory.

• Create a JAR file containing the Java classes.

You can test and deploy the extensions in the Eclipse PDE run-time environment.

The following sections describe how to create and deploy the Hibernate ODA driver and user interface plug-in projects. To download the source code for the Hibernate ODA driver and user interface extension examples go to http://www.actuate.com/birt/contributions.

Creating the Hibernate ODA driver plug-in project

Create the Hibernate ODA driver plug-in project, org.eclipse.birt.report.data.oda.hibernate, using the New Plug-in Project wizard in the Eclipse PDE.

How to create the Hibernate ODA driver plug-in project

1 From the Eclipse PDE menu, choose File→New→Project.

2 In New Project—Select a wizard, open Business Intelligence and Reporting Tools and select ODA Runtime Driver Plug-in Project. Choose Next. New Plug-in Project appears.

3 In Plug-in Project, modify the settings as shown in Table 20-8.

Table 20-8 Settings for Hibernate ODA Plug-in Project options

image

4 On Plug-in Content, modify the settings as shown in Table 20-9.

Table 20-9 Settings for Hibernate ODA Plug-in Content options

image

Choose Next. Templates appears.

5 In Templates, choose ODA Data Source Runtime Driver. Choose Next. ODA Data Source Runtime Driver appears.

6 In ODA Data Source Runtime Driver, specify values for the following options used to generate the ODA plug-in, as shown in Table 20-10.

Table 20-10 Settings for ODA Data Source Runtime Driver options

image

Choose Finish. The Hibernate ODA driver plug-in project appears in the Eclipse PDE workbench.

How to specify the properties of the Hibernate ODA plug-in project

1 Using the Eclipse PDE Manifest Editor, in Dependencies, specify the following required plug-ins in the following order:

• org.eclipse.core.runtime

• org.eclipse.datatools.connectivity.oda

• org.eclipse.birt.report.data.oda.jdbc

2 In Runtime, in Exported Packages, verify that the following packages that the plug-in exposes to clients appear in the list:

• antlr

• antlr.actions.cpp

• antlr.actions.csharp

• antlr.actions.java

• antlr.actions.python

• antlr.ASdebug

• antlr.build

• antlr.collections

• antlr.collections.impl

• antlr.debug

• antlr.debug.misc

• antlr.preprocessor

• javax.transaction

• javax.transaction.xa

• net.sf.cglib.beans

• net.sf.cglib.core

• net.sf.cglib.proxy

• net.sf.cglib.reflect

• net.sf.cglib.transform

• net.sf.cglib.transform.hook

• net.sf.cglib.transform.impl

• net.sf.cglib.util

• net.sf.ehcache

• net.sf.ehcache.config

• net.sf.ehcache.hibernate

• net.sf.ehcache.store

• org.apache.commons.collections

• org.apache.commons.collections.comparators

• org.apache.commons.collections.iterators

• org.apache.commons.logging

• org.apache.commons.logging.impl

• org.apache.tools.ant.taskdefs.optional

• org.dom4j

• org.dom4j.bean

• org.dom4j.datatype

• org.dom4j.dom

• org.dom4j.dtd

• org.dom4j.io

• org.dom4j.jaxb

• org.dom4j.rule

• org.dom4j.rule.pattern

• org.dom4j.swing

• org.dom4j.tree

• org.dom4j.util

• org.dom4j.xpath

• org.dom4j.xpp

• org.eclipse.birt.report.data.oda.hibernate

• org.hibernate

• org.hibernate.action

• org.hibernate.cache

• org.hibernate.cache.entry

• org.hibernate.cfg

• org.hibernate.classic

• org.hibernate.collection

• org.hibernate.connection

• org.hibernate.context

• org.hibernate.criterion

• org.hibernate.dialect

• org.hibernate.dialect.function

• org.hibernate.engine

• org.hibernate.engine.query

• org.hibernate.engine.transaction

• org.hibernate.event

• org.hibernate.event.def

• org.hibernate.exception

• org.hibernate.hql

• org.hibernate.hql.antlr

• org.hibernate.hql.ast

• org.hibernate.hql.ast.exec

• org.hibernate.hql.ast.tree

• org.hibernate.hql.ast.util

• org.hibernate.hql.classic

• org.hibernate.id

• org.hibernate.impl

• org.hibernate.intercept

• org.hibernate.jdbc

• org.hibernate.jmx

• org.hibernate.loader

• org.hibernate.loader.collection

• org.hibernate.loader.criteria

• org.hibernate.loader.custom

• org.hibernate.loader.entity

• org.hibernate.loader.hql

• org.hibernate.lob

• org.hibernate.mapping

• org.hibernate.metadata

• org.hibernate.param

• org.hibernate.persister

• org.hibernate.persister.collection

• org.hibernate.persister.entity

• org.hibernate.pretty

• org.hibernate.property

• org.hibernate.proxy

• org.hibernate.secure

• org.hibernate.sql

• org.hibernate.stat

• org.hibernate.tool.hbm2ddl

• org.hibernate.tool.instrument

• org.hibernate.transaction

• org.hibernate.transform

• org.hibernate.tuple

• org.hibernate.type

• org.hibernate.usertype

• org.hibernate.util

• org.objectweb.asm

• org.objectweb.asm.attrs

3 On Runtime, in Classpath, add the following JAR files to the plug-in classpath:

• odahibernate.jar

• lib/ant-antlr-1.6.5.jar

• lib/antlr-2.7.6rc1.jar

• lib/asm.jar

• lib/asm-attrs.jar

• lib/cglib-2.1.3.jar

• lib/commons-collections-2.1.1.jar

• lib/commons-logging-1.0.4.jar

• lib/dom4j-1.6.1.jar

• lib/ehcache-1.1.jar

• lib/hibernate3.jar

• lib/jta.jar

These JAR files must have been previously imported into the lib directory in the Hibernate ODA plug-in. These JAR files can also be put in a new plug-in that the Hibernate ODA plug-in references.

4 On Extensions, add the extension point, org.eclipse.datatools.connectivity.oda.dataSource, and the following elements and details for:

• dataSource

Add the extension element details, as shown in Table 20-11.

Table 20-11 Property settings for the Hibernate dataSource extension element

image

The dataSource extension has an attribute named setThreadContextClassLoader, which, if set to true, sets the thread context class loader to the Hibernate ODA plug-in class loader. In this example, this attribute is set to true to avoid potential class conflicts with classes loaded with the Eclipse Tomcat plug-in.

• dataSet

Add the extension element details, as shown in Table 20-12.

Table 20-12 Property settings for the Hibernate dataSet extension element

image

5 In Extensions, select dataSource and add the following properties and element details:

• HIBCONFIG, as shown in Table 20-13.

Table 20-13 HIBCONFIG property settings

image

• MAPDIR, as shown in Table 20-14.

Table 20-14 MAPDIR property settings

image

6 In Extensions, select dataSet and add the list of dataTypeMapping elements, as shown in Table 20-15.

Table 20-15 Settings for Hibernate dataTypeMapping elements

image

Understanding the sample Hibernate ODA driver extension

The package for the Hibernate ODA extension example, org.eclipse.birt.report.data.oda.hibernate, implements the following classes using the ODA plug-in interfaces defined in the DTP plug-in, org.eclipse.datatools.connectivity.oda, and the extension points defined in the XML schema file, datasource.exsd. The package implements the following classes:

• Activator

Extends org.eclipse.core.runtime.Plugin. Defines the methods for starting, managing, and stopping a plug-in instance.

• HibernateDriver

Implements the IDriver interface. Instantiates the connection object for the Hibernate ODA driver, which provides the entry point for the Hibernate ODA plug-in.

• Connection

Implements the IConnection interface. Opens and closes the connection to the Hibernate ODA data source and instantiates the IQuery object.

• Statement

Implements the IQuery interface. Prepares the result set metadata containing the table and column names, executes the query, and fetches the data rows from the data source.

• ResultSet

Implements the IResultSet interface. Provides access to the data rows in the result set, maintaining a cursor that points to the current row. Handles the processing that gets the value for a column as the specified data type.

• ResultSetMetaData

Implements the IResultSetMetaData interface. Describes the metadata for each column in the result set.

• DataSetMetaData

Implements the IDataSetMetaData interface. Describes the features and capabilities of the driver for the data set.

• Messages

Defines the exception messages for the Hibernate ODA driver.

• DataTypes

Defines, validates, and returns the data types supported by the Hibernate ODA driver.

• CommonConstant

Defines the constants used in the package, such as the driver name, ODA version, query keywords, and delimiters.

• HibernateUtil

Manages the Hibernate SessionFactory that provides the session or run-time interface between the Hibernate service and the ODA driver. This class is built based on the example HibernateUtil, available at http://www.hibernate.org.

The Hibernate ODA driver plug-in supports specifying the Hibernate configuration file and mapping files directory in the data source wizard. The plug-in creates the Hibernate SessionFactory from these settings. The example project has an exampleconfig directory that contains a Hibernate configuration and mapping files for use with the BIRT MySQL example database, ClassicModels.

The following sections describe the classes where there are important differences between the implementation of Hibernate ODA driver and the earlier example, the CSV ODA driver.

Understanding HibernateDriver

The HibernateDriver class instantiates the Connection object for the Hibernate ODA driver. This class implements the IDriver interface, but does not provide any processing for the methods that configure logging and set the application context. Listing 20-33 shows the getConnection( ) method.

Listing 20-33 The getConnection( ) method


public IConnection getConnection( String connectionClassName )
      throws OdaException
{
   return new Connection( );
}

getMaxConnections( ) returns 0, imposing no limit on the number of connections to the ODA data source from the application. Listing 20-34 shows the getMaxConnections( ) method.

Listing 20-34 The getMaxConnections( ) method


public int getMaxConnections( ) throws OdaException
{
   return( 0 );
}

Understanding Connection

The Connection class implements the following methods:

• open( )

Opens a Hibernate session and sets the Boolean variable, isOpen, to true. The open( ) method uses the HibernateUtil class to obtain a session from a Hibernate SessionFactory, providing the run-time interface between the Hibernate service and the ODA driver.

The open( ) method retrieves the locations for the Hibernate configuration file and mapping files directory from connection properties. The open( ) method calls HibernateUtil.constructSessionFactory( ), which attempts to build the SessionFactory with these settings. If the SessionFactory already exists, the plug-in does not recreate the SessionFactory unless the Hibernate configuration file or the mapping directory have changed.

Listing 20-35 shows the code for the open( ) method.

Listing 20-35 The open( ) method


public void open( Properties connProperties )
   throws OdaException
{
   try
   {
      configfile =
         connProperties.getProperty( "HIBCONFIG" );
      mapdir = connProperties.getProperty( "MAPDIR" );
      HibernateUtil
         .constructSessionFactory( configfile, mapdir );
      Session testSession = HibernateUtil.currentSession( );
      this.isOpen = true;
   }catch( Exception e )
   {
      throw new OdaException( e.getLocalizedMessage( ) );
   }
}

• newQuery( )

Opens a new query by returning an instance of a Statement object, the class that implements the IQuery interface. The connection can handle multiple result set types, but the Hibernate ODA example uses only one and ignores the dataSetType parameter, as shown in Listing 20-36.

Listing 20-36 The newQuery( ) method


public IQuery newQuery( String dataSetType )
   throws OdaException
{
   if ( !isOpen( ) )
      throw new OdaException( Messages.getString
         ( "Common.CONNECTION_IS_NOT_OPEN" ) );
   return new Statement( this );
}

• getMetaData( )

Returns an IDataSetMetaData object of the data set type, as shown in Listing 20-37.

Listing 20-37 The getMetaData( ) method


public IDataSetMetaData getMetaData( String dataSetType )
   throws OdaException
{
   return new DataSetMetaData( this );
}

• getMaxQueries( )

Indicates the maximum number of queries the driver supports. The getMaxQueries( ) method returns 1, indicating that the Hibernate ODA driver does not support concurrent queries, as shown in Listing 20-38.

Listing 20-38 The getMaxQueries( ) method


public int getMaxQueries( ) throws OdaException
{
   return 1;
}

• commit( ) and rollback( )

Handle transaction processing. The Hibernate ODA driver example does not support transaction operations. In the Connection class, the commit( ) and rollback( ) methods throw UnsupportedOperationException. Listing 20-39 shows the code for the commit( ) method.

Listing 20-39 The commit( ) method


public void commit( ) throws OdaException
{
   throw new UnsupportedOperationException ( );
}

• close( )

Closes the Hibernate session, as shown in Listing 20-40.

Listing 20-40 The close( ) method


public void close( ) throws OdaException
{
   this.isOpen = false;
   try{
      HibernateUtil.closeSession( );
   }catch(Exception e){
      throw new OdaException( e.getLocalizedMessage( ) );
   }
}

Understanding DataSetMetaData

The DataSetMetaData class describes the features and capabilities of the data source for the specified data set. The Hibernate ODA driver example returns true or false to indicate support for a feature. The Hibernate ODA driver example does not support input or output parameters, named parameters, or multiple result sets.

The following code example indicates that the Hibernate ODA driver does not support multiple result sets, as shown in Listing 20-41.

Listing 20-41 The supportsMultipleResultSets( ) method


public boolean supportsMultipleResultSets( ) throws OdaException
{
   return false;
}

A method such as getSQLStateType( ), which has no implementation, simply throws UnsupportedOperationException, as shown in Listing 20-42.

Listing 20-42 The getSQLStateType( ) method


public int getSQLStateType( ) throws OdaException
{
   throw new UnsupportedOperationException ( );
}

Understanding Statement

The Statement class implements the IQuery interface. This class prepares and executes the query. Statement also handles parameters and retrieves the result set and result set metadata.

The Statement class implements the following methods:

• prepare( )

The ODA framework calls the prepare( ) method before executing the query. The ODA framework uses the query saved in the report design.

The Hibernate ODA user interface plug-in also calls prepare( ) to verify the columns used in the report design. The user interface plug-in passes an HQL statement that gets the columns from the result set object.

prepare( ) sets up the result-set metadata and stores the query in an object variable for use by the executeQuery( ) method. The ODA run time uses the result-set metadata to retrieve the data. BIRT Report Designer also uses the result-set metadata to display the columns in the user interface.

The prepare( ) method performs the following operations:

• Sets up array lists to contain the columns, column types, and column classes

• Trims the query String

• Creates a Hibernate Query object, using the HQL query

• Gets the Hibernate column names, types, and classes for the query

• Instantiates a ResultSetMetaData object, passing in the column names and data types

• Saves the query for execution

Listing 20-43 shows the code for the prepare( ) method.

Listing 20-43 The prepare( ) method


public void prepare( String query ) throws OdaException
{
   Query qry = null;
   testConnection( );
   ArrayList arColsType = new ArrayList( );
   ArrayList arCols = new ArrayList( );
   ArrayList arColClass = new ArrayList( );

   String[ ] props = null;
   try
   {
      Session hibsession = HibernateUtil.currentSession( );
      query = query.replaceAll( "[\n\r]+"," " );
      query = query.trim( );
      qry = hibsession.createQuery( query );
      Type[ ] qryReturnTypes = qry.getReturnTypes( );
      if( qryReturnTypes.length > 0
         && qryReturnTypes[0].isEntityType( ) )
      {
         for( int j=0; j< qryReturnTypes.length; j++ )
         {
            String clsName=qryReturnTypes[j].getName( );
            props =
               HibernateUtil.getHibernateProp( clsName );
            for( int x = 0; x < props.length; x++ )
            {
               String propType =
                  HibernateUtil.getHibernatePropTypes
                     ( clsName, props[x] );
               if( DataTypes.isValidType( propType ))
               {
                  arColsType.add( propType );
                  arCols.add( props[x] );
                  arColClass.add( clsName );
               }
            else
            {
                  throw new OdaException
                     ( Messages.getString
                     ( "Statement.SOURCE_DATA_ERROR" ) );
               }
            }
         }
      }
      else
      {
         props = extractColumns( qry.getQueryString( ) );
         for( int t=0; t < qryReturnTypes.length; t++)
         {
            if( DataTypes.isValidType
               (qryReturnTypes[t].getName( )))
            {
               arColsType.add( qryReturnTypes[t].getName( ));
               arCols.add( props[t] );
            }
            else
            {
               throw new OdaException
                  ( Messages.getString
                     ("Statement.SOURCE_DATA_ERROR") );
            }
         }
      }
   }
   catch( Exception e )
   {
      throw new OdaException( e.getLocalizedMessage( ) );
   }
   this.resultSetMetaData = new ResultSetMetaData
      (( String[ ])arCols.toArray
         ( new String[arCols.size( )] ),
         (String[ ])arColsType.toArray
            ( new String[arColsType.size( )] ),
         (String[ ])arCols.toArray
            ( new String[arCols.size( )] ),
         (String[ ])arColClass.toArray
            ( new String[arColClass.size( )] ));
   this.query = query;
}

• getMetaData( )

The BIRT framework calls getMetaData( ) after the prepare( ) method to retrieve the metadata for a result set. The BIRT framework uses the metadata to create the data set in the report.

Listing 20-44 shows the code for the getMetaData( ) method.

Listing 20-44 The getMetaData( ) method


public IResultSetMetaData getMetaData( ) throws OdaException
{
   return this.resultSetMetaData;
}

• executeQuery( )

The executeQuery( ) method executes the prepared query and retrieves the results. The executeQuery( ) method returns an IResultSet object, which is created using the list results, result-set metadata, and Hibernate types returned from the HQL query. The ODA framework uses the IResultSet object to iterate over the results.

The executeQuery( ) method performs the following operations:

• Sets up an array of org.hibernate.type.Type to map Java types to JDBC data types

• Sets up a list to contain the results set

• Trims the query String

• Instantiates a Hibernate Query object, creating the HQL query

• Executes the HQL query, returning the query result set in a List

• Gets the Hibernate types for the query result set

• Instantiates a ResultSet object, passing in the data, metadata, and Hibernate types

Listing 20-45 shows the code for the executeQuery( ) method.

Listing 20-45 The executeQuery( ) method


public IResultSet executeQuery( ) throws OdaException
{
   Type[ ] qryReturnTypes = null;
   List rst = null;
   try
   {
      Session hibsession = HibernateUtil.currentSession( );
      String qryStr = this.query;
      qryStr = qryStr.replaceAll( "[\n\r]+"," " );
      qryStr.trim( );
      Query qry = hibsession.createQuery( qryStr );
      rst = qry.list( );
      qryReturnTypes = qry.getReturnTypes( );

   }
   catch( Exception e )
   {
      throw new OdaException( e.getLocalizedMessage( ) );
   }
   return new ResultSet
      ( rst, getMetaData( ), qryReturnTypes );
}

• close( )

The close( ) method clears the Connection and ResultSetMetaData objects. In the Connection object, the close( ) method closes the Hibernate session.

Listing 20-46 shows the code for the Statement.close( ) method.

Listing 20-46 The Statement.close( ) method


public void close( ) throws OdaException
{
   connection = null;
   resultSetMetaData = null;
}

Understanding ResultSet

The ResultSet class implements the IResultSet interface. When this class is instantiated, it stores the list.iterator( ) passed from the Statement object. It uses the iterator when the ODA driver framework calls the next( ) method.

The iterator points to the next available row of data from the HQL query results. The framework calls the accessor methods that get the data types for the columns in the current row. For example, if the first column is a String, the framework calls getString( ). This method calls the getResult( ) method, which interprets the HQL query results.

The getResult( ) method parses the results in one of the following ways, depending on whether the query returns a Hibernate EntityType or just an array of values:

• If the query uses HQL and each return type is an EntityType, getResult( ) gets each Column class and uses the Hibernate ClassMetaData methods to retrieve the value.

• If the query returns standard data types, getResult( ) gets each value or values, returning an Object containing the simple value or an array of Objects containing the multiple values.

Listing 20-47 shows the code for the getResult( ) method.

Listing 20-47 The getResult( ) method


private Object getResult( int rstcol ) throws OdaException
   {
      Object obj = this.currentRow;
      Object value = null;
      try
         {

         if( qryReturnTypes.length >
            0 && qryReturnTypes[0].isEntityType( ))
         {
            String checkClass =
            (( ResultSetMetaData )getMetaData( ))
               .getColumnClass(rstcol);
            Object myVal =
            HibernateUtil.getHibernatePropVal( obj,
               checkClass,
               getMetaData( ).getColumnName( rstcol ));
            value = myVal;
         }
         else
         {
            if( getMetaData( ).getColumnCount( ) == 1)
            {
            value = obj;
         }
         else
         {
            Object[ ] values = ( Object[ ])obj;
            value = values[rstcol-1];
         }

         }
      }
      catch( Exception e )
      {
         throw new OdaException( e.getLocalizedMessage( ) );
      }

   return( value );
}

Understanding HibernateUtil

HibernateUtil is a utility class that provides the run-time interface between the Hibernate service and the application. The HibernateUtil class example derives from the class provided with the Hibernate documentation. HibernateUtil performs the following operations:

• Initializes the SessionFactory

• Builds the Hibernate SessionFactory

• Opens and closes a session

• Returns information on Hibernate classes and properties

• Registers the JDBC driver with the DriverManager

Connection.open( ) calls HiberFnateUtil.constructSessionFactory( ), which creates a SessionFactory if one does not already exist. The constructSessionFactory( ) method closes and rebuilds the SessionFactory if the location of the configuration file or mapping files directory has changed.

The SessionFactory construction process creates the ClassLoader. The ClassLoader adds the drivers directory in the org.eclipse.birt.report.data.oda.jdbc plug-in and the hibfiles directory in the Hibernate ODA plug-in to classpath. This process also registers the JDBC driver specified in the Hibernate config file with the DriverManager.

The HibernateUtil class implements the following methods:

• initSessionFactory( )

This method creates the SessionFactory object from the configuration settings in the hibernate.cfg.xml file.

Listing 20-48 shows the code for the initSessionFactory( ) method.

Listing 20-48 The initSessionFactory( ) method


private static synchronized void initSessionFactory
   ( String hibfile, String mapdir)
   throws HibernateException
   {

   if( sessionFactory == null)
   {
      Thread thread = Thread.currentThread( );

         try
      {
         oldloader = thread.getContextClassLoader( );
         refreshURLs( );
         ClassLoader changeLoader = new URLClassLoader
            ( ( URL [ ])URLList.toArray
               ( new URL[0]),thread
                  .getContextClassLoader( ));
         thread.setContextClassLoader( changeLoader );
         Configuration cfg =
            buildConfig( hibfile,mapdir );
         Class driverClass =
            changeLoader.loadClass( cfg.getProperty
               ( "connection.driver_class" ));
         Driver driver =
            ( Driver ) driverClass.newInstance( );
            WrappedDriver wd =
               new WrappedDriver( driver,
               cfg.getProperty
               ( "connection.driver_class" ));
            boolean foundDriver = false;
            Enumeration drivers =
               DriverManager.getDrivers( );
      while ( drivers.hasMoreElements( ))
      {
         Driver nextDriver =
            ( Driver )drivers.nextElement( );
         if ( nextDriver.getClass( ) == wd.getClass( ))
         {
            if( nextDriver.toString( )
               .equals(wd.toString( )) )
            {
               foundDriver = true;
               break;
            }
         }
      }
      if( !foundDriver )
      {
         DriverManager.registerDriver( wd );
      }
      sessionFactory = cfg.buildSessionFactory( );
      configuration = cfg;
      HibernateMapDirectory = mapdir;
      HibernateConfigFile = hibfile;
   }
      catch( Exception e)
      {
         e.printStackTrace( );
         throw new HibernateException
            ( "No Session Factory Created " +
               e.getLocalizedMessage( ));
      }
      finally
      {
         thread.setContextClassLoader( oldloader );
      }
   }
}

• constructSessionFactory

This method checks to see if a configuration change occurred. If a change occurred, the method closes the session and SessionFactory and calls the initSessionFactory to rebuild the SessionFactory.

Listing 20-49 shows the code for the constructSessionFactory( ) method.

Listing 20-49 The constructSessionFactory( ) method


public static void constructSessionFactory
   ( String hibfile, String mapdir)
   throws HibernateException
   {
      if( hibfile == null)
      {
         hibfile = "";
      }

      if( mapdir == null)
      {
         mapdir = "";
      }

      if( sessionFactory == null)
      {
         initSessionFactory( hibfile, mapdir);
         return;
      }

      if( HibernateMapDirectory.equalsIgnoreCase
         ( mapdir ) && HibernateConfigFile
            .equalsIgnoreCase( hibfile ))
      {
         return;
      }
      synchronized( sessionFactory )
      {
         Session s = ( Session ) session.get( );

         if ( s != null )
         {
         closeSession( );
         }

         if ( sessionFactory != null &&
            !sessionFactory.isClosed( ))
         {
            closeFactory( );
         }
         sessionFactory = null;
         initSessionFactory( hibfile, mapdir);
}

• currentSession( )

This method opens a session when called by the Connection.open( ) method, as shown in Listing 20-50.

Listing 20-50 The currentSession( ) method


public static Session currentSession( )
   throws HibernateException {
   Session s = ( Session ) session.get( );
   if ( s == null ) {
      s = sessionFactory.openSession( );
      session.set( s );
   }
      return s;
   }

Other methods in this class return information on a particular class and its properties. The getHibernateProp( ) method returns the properties for a class. The getHibernatePropTypes( ) method returns the data type for a property of a class.

Building the Hibernate ODA driver plug-in

To build and deploy the org.eclipse.birt.report.data.oda.hibernate plug-in using the Eclipse PDE Manifest Editor, perform the following tasks:

• In Build, specify the Build Configuration to include the following items:

• In Runtime Information, add the odahibernate.jar file.

• In Binary Build, select the following files and folders:

• META-INF

• exampleconfig

• hibfiles

• lib

• plugin.xml

• In Overview, in Exporting, choose Export Wizard and perform the following tasks:

• In Available Plug-ins and Fragments, select org.eclipse.birt.report.data.oda.hibernate.

• In Options, verify that Package plug-ins as individual JAR archives is not selected.

In Destination, choose the directory, $INSTALL_DIRirt-runtime-2_6_0 Report Engine. The Hibernate ODA example uses MySQL as the database. The BIRT sample database and the MySQL installation scripts can be downloaded from http://www.eclipse.org/birt/db. For information about the required Hibernate libraries, please refer to the Hibernate web site at http://www.hibernate.org.

Developing the Hibernate ODA user interface extension

To use the data retrieved by the Hibernate ODA driver in a BIRT report design, you must extend the DTP design user interface. To implement the Hibernate ODA user interface, you extend the following extension points:

• org.eclipse.datatools.connectivity.oda.design.ui.dataSource

The dataSource extension point defines and implements the user interface for new data source and data set wizards. These wizards use the Hibernate ODA driver plug-in to extend the functionality available in the Data Explorer of BIRT Report Designer.

• org.eclipse.ui.propertyPages

The propertyPages extension displays and manipulates the Hibernate configuration file and mapping files directory locations.

• org.eclipse.datatools.connectivity.connectionProfile

The connectionProfile extension shares a data source connection between applications.

To start developing the Hibernate ODA user interface plug-in, create the plug-in project, org.eclipse.birt.report.data.oda.hibernate.ui.

How to create the Hibernate ODA user interface plug-in project

1 From the Eclipse menu, choose File→New→Project. New Project appears.

2 On New Project—Select a wizard, open Business Intelligence and Reporting Tools and select ODA Designer Plug-in Project. Choose Next. New Plug-in Project appears.

3 In Plug-in Project, modify the settings as shown in Table 20-16. Choose Next. Plug-in Content appears.

Table 20-16 Settings for Hibernate ODA UI Plug-in Project options

image

4 In Plug-in Content, modify the settings as shown in Table 20-17. Choose Finish.

Table 20-17 Settings for Hibernate ODA UI Plug-in Content options

image

5 In Templates, choose ODA Data Source Designer. Choose Next. ODA Data Source Designer appears, as shown in Figure 20-25.

Figure 20-25 Specifying the ODA Data Source Designer template

image

6 In ODA Data Source Designer, specify new values for the following options used to generate the Hibernate ODA user interface plug-in, as shown in Table 20-18. Choose Finish.

Table 20-18 Settings for Hibernate ODA Data Source Designer options

image

The Hibernate ODA user interface plug-in project appears in the Eclipse PDE workbench.

How to specify the Hibernate ODA user interface dependencies

On the Eclipse PDE Manifest Editor, in Dependencies, specify the required plug-ins in the following order:

• org.eclipse.core.runtime

• org.eclipse.ui

• org.eclipse.datatools.connectivity.oda.design.ui

• org.eclipse.birt.report.data.oda.hibernate

How to specify the Hibernate ODA user interface runtime

On Runtime, in Exported Packages, add org.eclipse.birt.report.oda.hibernate.ui to the list of packages that this plug-in exposes to clients.

How to specify the Hibernate ODA user interface extension points

1 On the PDE Manifest Editor, choose Extensions.

2 In All Extensions, choose Add. New Extension appears.

3 On New Extension—Extension Points, in the list of extension points, select the following plug-in:

org.eclipse.datatools.connectivity.oda.design.ui.dataSource

Choose Finish.

4 Repeat steps 2 and 3 to add the following extension points to the list on the Extensions page:

• org.eclipse.ui.propertyPages

• org.eclipse.datatools.connectivity.connectionProfile

How to add the extension details

1 In Extensions, select the extension point, org.eclipse.datatools.connectivity.oda.design.ui.dataSource, and add the following elements and element details:

• dataSourceUI

Add the following id:

org.eclipse.birt.report.data.oda.hibernate

Add the following extension element to dataSourceUI:

newDataSourceWizard

Add the extension element details for the extension element, newDataSourceWizard, as shown in Table 20-19.

Table 20-19 Property settings for the Hibernate newDataSourceWizard

image

• dataSetUI

Add the extension element details for the extension element, dataSetUI, as shown in Table 20-20.

Table 20-20 Property settings for the Hibernate dataSetUI extension element

image

2 In Extensions, select dataSetUI and add the following properties and element details:

• dataSetWizard, as shown in Table 20-21

Table 20-21 Property settings for the Hibernate dataSetWizard extension element

image

• dataSetPage, as shown in Table 20-22

Table 20-22 Property settings for the Hibernate dataSetPage extension element

image

3 In Extensions, select org.eclipse.ui.propertyPages and add the following ODA Hibernate Data Source Connection Properties (page) property and extension element details, as shown in Table 20-23.

Table 20-23 Property settings for the Hibernate page extension element

image

4 In Extensions, select page and add the following filter property and extension element details, as shown in Table 20-24.

Table 20-24 Property settings for the Hibernate filter extension element

image

5 In Extensions, select org.eclipse.datatools.connectivity.connectionProfile, and add the following properties and element details:

• category, as shown in Table 20-25

Table 20-25 Property settings for the Hibernate category extension element

image

• connectionProfile, as shown in Table 20-26

Table 20-26 Property settings for the Hibernate connectionProfile extension element

image

• connectionFactory, as shown in Table 20-27

Table 20-27 Property settings for the Hibernate connectionFactory extension element

image

• newWizard, as shown in Table 20-28

Table 20-28 Property settings for the Hibernate newWizard extension element

image

Understanding the sample Hibernate ODA user interface extension

The following sections describe the code-based extensions a developer must make to complete the development of the Hibernate ODA user interface extension, after defining the plug-in framework in the Eclipse PDE.

The Hibernate ODA user interface plug-in implements the following classes:

• HibernatePropertyPage

Creates and initializes the editor controls for the property page that specify the ODA data source. This class updates the connection profile properties with the values collected from the page. HibernatePropertyPage extends org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSourceEditorPage, the abstract base class for implementing a customized ODA data source property page.

• HibernatePageHelper

Implements the user interface that specifies data source properties. This utility class specifies the page layout, sets up the controls that listen for user input, verifies the location of the Hibernate configuration file, and sets up the location of the mapping directory. The HibernateDataSourceWizard and HibernatePropertyPage classes use HibernatePageHelper. HibernatePageHelper also extends org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSourceEditorPage.

• HibernateDataSourceWizard

Creates and initializes the controls for the data source wizard page. The class sets the configuration file message and collects the property values. In the extension element settings for newDataSourceWizard, the pageClass property specifies this class as the implementation class for the dataSourceUI wizard. The HibernateDataSourceWizard class extends org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSourceWizardPage, the abstract base class for implementing a customized ODA data source wizard page.

• HibernateHqlSelectionPage

Creates the user interface that specifies an HQL statement. The Hibernate ODA user interface plug-in calls HibernateHqlSelectionPage when creating or modifying the data set for a data source. In the extension element settings for dataSetPage, the wizardPageClass property specifies this class as the implementation class for the dataSetUI page wizard. HibernateHqlSelectionPage also extends org.eclipse.datatools.connectivity.oda.design.ui.wizards.DataSourceWizardPage.

• Messages

This class and the related properties file, messages.properties, generate the messages displayed in the Hibernate ODA user interface.

Understanding HibernatePageHelper

This class creates the components that select the Hibernate configuration file and a mapping files directory using the following methods:

• createCustomControl( )

Builds the user interface for the data source

• initCustomControl( )

Sets the initial property values

• collectCustomProperties( )

Returns the modified properties to the ODA framework

When the data source page displays, the Finish button becomes available when the setPageComplete( ) method indicates the page is complete.

HibernateDataSourceWizard.createPageCustomControl( ) and HibernatePropertyPage.createAndInitCustomControl( ) call HibernatePageHelper. The createCustomControl( ) method is the entry point for this class.

Listing 20-51 shows the code for the createCustomControl( ) method.

Listing 20-51 The createCustomControl( ) method


void createCustomControl( Composite parent )
   {
      Composite content = new Composite( parent, SWT.NULL );
      GridLayout layout = new GridLayout( 3, false );
      content.setLayout(layout);
      setupConfigLocation( content );
      setupMapLocation( content );
   }

The setupConfigLocation( ) method sets up the configuration file location. The setupMapLocation( ) method sets up the mapping folder. These two methods perform similar tasks.

Listing 20-52 shows the code for the setupConfigLocation( ) method. This method adds a label, a text entry component, and a button. The text entry component has a ModifyListener( ) method, which verifies that the file selected exists, and the button has a SelectionAdapter( ) method, which uses the FileDialog( ) method to access the configuration file.

Listing 20-52 The setupConfigLocation( ) method


private void setupConfigLocation( Composite composite )
   {
      Label label = new Label( composite, SWT.NONE );
      label.setText("Select Hibernate Config File" );
      GridData data =
         new GridData( GridData.FILL_HORIZONTAL );
      m_configLocation = new Text( composite, SWT.BORDER );
      m_configLocation.setLayoutData( data );
      setPageComplete( true );
      m_configLocation.addModifyListener
         ( new ModifyListener( )
            {
               public void modifyText( ModifyEvent e )
               {
                  verifyConfigLocation( );
               }
            } );
   m_browseConfigButton = new Button( composite, SWT.NONE );
   m_browseConfigButton.setText( "..." );
   m_browseConfigButton.addSelectionListener
      ( new SelectionAdapter( )
      {
         public void widgetSelected( SelectionEvent e )
         {
            FileDialog dialog = new FileDialog
               ( m_configLocation.getShell( ) );
            if( m_configLocation.getText( ) != null &&
               m_configLocation.getText( )
                  .trim( ).length( ) > 0 )
               {
                  dialog.setFilterPath
                     ( m_configLocation.getText() );
               }
               dialog.setText
                  ( "Select Hibernate Config File" );
               String selectedLocation = dialog.open( );
               if( selectedLocation != null )
               {
                  m_configLocation.setText
                     ( selectedLocation );
               }
            }
         } );
   }

The initCustomControl( ) method initializes the properties settings. The plug-in passes the properties to the method from the createPageCustomControl( ) and setInitialProperties( ) methods of the HibernateDataSourceWizard class and the createAndInitCustomControl( ) method of the HibernatePropertyPage class.

The initCustomControl( ) method retrieves the properties for the Hibernate configuration file and mapping files directory and sets text component values.

Listing 20-53 shows the code for the initCustomControl( ) method.

Listing 20-53 The initCustomControl( ) method


void initCustomControl( Properties profileProps )
   {
      setPageComplete( true );
      setMessage( DEFAULT_MESSAGE, IMessageProvider.NONE );
      if( profileProps == null || profileProps.isEmpty() ||
         m_configLocation == null )
         return;
      String configPath =
         profileProps.getProperty( "HIBCONFIG" );
      if( configPath == null )
      configPath = EMPTY_STRING;
      m_configLocation.setText( configPath );
      String mapPath = profileProps.getProperty( "MAPDIR" );
      if( mapPath == null )
         mapPath = EMPTY_STRING;
      m_mapLocation.setText( mapPath );
      verifyConfigLocation( );
   }

When the user presses the Finish or Test Connection button, the plug-in calls the collectCustomProperties( ) method to retrieve the new values for the Hibernate configuration file and mapping files directory. The HibernateDataSourceWizard and HibernatePropertyPage classes call the HibernatePageHelper.collectCustomProperties( ) method from their collectCustomProperties( ) methods.

Listing 20-54 shows the code for the collectCustomProperties( ) method.

Listing 20-54 The collectCustomProperties( ) method


Properties collectCustomProperties( Properties props )
   {
      if( props == null )
         props = new Properties( );
      props.setProperty( "HIBCONFIG",
         getConfig( ) );
      props.setProperty( "MAPDIR", getMapDir( ) );
      return props;
   }

Understanding HibernateDataSourceWizard

The HibernateDataSourceWizard class extends the DTP DataSourceWizardPage by implementing three methods that the ODA framework calls:

• createPageCustomControl( )

Constructs the user interface

• setInitialProperties( )

Sets the initial values of the user interface

• collectCustomProperties( )

Retrieve the modified values

This class creates the HibernatePageHelper class, and uses the methods described earlier to handle these three methods. The ODA framework uses this class to create a new data source.

Understanding HibernatePropertyPage

The HibernatePropertyPage class extends the DTP DataSourceEditorPage by implementing two methods that the ODA framework calls:

• createAndInitCustomControl( )

Constructs the user interface and sets the initial values

• collectCustomProperties( )

Retrieves the modified values

This class creates the HibernatePageHelper class and uses the methods described earlier to handle these two methods. The ODA framework uses this class to create a new data source.

Understanding HibernateHqlSelectionPage

The HibernateHqlSelectionPage class extends DataSetWizardPage to define the page controls and related functionality for the Hibernate ODA data set wizard. HibernateHqlSelectionPage allows the user to create an HQL statement that selects the data set and embeds the HQL statement in the report design. This page links to the Hibernate ODA through the wizardPageClass attribute of the dataSetPage element within the dataSource extension.

The HibernateHqlSelectionPage class implements the following methods:

• createPageControl( )

This method performs the following operations:

• Sets up a composite set of controls using a series of GridLayout and GridData objects to create the data set editor user interface

• Sets the user prompt to enter an HQL statement and verify the query

• Adds a text control to allow the user to enter and modify text

• Adds a ModifyListener to the text control to detect user input

• Sets up the Verify Query button and adds a SelectionListener to detect when the user selects the button

• Returns the composite page control

Listing 20-55 shows the code for the createPageControl( ) method.

Listing 20-55 The createPageControl( ) method


public Control createPageControl( Composite parent )
   {
      Composite composite = new Composite
      ( parent, SWT.NONE );
   GridLayout layout = new GridLayout( );
   layout.numColumns = 1;
   composite.setLayout( layout );
   Label label = new Label( composite, SWT.NONE );
   label.setText( Messages.getString
      ( "wizard.title.selectColumns" ));
   GridData data = new GridData( GridData.FILL_BOTH );
   queryText = new Text( composite,SWT.MULTI |
      SWT.WRAP | SWT.V_SCROLL );
   queryText.setLayoutData( data );

   queryText.addModifyListener( new ModifyListener( )
   {
      public void modifyText( ModifyEvent e )
      {
         if( m_initialized == false)
         {
            setPageComplete(true);
            m_initialized = true;
         }
         else
         {
            setPageComplete(false);
         }
         }
   } );
   setPageComplete( false );
   Composite cBottom = new Composite
      ( composite, SWT.NONE );
   cBottom.setLayoutData
      ( new GridData( GridData.FILL_HORIZONTAL ) );
   cBottom.setLayout( new RowLayout( ) );
   queryButton = new Button( cBottom, SWT.NONE );
   queryButton.setText(Messages.getString
      ( "wizard.title.verify" ));
   queryButton.addSelectionListener
         ( new SelectionAdapter( ))
      {
      public void widgetSelected( SelectionEvent event )
      {
                 verifyQuery( );
            }
      } );
      return composite;
         }

• initializeControl( )

The plug-in calls this method to retrieve the HQL query from the current design and initializes the HQL text component with this value. initializeControl( ) also reads the Hibernate configuration file and mapping files directory from the report design and stores them in member variables for use when building a query.

Listing 20-56 shows the code for the initializeControl( ) method.

Listing 20-56 The initializeControl( ) method


private void initializeControl( )
{
   Properties dataSourceProps =
      getInitializationDesign( ).getDataSourceDesign( )
         .getPublicProperties( );

   m_hibconfig =
            dataSourceProps.getProperty( "HIBCONFIG" );
   m_mapdir = dataSourceProps.getProperty( "MAPDIR" );

   DataSetDesign dataSetDesign =
         getInitializationDesign( );
   if( dataSetDesign == null )
         return;

   String queryTextTmp = dataSetDesign.getQueryText( );
   if( queryTextTmp == null )
      return;

   queryText.setText( queryTextTmp );
   this.m_initialized = false;
   setMessage( "", NONE );
}

• verifyQuery( )

This method is the selection event called when the user chooses the Verify Query button. verifyQuery performs the following operations:

• Opens a connection to the run-time environment.

• Instantiates a Query object and gets the query text entered by the user.

• Prepares the query.

• Checks the column to determine if the query prepare was successful. Depending on the success of the query prepare, verifyQuery( ) indicates that page processing is complete or incomplete.

• Re-enables the Verify Query button.

• Closes the connection.

Listing 20-57 shows the code for the verifyQuery( ) method.

Listing 20-57 The verifyQuery( ) method


boolean verifyQuery( )
{
   setMessage( "Verifying Query", INFORMATION );
   setPageComplete( false );
   queryButton.setEnabled( false );
   Connection conn = new Connection( );
   try
   {
      Properties prop = new Properties( );
      if( m_hibconfig == null)m_hibconfig = "";
      if( m_mapdir == null)m_mapdir = "";
      prop.put("HIBCONFIG", m_hibconfig );
         prop.put("MAPDIR", m_mapdir);
      conn.open( prop );
      IQuery query = conn.newQuery( "" );
      query.prepare( queryText.getText( ) );
      int columnCount =
         query.getMetaData( ).getColumnCount( );
      if ( columnCount == 0 )
      {
         setPageComplete( false );
         return false;
      }
      setPageComplete( true );
      return true;
   }
   catch ( OdaException e )
   {
      System.out.println( e.getMessage( ) );
      showError( "ODA Verify Exception", e.getMessage( ) );
      setPageComplete( false );
      return false
   }
   catch ( Exception e )
   {
      System.out.println( e.getMessage( ) );
      showError( "Verify Exception", e.getMessage( ) );
      setPageComplete( false );
      return false;
   }
   finally
   {
      try
            {
               queryButton.setEnabled( true );
               conn.close( );
            }
            catch ( OdaException e )
            {
               System.out.println( e.getMessage( ) );
               setMessage( e.getLocalizedMessage( ),
               ERROR );e.getMessage( ) );
               setPageComplete( false );
               return false;
         }
      }
   }

• savePage( )

The savePage( ) method is called when the ODA framework calls the collectDataSetDesign( ) method. This action occurs when the user presses the Finish button on the new data set wizard or the OK button on the data set editor is pressed. The savePage( ) method saves the query to the report, as shown in Listing 20-58.

Listing 20-58 The savePage( ) method


private boolean savePage( )
{
   IConnection conn = null;
   try
   {
      IDriver hqDriver = new HibernateDriver( );
      conn = hqDriver.getConnection( null );
      IResultSetMetaData metadata =
         getResultSetMetaData( dataSetDesign
            .getQueryText( ), conn );
      setResultSetMetaData( dataSetDesign, metadata );
   }
   catch( OdaException e )
   {
      dataSetDesign.setResultSets( null );
   }
   finally
   {
      closeConnection( conn );
   }
}

• getResultSetMetaData( )

The savePage( ) method calls the getResultSetMetaData( ) method when saving the report design. This method retrieves the query metadata that setResultSetMetaData( ) uses to create the data set columns.

Listing 20-59 shows the code for the getResultSetMetaData( ) method.

Listing 20-59 The getResultSetMetaData( ) method


private IResultSetMetaData getResultSetMetaData
   ( String queryText, IConnection conn )
   throws OdaException
{
         java.util.Properties prop =
            new java.util.Properties( );

         if( m_hibconfig == null) m_hibconfig = "";
         if( m_mapdir == null) m_mapdir = "";

         prop.put( "HIBCONFIG", m_hibconfig );
         prop.put( "MAPDIR", m_mapdir );
         conSn.open( prop );

         IQuery query = conn.newQuery( null );
         query.prepare( queryText );
         return query.getMetaData( );
}

• setResultSetMetaData( )

The savePage( ) method calls the setResultSetMetaData( ) method when saving the report design. This method uses the DataSetDesign and the ResultSetMetaData objects for the query to create the columns in the data set for use in the report design.

Listing 20-60 shows the code for the setResultSetMetaData( ) method.

Listing 20-60 The setResultSetMetaData( ) method


private void setResultSetMetaData
   ( DataSetDesign dataSetDesign,
      IResultSetMetaData md ) throws OdaException
{
ResultSetColumns columns =
      DesignSessionUtil.toResultSetColumnsDesign( md );
   ResultSetDefinition resultSetDefn =
      DesignFactory.eINSTANCE
      .createResultSetDefinition( );

   resultSetDefn.setResultSetColumns( columns );
   dataSetDesign.setPrimaryResultSet( resultSetDefn );
   dataSetDesign.getResultSets().setDerivedMetaData( true );
}

• collectDataSetDesign( )

The plug-in calls this method when creating or modifying the query finishes. The plug-in passes the current design to this method. collectDataSetDesign( ) then verifies that a query exists and sets the design query to the value of the query text. The savePage( ) method saves the design and creates the columns in the data set.

Listing 20-61 shows the code for the collectDataSetDesign( ) method.

Listing 20-61 The collectDataSetDesign( ) method


protected DataSetDesign collectDataSetDesign
   ( DataSetDesign design )
{
if( ! hasValidData( ) )
      return design;
   design.setQueryText( queryText.getText( ) );
   savePage( design );
   return design;
}

Building the Hibernate ODA user interface plug-in

To build the org.eclipse.birt.report.data.oda.hibernate.ui plug-in using the Eclipse PDE Manifest Editor, perform the following tasks:

• In Build, specify the Build Configuration to include the following items:

• In Runtime Information, add the hibernateodaui.jar file.

• In Binary Build, select the following files and folders:

• META-INF

• plugin.xml

Build Configuration appears, as shown in Figure 20-26.

Figure 20-26 Build Configuration settings

image

Testing the Hibernate ODA user interface plug-in

To test the Hibernate ODA user interface plug-in, after exporting it, use a run-time configuration of the Eclipse PDE workbench.

How to launch the Hibernate ODA user interface plug-in

1 From the Eclipse SDK menu, choose Run—Run Configurations. On Run Configurations, right-click Eclipse Application. Choose New.

2 Create a configuration to launch an Eclipse application by performing the following tasks:

1 In Name, type:

Hibernate ODA Test

2 On Main, in Location, type:

C:TestHibernate

Run Configurations appears as shown in Figure 20-27.

Figure 20-27 Creating a configuration to launch an Eclipse application

image

3 Add the Hibernate ODA plug-ins to the launch configuration.

1 In Run Configurations, choose Plug-ins.

2 In Launch with, choose

plug-ins selected below only

from the drop-down list as shown in Figure 20-28.

Figure 20-28 Adding Hibernate ODA plug-ins to launch configuration

image

3 In Plug-ins, check the following plug-ins:

org.eclipse.birt.data.oda.hibernate
org.eclipse.birt.data.oda.hibernate.ui

4 Choose Run to launch the run-time workbench.

5 In the run-time workbench, choose the Report Design perspective.

6 In Report Design, create a new report project and create a new blank report.

How to specify a data source and data set

1 In Report Design, choose Data Explorer. Data Explorer appears.

2 In Data Explorer, right-click Data Sources and choose New Data Source, as shown in Figure 20-29. New Data Source appears.

Figure 20-29 Choosing New Data Source

image

In New Data Source, choose Create from a data source type in the following list and select Hibernate Data Source as the data source type, as shown in Figure 20-30. Choose Next.

Figure 20-30 Selecting Hibernate Data Source

image

Hibernate Data Source appears, as shown in Figure 20-31. In Hibernate Data Source, select the Hibernate configuration file and mapping directory or leave these items blank if you use the hibfiles directory. Choose Finish.

Figure 20-31 Configuring the Hibernate Data Source

image

Data Explorer appears with the new data source in Data Sources, as shown in Figure 20-32.

Figure 20-32 New data source in Data Explorer

image

3 In Data Explorer, right-click Data Sets and choose New Data Set, as shown in Figure 20-33.

Figure 20-33 Choosing New Data Set

image

New Data Set appears, as shown in Figure 20-34.

Figure 20-34 New Data Set

image

Choose Next. Hibernate Data Set appears.

4 In Edit Data Set, perform the following tasks:

1 In Enter HQL and Verify Query, type:

select ord.orderNumber, cus.customerNumber,
         cus.customerName
from Orders as ord, Customer as cus
where  ord.customerNumber = cus.customerNumber
and cus.customerNumber = 363

Edit Data Set displays the query, as shown in Figure 20-35.

Figure 20-35 Editing the HQL query

image

2 Choose Verify Query.

3 Choose Finish. Edit Data Set appears. Choose Preview Results. Preview Results appears as shown in Figure 20-36.

Figure 20-36 Previewing the data set

image

Choose OK. Data Explorer appears.

5 In Data Explorer, expand Data Sets. The new data set lists three columns, as shown in Figure 20-37.

Figure 20-37 Data set in Data Explorer

image

6 To build a report that uses the data set, perform the following tasks:

1 In Data Explorer, drag Data Set to the layout editor. The layout appears, as shown in Figure 20-38.

Figure 20-38 Report design in the layout editor

image

2 To view the output for new_report_1.rptdesign, choose Preview. The Preview, appears as shown in Figure 20-39.

Figure 20-39 Preview of the report design

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset