Creating data provider extensions

A data provider extension is used to enrich attributes within the business model. An extension is a data provider class that defines how the attribute is enriched.

Before you begin

Before you create a data provider extension, you must create an extension project to store and package the extension, and define the data providers in the business model.

About this task

Use a data provider extension to enrich entities from an external source. An extension can be useful if the data that you need is not in the incoming event or in the object grid. You must define the data provider in your business object model definitions, and then use the definitions to enrich the attributes of entities in your business object model. For more information, see Business model definitions.

In your extension implementation, you must send a request to retrieve the relevant information. An agent can reference the enriched attributes in the same way that it refers to non-enriched attributes. The runtime resolves and loads the enriched data when it is needed by using the specified provider in the business model statements.

Note: Manage any resources and the lifecycle resources associated with those resources within the scope of this provider implementation. For example, notice in the example that the code closes the database connection to prevent a connection pool from running out of connections.

A data provider is associated with a request and response type. The generated names for the request and response use the provider name as a prefix. To use a data provider that is defined in the Definitions tab of the BMD editor, you must create one or more enrichment statements in the Statements tab.

The statements define the mapping between the entity that is enriched and the values that are returned by the data provider. When an enrichment statement is defined, an error appears on the solution project until a data provider implementation is created. The names of the data provider elements must match those defined in the Definitions tab.

Procedure

  1. Click File > New > Other > Insight Designer > Data Provider Extension, and then click Next.

    The Data Provider Extension wizard is displayed.

  2. From the menu, select the Extension Project in which you want to store the data provider extension.
  3. Enter a package name. The package name must comply with the Java™ package naming conventions. For example, com.example.mypackage.
  4. Enter a Java class name. By convention, Java class names start with an uppercase letter.
  5. From the Data Provider Interface menu, select an interface name. The interface names are generated from your business model statements.
  6. In the response cache timeout field, enter a value in seconds. The maximum response cache timeout can be expressed in values of 0 or more.
  7. Click Finish.

    The generated data provider class opens in Eclipse.

Example

The following Java code shows a data provider extension. The java.naming and java.sql must be added to the extension project bundle manifest. The example uses the customer ID from the request to query an SQL database. It assumes that the server configuration includes a data source for the database that is bound to the JNDI name "jdbc/CustomerProviderDB".

package com.example.extensions.provider;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.ibm.ia.common.ComponentException;
import com.ibm.ia.extension.DataProvider;
import com.ibm.ia.extension.annotations.DataProviderDescriptor;

import com.example.providers.CustomerProvider;
import com.example.providers.CustomerProviderRequest;
import com.example.providers.CustomerProviderResponse;
import com.example.providers.ConceptFactory;

@DataProviderDescriptor(dataProvider = CustomerProvider.class,
                        responseCacheTimeout = 300)
public class CustomerDataProvider
        extends DataProvider<CustomerProviderRequest, CustomerProviderResponse>
        implements CustomerProvider {
    
    // Data source JNDI name
    private static final String DATA_SOURCE_JNDI = "jdbc/CustomerProviderDB";

    // Select SQL from DB
    private final static String SELECT_CUSTOMER =
            "SELECT FIRST_NAME, LAST_NAME FROM CUSTOMER WHERE CUSTOMER_ID = ?";
    
    // Cached reference to the DataSource
    private ThreadLocal<DataSource> dataSource = new 
				ThreadLocal<DataSource>();

    @Override
    public CustomerProviderResponse processRequest(CustomerProviderRequest request)
            throws ComponentException {
        
        Connection conn = null;
        try {
            // Lookup the DataSource for the database if not already cached
            if (dataSource.get() == null) {
                Context initialContext = new InitialContext();
                dataSource.set(
                        (javax.sql.DataSource) initialContext
                            .lookup(DATA_SOURCE_JNDI));
            }

            // Make a connection to the database
            conn = dataSource.get().getConnection();

            // Prepare the statement to retrieve the data from the database
            PreparedStatement prepSt = conn.prepareStatement(SELECT_CUSTOMER);

            // Get customer ID from the request
            String customerId = request.getCustomerId();
            prepSt.setString(1, customerId);

            // Execute the query
            ResultSet resultSet = prepSt.executeQuery();

            // Read the first row from the result set
            if (resultSet.next()) {
                
                // Create a response object
                ConceptFactory factory = getConceptFactory(ConceptFactory.class);
                CustomerProviderResponse response =
                        factory.createCustomerProviderResponse();
                
                // Set the response fields
                response.setFirstName(resultSet.getString(1));
                response.setLastName(resultSet.getString(2));
                
                // Return the response
                return response;
                
            } else {
                throw new ComponentException(
                        "CustomerDataProvider: No result for " + customerId);
            }

        } catch (NamingException | SQLException e) {    
            throw new ComponentException(
                    "CustomerDataProvider: " + e.getLocalizedMessage(), e);
        } finally {
            // Always try close the connection
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e2) {
                    // Assume it's closed
                }
            }
        }
    }
}

The previous example also assumes that the following data is defined in the BMD and has the package name com.example.providers.

The names that you use to reference request and response data in the Java implementation are mapped directly to the definitions in the BMD. For example, the following definition returns a given name ("first name") and a surname ("last name"):

a CustomerProvider is a data provider ,
        accepts a customer id,
        returns a first name, a last name.

The implementation uses these names in the response fields:

response.setFirstName(resultSet.getString(1));
response.setLastName(resultSet.getString(2));

Input values can be derived from values in the entity, and output values can be derived from both the entity and the results of the data provider execution.

What to do next

Complete the Java file with the information necessary to connect to your external data source and return a value.

Deploy the Extension Project to the server as part of the solution. Entity values that are enriched by a data provider are cached in the memory of the local partition to improve performance the next time they are queried. The response cache timeout that you specified in the wizard is the longest time that a value is stored.

The server grid configuration might cause the value to be released earlier. Verify the timeToLive value in the objectgrid.xml file to see how long a value is held in the map.