Chapter 11. Using Scripting to Access Data

BIRT supports accessing data using JavaScript code. This type of data source is called a scripted data source. Using a scripted data source, you can access objects other than the built-in data source types.

Because the JavaScript code for accessing and managing a scripted data source can wrap Java objects, a scripted data source can access an EJB, an XML stream, a Hibernate object, or any other Java object that retrieves data. A scripted data source must return data in tabular format, so that BIRT can perform sorting, aggregation, and grouping.

Using a Scripted Data Source

Creating a scripted data source and a non-scripted data source are similar tasks. The differences between creating a scripted data source and a non-scripted data source are:

• The scripted data source must be selected from the list of data source types when creating a scripted data source.

• Two event handler methods, open( ) and close( ), are available only for a scripted data source.

Every scripted data source must have at least one scripted data set. The differences between creating a scripted data set and a non-scripted data set are:

• The scripted data set must be associated with a scripted data source.

• The code for the scripted data set fetch( ) event handler method must be provided.

• There is a different dialog for identifying the columns of a scripted data set.

When using BIRT Report Designer to create a scripted data source, the following tasks must be performed:

• Create a scripted data source.

Right-click on Data Sources in Data Explorer and select Scripted Data Source in the list of data source types.

• Create a scripted data set.

Right-click on Data Sets in Data Explorer and select a scripted data source from the list of available data sources.

• Define output columns.

Define the names and types of output columns, using the scripted data set editor.

• Supply code for the data source open( ) and close( ) methods.

There are two scripted data source event handler methods, open( ) and close( ). It is not mandatory to implement either method, but most applications require the use of the open( ) method to initialize a data source. Typically, you create a Java object for accessing the data source in the open( ) method.

Use the close( ) method to clean up any loose ends, including setting object references to null to ensure that the objects are deleted during garbage collection.

• Supply code for the data set methods.

There are three scripted data set event handler methods, open( ), fetch( ), and close( ). Implementing the fetch( ) method is mandatory to initialize variables and to prepare the data source for fetching data.

Use the fetch( ) method to get a row of data from the data source and to populate the columns of the row object. The fetch( ) method must return either true or false. A true value tells BIRT that there is another row to process. A false return value signifies that there are no more rows to process.

Use the close( ) method to perform cleanup operations.

• Place the columns on the report layout.

Place a data set column on a report layout the same way you place a column for a non-scripted data set.

The following tutorial guides you through the procedure required to perform each task in this process.

Tutorial 2: Creating a scripted data source

This tutorial provides instructions for the process of creating and scripting a simulated scripted data source.

In this tutorial, you perform the following tasks:

• Create a new report design.

• Create a scripted data source.

• Create a scripted data set.

• Write the open( ) and close( ) methods of the data source.

• Write the open( ) method of the data set.

• Write the fetch( ) method of the data set.

• Place the columns on the report layout.

Task 1: Create a new report design

In this task, you create a new report in BIRT Report Designer and name it ScriptedDataSrc.rptdesign.

1 Choose File→New→Report.

2 In File Name in New Report, type:

ScriptedDataSrc.rptdesign

3 In Enter or Select the Parent Folder, accept the default folder. Choose Next.

4 In Report Templates, select My First Report. Choose Finish. The BIRT report design screen appears. If a Cheat Sheet tab appears, close it.

Task 2: Create a scripted data source

In this task you create the new data source.

1 In Data Explorer, right-click Data Sources and choose New Data Source. Select a Data Source type appears.

2 In New Data Source, select Scripted Data Source.

3 In Data Source Name, type:

ScriptedDataSource

4 Choose Finish.

Data Explorer and the code window for ScriptedDataSource appear, as shown in Figure 11-1.

Figure 11-1 Data Explorer and ScriptedDataSource code window

image

Task 3: Create a scripted data set

In this task, you create the new data set.

1 In Data Explorer, right-click Data Sets. Choose New Data Set. New Data Set appears, as shown in Figure 11-2.

Figure 11-2 New data set for a scripted data source

image

2 In Data Set Name, replace the default name by typing:

ScriptedDataSet

3 Choose Next. Output columns appears, as shown in Figure 11-3.

Figure 11-3 Preparing to define the output columns

image

4 In Output columns choose Add to define the first column for the data set. New Script Data Set Column appears.

5 In the Column Name, type:

col1

6 In Data Type select Integer from the drop-down list. New Script Data Set Column now contains the definition of one output column, as shown in Figure 11-4.

Figure 11-4 Defining the first output column

image

7 Choose OK. The col1 column definition appears in the Output columns list.

8 Using the same procedure create the definitions for col2 and col3. These data types are respectively String and Float. When you finish, the column definitions should look the ones shown in Figure 11-5.

Figure 11-5 Completed column definitions

image

9 In Data Explorer, select ScriptedDataSet. The script window for the data set appears, including the column definitions, as shown in Figure 11-6.

Figure 11-6 Code window for ScriptedDataSet

image

Task 4: Write the open( ) and close( ) methods of the data source

Use the open( ) method to open the data source. Use the close( ) method to do cleanup tasks. Typically, you need to place some code in these methods. The open( ) method is the default selected method upon creating a data set.

1 Select open from the pull-down list of methods.

2 Type the following code into the code window for the open( ) method:

dummyObject = new Object( );

The previous example code is placeholder code for this simplified example. In a typical application, you use this method to initialize a Java object that provides access to the data for the report.

3 Select close from the pull-down list of methods.

4 Type the following code into the code window for the close method:

dummyObject = null;

Task 5: Write the open( ) method of the data set

When you create the data set, the open( ) method is selected by default. Use the open( ) method of the data set to do initialization, such as defining a counter and setting it to zero.

1 Select open from the pull-down list of methods.

2 Type the following code into the code window:

recordCount = 0;

Task 6: Write the fetch( ) method of the data set

Use the fetch( ) method to process row data. The fetch( ) method must return either true or false. Fetch( ) returns true to indicate that there is a row to process. Fetch( ) returns false to indicate that there are no more rows to process. The fetch( ) method also calculates the values of computed fields. The report only has column headings at this point. To include data, you must add code to the fetch( ) method.

1 Choose the Script tab.

2 Select ScriptedDataSet in Data Explorer. Select fetch in the drop-down list of methods.

3 Type the following code into the code window. This code limits the number of rows that appear in the report to 19.

if(recordCount < 20) {
   recordCount++;
   row.col1 = recordCount;
      row["col2"] = "Count = " + recordCount;
      row[3] = recordCount * 0.5;
      return true;
   }
else return false;

Task 7: Place the columns on the report layout

You place columns for a scripted data set in the same way as for a nonscripted data set.

1 On ScriptedDataSrc.rptdesign, select Layout.

2 In Data Explorer, select ScriptedDataSet and drag the data set into the report layout.

Figure 11-7 shows the three columns of the data set in the layout editor.

Figure 11-7 New columns in the report design

image

3 Choose Preview.

The preview of the report appears, as shown in Figure 11-8.

Figure 11-8 Report preview, showing the new columns

image

4 Choose Preview. The report now contains 20 rows and 3 columns of data. Rows 1-6 of the report preview are shown in Figure 11-9.

Figure 11-9 Report preview, rows 1 through 6

image

5 Rows 7 through 20 of the report preview are shown in Figure 11-10.

Figure 11-10 Report preview, rows 7 through 20

image

Writing the scripted data set in Java

You can also implement this example in Java. Setup the Java project in the same workspace as the BIRT report project. In the report project, repeat the previous tasks, omitting tasks 4, 5, and 7.

In the Java project, add the Java class file in Listing 11-1. Finally, in the report project, select the scripted data set and set the event handler class property to the class in Listing 11-1.

Listing 11-1 MyScriptedDataSet.java


import org.eclipse.birt.report.engine.api.script
   .IScriptedDataSetMetaData;
import org.eclipse.birt.report.engine.api.script
   .IUpdatableDataSetRow;
import org.eclipse.birt.report.engine.api.script.eventadapter
   .ScriptedDataSetEventAdapter;
import org.eclipse.birt.report.engine.api.script.instance
   .IDataSetInstance;
public class MyScriptedDataSet extends ScriptedDataSetEventAdapter
   {
   public int recordCount = 0;
   @Override
   public boolean fetch( IDataSetInstance dataSet,
      IUpdatableDataSetRow row ) {
      try{
         if( recordCount < 20) {
            recordCount++;
            row.setColumnValue( "col1", recordCount );
            row.setColumnValue( "col2", "Count =
               " + recordCount );
            row.setColumnValue( "col3", recordCount*.05 );
            return true;
         }else{
            return false;
         }
      }catch( Exception e ){
         e.printStackTrace( );
         return false;
      }
   }
   @Override
   public void open( IDataSetInstance dataSet ) {
      recordCount = 0;
   }
}

Selecting Browse for the event handler property displays the scripted data set class. This example shows how to implement the scripted data set in Java. You can implement a scripted data source in a similar way.

In this example, the recordCount is stored as a global variable of the MyScriptedDataSet object. Using the global variable in this way is only valid for the data source or data set event handlers and should not be used when extending other event adapters. All other event adapters create a new instance of the event handler class for each instance of a report item. For example, when extending the RowEventAdapter, a new instance of the extending class is created for each row that uses the extended adapter.

Using a Java object to access a data source

A common use of a scripted data set is to access a Java object that accesses or generates the data for a report. This section shows how to access a Java class in the JavaScript code for a scripted data set.

Performing initialization in the data set open( ) method

Use the data set open( ) method to perform initialization tasks. A typical initialization task is to get an instance of the Java object that provides the data for the report.

When referring to a Java object, first import its package into the JavaScript environment. For example, the following code imports the package com.yourCompany.yourApplication:

importPackage( Packages.com.yourCompany.yourApplication );

This statement is like the import statement in Java and allows you to omit the package name when referencing a class. This statement is normally the first line in the open( ) method. You typically follow the importPackage statement with code to create the Java object instance, as shown in the following code:

var myList = MyListFactory.getList( );

A typical way of getting rows of data from a Java object is to use an iterator object. The open( ) method is the proper place to create an iterator object. For example, the following statement gets an iterator from myList:

var iterator = myList.getIterator( );

Getting a new row of data in the data set fetch( ) method

Once you have a way to get rows of data from your Java object, use the fetch( ) method to call the Java method that returns the rows. The fetch( ) method determines if there are any more rows of data and returns false if there are none, as shown in the following code:

if( iterator.hasNext( ) == false ){
   return false;
}

At this point, the fetch( ) method can populate a row with the data that it gets from the iterator, as shown in the following code:

var node = iterator.next( );
row[1] = node.getFirstCol( );
row[2] = node.getSecondCol( );
row[3] = node.getThirdCol( );

You must return true to signal BIRT that there is a valid row of data to process, as shown in the following code:

return true;

Cleaning up in the data set close( ) method

You can perform any cleanup in the close( ) method. This method is a good place to set to null any objects that you created. For example, the following code sets three object references to null:

myList = null;
iterator = null;
node = null;

Deciding where to place your Java class

If a scripted data source uses a custom Java class, that class must reside in a location where BIRT can find it. BIRT can find the Java class if its location meets any of the following requirements:

• The Java class is in the classpath of the Java Runtime Environment (JRE) under which Eclipse runs.

Consider using this option if your Java class is in this location for other reasons.

• The Java class is in <ECLIPSE_INSTALL>pluginsorg.eclipse.birt.report .viewerirtWEB-INFlib.

Consider using this option if your Java class is built, tested, and ready to deploy.

• The Java class is a part of an Eclipse Java project that is in the same workspace as the BIRT report project.

Consider using this option if you are developing your Java class simultaneously with developing your BIRT report.

Deploying your Java class

Before you deploy your BIRT report to an application server, you must place your Java class in a JAR file. You must then deploy that JAR file to the proper location on the application server, so that the BIRT report viewer can find it at run time.

Using input and output parameters with a scripted data set

The scripted data set JavaScript event handler methods have two arrays you can use to access parameters, inputParams and outputParams. The inputParams array contains one string for every parameter defined as input. The outputParams array contains one string for every parameter whose direction is defined as output.

For example, assume that you have a scripted data set with an input and an output parameter, as shown in Figure 11-11.

Figure 11-11 A scripted data set, with input and output parameters

image

You can get and set the values of the out_msg and in_count parameters by using the inputParams and outputParams arrays as in the following example:

outputParams[ "out_msg" ] = "Total rows: " +
   inputParams[ "in_count"];

You can access a parameter in the array either by the name of the parameter or by a 1-based index value. The inputParams and outputParams arrays are not accessible to Java event handlers.

Creating a web services data source using a custom connection class

As stated earlier, you can use a custom connection class to create a web services data source. The custom connection class is responsible for returning an Input Stream that contains a SOAP XML response. This class must implement a connect( ) method. This method has to return an Object, implementing a executeQuery( ) and disconnect( ) method.

The connect( ) method accepts two parameters, which contain the connection properties and application context.

BIRT uses an application context map to store values and objects for use in all phases of report generation and presentation. You can reference objects in the application context from Script, the Expression Builder, in the ODA layer, and so forth. The application context map contains specific name value pairs that are passed to all generation and rendering processes.

You can use the application context to pass a security identifier that can be validated in the connection class and passed in as part of the SOAP request. In many cases the web services require such identifiers.

The Connection Class

If the connectionClass public property of the data source is set to a non-empty string, the run-time driver uses a custom connection class to create connections to the web service. The custom connection class is also responsible for executing the web services queries for web services data sets associated with this data source.

The connection class property is the fully qualified name of a Java class, which must implement the following class method to establish a web services connection. The Java class must be in the application classpath.

public static Object connect(
      java.util.Map connectionProperties,
      java.util.Map appContext );

The connectionProperties parameter specifies the run-time values of all public connection properties available to the driver as a (String, String) map keyed by the connection property name. The map may contain any or all of the following map keys: soapEndPoint, connectionTimeOut, connectionClass, OdaConnProfileName, and OdaConnProfileStorePath.

The appContext parameter provides all the application context values as (String, Object) pairs. Its value is never null, but its collection may be empty.

The connection Instance

The connect(...) method of the custom driver class, after establishing a successful connection, returns a non-null object which implements the following two methods.

public Object executeQuery(
     java.lang.String queryText,
     java.util.Map parameterValues,
     java.util.Map queryProperties
);

public void disconnect( );

The executeQuery( ) Method

The queryText parameter specifies the query text. It can be null if the connection class does not require the report design to provide a query text.

The parameterValues parameter specifies values of all the data set parameters as a (String, Object) map keyed by parameter name. It can be null if the data set does not define any parameters.

The queryProperties parameter specifies values of all the data set public properties as a (String, String) map keyed by property name. The map can contain the queryTimeOut map key.

The query method must return a value of either of the following data types:

• java.lang.String

The returned String is the complete SOAP response.

• java.io.InputStream

The returned stream is SOAP response stream.

The disconnect( ) method

This method closes the connection. The driver implementation of the disconnect method is optional. If it is implemented, BIRT calls this method when the associated data source closes.

Method calling and error handling

The driver calls the custom driver class using Java reflection. The class and connection object do not need to implement any predefined interface. Any exception thrown by any of the defined methods is treated as an error and results in a failure and a thrown ODA Exception.

Custom connection class example

Listing 11-2 is an example of a custom class that accesses a set of connection properties, then instantiates a query object.

Listing 11-2 Custom Connection class


import java.util.Iterator;
import java.util.Map;
public class MyConnectionClass {
   public static Object connect( Map connProperties,
      Map appContext )
   {
      Iterator it = connProperties.keySet( ).iterator( );
       while ( it.hasNext( ) ) {
           Object key = it.next( );
       }
      it = connProperties.values( ).iterator( );
       while ( it.hasNext( ) ) {
           // Get value
           Object value = it.next( );
       }
      MyWSQuery msg = new MyWSQuery( );
      return msg;
   }
}

Listing 11-3 is an example of a query class implementation that executes a query by opening a file input stream and disconnects, closing the file.

Listing 11-3 Query class implementation


import java.io.FileInputStream;
import java.util.Map;
   public class MyWSQuery {
      public Object executeQuery( String queryText,
         Map parameterValues, Map queryProperties )
      {
         FileInputStream fis = null;
         try {
            fis = new FileInputStream( "c:/ExchangeRates.xml" );
         } catch ( Exception e ) {

         }
         return fis;
      }
      public void disconnect( )
      {
         If( fis != null )
         {
            fis.close( );
            fis = null;
         }
      }
   }

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

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