![]() ![]() ![]() ![]() ![]() ![]() |
This chapter discusses the Data Services Mediator API, a Java API for invoking data service operations from Java applications. This chapter explains in detail how to use the Mediator API and includes working sample applications to help you get started.
Using the Mediator API is one of several techniques for invoking data services from client applications. See Chapter 1, “Introducing Data Services for Client Applications” for a summary of these techniques.
This chapter includes these topics:
The Mediator API is the Java API for retrieving Service Data Object (SDO) artifacts from a data service and returning them to their source. In your Java client, you call Mediator API methods to connect to a data service, invoke data service methods, and send updated data objects back to the server. You use SDO API methods to manipulate the data objects within your Java client.
For example, you might call a Mediator API method getAllCustomers() to retrieve a collection of customer data objects from the data service. Then, you could call an SDO method such as setCustomerName() to modify a customer object. Finally, you might call another Mediator API method, such as updateCustomers() to return the modified data object to the data source on the server.
Topics in this section include:
The Java programming model provided by Oracle Data Service Integrator for invoking data service operations is based on Service Data Objects (SDO). SDO, a specification proposed jointly by Oracle, IBM, SAP, and others, is a Java-based architecture and API for data programming. Oracle Data Service Integrator lets programmers uniformly access data objects from heterogeneous data sources, including relational databases, XML data sources, web services, and enterprise information systems.
Tip: | See “Introducing Service Data Objects (SDO)” on page 1-6 for a general overview of SDO. For a more in-depth discussion of SDO, see “Data Programming Model and Update Framework” on page 2-1. Finally, see the dev2dev article Service Data Objects, which provides links to the SDO specifications and Javadoc. |
While the SDO specification does not specify a mechanism for updating data objects, it does discuss the need for update services, called mediator services. The Mediator API is an Oracle Data Service Integrator implementation of a mediator service. The Mediator API lets you gain access to SDO-compliant objects, called DataObjects, and return them to their source data store.
The important points to remember are that the Mediator API lets you connect to a data service and invoke data service operations. Results are returned as SDO-compliant data objects. Using methods of the SDO API, you can then change or manipulate the data objects. Finally, you use the Mediator API to perform the update.
See “Oracle Data Service Integrator and SDO” on page 2-2 for a general overview of SDO data objects and other artifacts.
The Oracle Data Service Integrator Mediator API comprises two main interfaces: dynamic and static. As an application developer, you need to choose one of these approaches.
Tip: | For most use cases, the Static Mediator API is your best choice. The Static Mediator inherits from the Dynamic Mediator and therefore includes all of the functionality of the Dynamic Mediator API. In addition, the static API is type-safe at compile-time. Generally speaking, the static API is simpler and more convenient to use than the Dynamic Mediator API. |
The Dynamic Mediator API consists of the classes and interfaces listed in Table 3-1. Refer to the Javadoc on e-docs for more information on these classes and interfaces.
The interface for interacting with a data service. The invoke() method of this interface is used to call data service operations. If a data service operation returns a result, the invoke() method returns a DASResult object– a collection of SDO data objects or simple types. (Package: com.bea.dsp.das)
|
|
The Mediator APIs that return data sets return an object called DASResult (Data Access Service Result). DASResult is similar to a Java Iterator. See Understanding DASResult. (Package: com.bea.dsp.das)
|
|
The interface for preparing and executing ad hoc queries. An ad hoc query is one that is defined in the client program, not in the data service. See Making Ad Hoc Queries. (Package: com.bea.dsp.das)
|
|
Oracle Data Service Integrator maintains a global cache of SDO HelperContext objects. These objects can be used, for instance, to create new data objects. This class contains methods that let you query and manipulate this cache. See Creating New DataObjects. (Package: com.bea.dsp.das)
|
|
Sample of both Static and Dynamic Mediator clients applications are provided. See Sample Static Mediator Application and Sample Dynamic Mediator Application.
It may be confusing at first discussing SDO and the mediator APIs together. You can think of SDO as the standard enabling technology that allows client applications to access and update data through Oracle Data Service Integrator data services. SDO has a Java API for handling DataObjects and collections of DataObjects. SDO DataObjects can be either dynamic or static.
The SDO APIs are standard implementations. You can read the full SDO specification, “SDO for Java Specification V2.1” here:
The mediator APIs, on the other hand, are Oracle Data Service Integrator-specific implementations. The mediator APIs are designed to let you access SDO DataObjects and return them to the server. For more information on the how Oracle Data Service Integrator uses SDO, see Chapter 2, “Data Programming Model and Update Framework.”
This section lists the basic steps to get started writing a Java client application that interacts with a data service.
Topics in this section include:
These are the basic steps to follow when developing a Java client that uses the Mediator APIs.
Tip: | You can discover data services that are available to you by using the Oracle Data Service Integrator Console. See Viewing Metadata Using the Service Explorer in the Oracle Data Service Integrator Administration Guide. |
Tip: | The Static Mediator API is generally recommended for most use cases. The static API is type safe and generally easier to use than the Dynamic Mediator API. |
You can set the CLASSPATH by either adding the Oracle Data Service Integrator client library to the project or by manually setting the CLASSPATH.
You can add the Oracle Data Service Integrator client library to your project by doing either of the following:
You can add the Oracle Data Service Integrator client library to an existing project.
Alternatively, you can do the following:
You can add the Oracle Data Service Integrator client library when creating a new Java project.
You can optionally set the CLASSPATH manually, if required. The CLASSPATH settings depend on whether you are using the Static or Dynamic Mediator API.
Note: | You can use the Java Mediator API with either the weblogic.jar or the wlfullclient.jar file. For more information about choosing between weblogic.jar or wlfullclient.jar, see Overview of Stand-alone Clients in the Oracle WebLogic Server documentation. For more information about creating the wlfullclient.jar file, see Using the WebLogic JarBuilder Tool. |
The following JARs must be in the CLASSPATH of your Java application if you are using the Static Mediator API.
CLASSPATH=
<dataspace-dsp-client>.jar <= this is the generated static client jar
<ALDSP_HOME>/lib/ld-client.jar
<BEA_HOME>/modules/com.bea.core.sdo_1.1.0.0.jar
<WL_HOME>/server/lib/weblogic.jar
CLASSPATH=
<dataspace-dsp-client>.jar <= this is the generated static client jar
<ALDSP_HOME>/lib/ld-client.jar
<BEA_HOME>/modules/com.bea.core.sdo_1.1.0.0.jar
<BEA_HOME>/modules/com.bea.core.xml.xmlbeans_1.0.0.0_2-4-0.jar
<WL_HOME>/server/lib/wlfullclient.jar
The following JARs must be in the CLASSPATH of your Java application if you are using the Dynamic Mediator API.
CLASSPATH=
<ALDSP_HOME>/lib/ld-client.jar
<BEA_HOME>/modules/com.bea.core.sdo_1.1.0.0.jar
<WL_HOME>/server/lib/weblogic.jar
CLASSPATH=
<ALDSP_HOME>/lib/ld-client.jar
<BEA_HOME>/modules/com.bea.core.sdo_1.1.0.0.jar
<BEA_HOME>/modules/com.bea.core.xml.xmlbeans_1.0.0.0_2-4-0.jar
<WL_HOME>/server/lib/wlfullclient.jar
Certain application contexts, such as web applications, employ their own class loaders. In these cases, you must take steps to ensure that the static mediator classes use the correct class loader. If you do not take these steps, class cast exceptions can occur.
To ensure that your static mediator classes resolve properly in such contexts, you can pass the appropriate class loader object to com.bea.dsp.das.HelperContextCache.setClassLoader() before creating DataAccessService or PreparedExpression objects in your code. The example code in Listing 3-5 shows one possible variation on this approach, where the class loader is obtained from the current thread object. This variation works well for web applications deployed on WebLogic Server.
import com.bea.dsp.das.HelperContextCache;
...
ClassLoader sdoCompiledSchemaLoader =
Thread.currentThread().getContextClassLoader();
HelperContextCache.setClassLoader(dataSpaceName, sdoCompiledSchemaLoader);
...
DataAccessService das = DataAccessServiceFactory.newDataAccessService(ctx,
dataSpaceName, dsUri);
...
Note: | In Listing 3-5, you could use the following code to obtain a PreparedExpression object: |
Note: | PreparedExpression pe = DataAccessServiceFactory.prepareExpression(ctx, dspDataSpace, adhoc); |
Other possible approaches to obtaining the class loader object include:
this
keyword to refer to the current object). A good way to get started is to run the sample application code that is provided in this chapter. Samples that use both the Static and the Dynamic Mediator APIs are included. The samples illustrate simple but common use cases: retrieving data, modifying it, and updating it. See Sample Static Mediator Application and Sample Dynamic Mediator Application.
This section presents a simple Java program that you can copy, compile, and run. The program uses the Static Mediator API to perform these basic tasks: authenticating the client, retrieving data, modifying data, and updating data on the server. For a basic overview of the Static Mediator API, see Dynamic and Static Mediator APIs. See also Mediator API Basics and Advanced Topics.
Before you can build and test the sample Java application, you need to set up an Oracle Data Service Integrator data service. The instructions assume that you are familiar with the Oracle Data Service Integrator perspective in the Eclipse IDE, as described in the Data Services Developer’s Guide.
Note: | The sample Java client that is presented in this section calls operations in this sample data service. The sample Java code is designed to work with this specific data service. |
Figure 3-2 shows the resulting Dataspace configuration:
Note: | Listing 3-6 is a simple data service file, containing the XQuery code that defines the service and its operations. Listing 3-7 and Listing 3-8 are schema files that are required by the data service. The Mediator API lets you invoke the data service operations from a Java client. For more information on data services, see Data Services Developer’s Guide. |
xquery version "1.0" encoding "UTF-8";
(::pragma xds <x:xds targetType="t:CUSTOMER" xmlns:x="urn:annotations.ld.bea.com" xmlns:t="ld:Retail/CUSTOMER">
<creationDate>2007-11-08T17:13:51</creationDate>
<relationalDB name="dspSamplesDataSource" providerId="Pointbase"/>
<field xpath="CUSTOMER_ID" type="xs:short">
<extension nativeXpath="CUSTOMER_ID" nativeTypeCode="5"
nativeType="SMALLINT" nativeSize="5" nativeFractionalDigits="0"
nativeKey="true">
<autoNumber type="identity"/>
</extension>
<properties nullable="false"/>
</field>
<field xpath="FIRST_NAME" type="xs:string">
<extension nativeXpath="FIRST_NAME" nativeTypeCode="12"
nativeType="VARCHAR" nativeSize="64" nativeFractionalDigits="0"/>
<properties nullable="false"/>
</field>
<field xpath="LAST_NAME" type="xs:string">
<extension nativeXpath="LAST_NAME" nativeTypeCode="12"
nativeType="VARCHAR" nativeSize="64" nativeFractionalDigits="0"/>
<properties nullable="false"/>
</field>
<field xpath="CUSTOMER_SINCE" type="xs:date">
<extension nativeXpath="CUSTOMER_SINCE" nativeTypeCode="91"
nativeType="DATE" nativeSize="10" nativeFractionalDigits="0"/>
<properties nullable="false"/>
</field>
<field xpath="EMAIL_ADDRESS" type="xs:string">
<extension nativeXpath="EMAIL_ADDRESS" nativeTypeCode="12"
nativeType="VARCHAR" nativeSize="32" nativeFractionalDigits="0"/>
<properties nullable="false"/>
</field>
<field xpath="TELEPHONE_NUMBER" type="xs:string">
<extension nativeXpath="TELEPHONE_NUMBER" nativeTypeCode="12"
nativeType="VARCHAR" nativeSize="32" nativeFractionalDigits="0"/>
<properties nullable="false"/>
</field>
<field xpath="SSN" type="xs:string">
<extension nativeXpath="SSN" nativeTypeCode="12" nativeType="VARCHAR"
nativeSize="16" nativeFractionalDigits="0"/>
<properties nullable="true"/>
</field>
<field xpath="BIRTH_DAY" type="xs:date">
<extension nativeXpath="BIRTH_DAY" nativeTypeCode="91" nativeType="DATE"
nativeSize="10" nativeFractionalDigits="0"/>
<properties nullable="true"/>
</field>
<field xpath="DEFAULT_SHIP_METHOD" type="xs:string">
<extension nativeXpath="DEFAULT_SHIP_METHOD" nativeTypeCode="12"
nativeType="VARCHAR" nativeSize="16" nativeFractionalDigits="0"/>
<properties nullable="true"/>
</field>
<field xpath="EMAIL_NOTIFICATION" type="xs:short">
<extension nativeXpath="EMAIL_NOTIFICATION" nativeTypeCode="5"
nativeType="SMALLINT" nativeSize="5" nativeFractionalDigits="0"/>
<properties nullable="true"/>
</field>
<field xpath="NEWS_LETTTER" type="xs:short">
<extension nativeXpath="NEWS_LETTTER" nativeTypeCode="5"
nativeType="SMALLINT" nativeSize="5" nativeFractionalDigits="0"/>
<properties nullable="true"/>
</field>
<field xpath="ONLINE_STATEMENT" type="xs:short">
<extension nativeXpath="ONLINE_STATEMENT" nativeTypeCode="5"
nativeType="SMALLINT" nativeSize="5" nativeFractionalDigits="0"/>
<properties nullable="true"/>
</field>
<field xpath="LOGIN_ID" type="xs:string">
<extension nativeXpath="LOGIN_ID" nativeTypeCode="12" nativeType="VARCHAR"
nativeSize="50" nativeFractionalDigits="0"/>
<properties nullable="true"/>
</field>
<key name="CUSTOMER_0_SYSTEMNAMEDCONSTRAINT__PRIMARYKEY"
type="cus:CUSTOMER_KEY" inferredSchema="true"
xmlns:cus="ld:Retail/CUSTOMER"/>
</x:xds>::)
declare namespace f1 = "ld:Retail/CUSTOMER";
import schema namespace t1 = "ld:Retail/CUSTOMER" at "ld:Retail/schemas/CUSTOMER.xsd";
import schema "ld:Retail/CUSTOMER" at "ld:Retail/schemas/CUSTOMER_KEY.xsd";
(::pragma function <f:function xmlns:f="urn:annotations.ld.bea.com"
visibility="public" kind="read" isPrimary="false" nativeName="CUSTOMER"
nativeLevel2Container="SAMPLECUSTOMER" style="table">
<nonCacheable/> </f:function>::)
declare function f1:CUSTOMER() as schema-element(t1:CUSTOMER)* external;
(::pragma function <f:function xmlns:f="urn:annotations.ld.bea.com"
visibility="public" kind="create" isPrimary="true" nativeName="CUSTOMER"
nativeLevel2Container="SAMPLECUSTOMER" style="table">
<nonCacheable/> </f:function>::)
declare procedure f1:createCUSTOMER($p as element(t1:CUSTOMER)*)as
schema-element(t1:CUSTOMER_KEY)* external;
(::pragma function <f:function xmlns:f="urn:annotations.ld.bea.com"
visibility="public" kind="update" isPrimary="true" nativeName="CUSTOMER"
nativeLevel2Container="SAMPLECUSTOMER" style="table">
<nonCacheable/> </f:function>::)
declare procedure f1:updateCUSTOMER($p as changed-element(t1:CUSTOMER)*) as
empty() external;
(::pragma function <f:function xmlns:f="urn:annotations.ld.bea.com"
visibility="public" kind="delete" isPrimary="true" nativeName="CUSTOMER"
nativeLevel2Container="SAMPLECUSTOMER" style="table">
<nonCacheable/> </f:function>::)
declare procedure f1:deleteCUSTOMER($p as element(t1:CUSTOMER)*) as empty()
external;
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema targetNamespace="ld:Retail/CUSTOMER" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="CUSTOMER_KEY">
<xs:complexType>
<xs:sequence>
<xs:element name="CUSTOMER_ID" type="xs:short"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema targetNamespace="ld:Retail/CUSTOMER" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="CUSTOMER">
<xs:complexType>
<xs:sequence>
<xs:element name="CUSTOMER_ID" type="xs:short" minOccurs="0"/>
<xs:element name="FIRST_NAME" type="xs:string"/>
<xs:element name="LAST_NAME" type="xs:string"/>
<xs:element name="CUSTOMER_SINCE" type="xs:date"/>
<xs:element name="EMAIL_ADDRESS" type="xs:string"/>
<xs:element name="TELEPHONE_NUMBER" type="xs:string"/>
<xs:element name="SSN" type="xs:string" minOccurs="0"/>
<xs:element name="BIRTH_DAY" type="xs:date" minOccurs="0"/>
<xs:element name="DEFAULT_SHIP_METHOD"
type="xs:string" minOccurs="0"/>
<xs:element name="EMAIL_NOTIFICATION" type="xs:short" minOccurs="0"/>
<xs:element name="NEWS_LETTTER" type="xs:short" minOccurs="0"/>
<xs:element name="ONLINE_STATEMENT" type="xs:short" minOccurs="0"/>
<xs:element name="LOGIN_ID" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
The sample Java application listed later in this section requires that you first generate a Mediator Client JAR from the data service. The classes in this JAR contain type-safe methods that call the data service functions and procedures. The generated Java methods have the same names as their corresponding data service functions and procedures.
Tip: | You can generate a Mediator Client JAR file using the IDE, the Oracle Data Service Integrator Console, or an Ant script. These methods are described in detail in the Data Services Developer’s Guide. For this example, we will use the IDE. |
To generate a mediator client JAR file using the IDE:
Tip: | For detailed information on how generated class names in the JAR file are derived, see Naming Conventions for Generated Classes. |
Listing 3-10 lists the sample Java program that uses the Static Mediator API. The application simply retrieves a DataObject from a data store, modifies the object, and returns it to the data store. This example assumes you are using the Eclipse IDE, but you can use the IDE or build environment of your choice. For this example, we set up an Eclipse Java project called MediatorClient.
Note: | The imported classes CUSTOMERDAS and CUSTOMER (see Listing 3-9) are located in the Static Mediator Client JAR file, which must be in the CLASSPATH. |
package com.bea.dsp.sample;
import das.ejb.retail.CUSTOMERDAS;
import retail.customer.CUSTOMER;
import com.bea.dsp.das.DASResult;
import com.bea.dsp.sdo.SDOUtil;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
public class StaticSampleApp {
public static void main(String[] args) throws Exception {
// Create InitialContext for mediator
Hashtable<String, String> hash = new Hashtable<String, String>();
hash.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
hash.put(Context.PROVIDER_URL,"t3://localhost:7001");
hash.put(Context.SECURITY_PRINCIPAL,"weblogic");
hash.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context ctx = new InitialContext(hash);
// Create DataAccessService handle with Context and dataspace name
CUSTOMERDAS das = CUSTOMERDAS.getInstance(ctx, "MediatorSamples");
// Invoke the basic 'get all customers' function
DASResult<CUSTOMER> result = das.CUSTOMER();
// Obtain the first CUSTOMER DataObject - also be sure to
// always dispose() any DASResults
try {
CUSTOMER customer = result.next();
// Enable change-tracking for that CUSTOMER
SDOUtil.enableChanges(customer);
// Modify customer
customer.setFIRST_NAME("New First Name");
customer.setEMAIL_ADDRESS("first_name@example.com");
// Send changes back to DSP - update function takes an array
// of CUSTOMERs
das.updateCUSTOMER(new CUSTOMER[] { customer });
}
finally {
result.dispose();
}
}
}
To verify that the Java client worked, simply test the data service:
This section examines the parts of the Java sample in Listing 3-9. This section discusses:
The first two classes are located in the generated Mediator Client JAR file, which must be in your build path. The CUSTOMERDAS class is the generated DataAccessService class for the data service. This class contains type-safe methods that map to the actual data service operations. The CUSTOMER class provides the SDO interface for manipulating DataObjects returned from the data service.
import das.ejb.retail.CUSTOMERDAS;
import retail.customer.CUSTOMER;
import com.bea.dsp.das.DASResult;
import com.bea.dsp.sdo.SDOUtil;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
A DataAccessService object lets you call methods on a data service. See the Javadoc for more information on this class. For the Static Mediator API, DataAccessService (DAS) classes have a factory method named getInstance() to return the handle.
The getInstance() method requires two parameters to return the handle:
Hashtable<String, String> hash = new Hashtable<String, String>();
hash.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
hash.put(Context.PROVIDER_URL,"t3://localhost:7001");
hash.put(Context.SECURITY_PRINCIPAL,"weblogic");
hash.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context ctx = new InitialContext(hash);
CUSTOMERDAS das = CUSTOMERDAS.getInstance(ctx, "MediatorSamples");
The generated DataAccessService method CUSTOMER() retrieves the result set from the data service. This method returns all customer objects from the data service. The return type is a DASResult object, which works like an iterator. For more information on this return type, see Understanding DASResult.
DASResult<CUSTOMER> result = das.CUSTOMER();
Note: | The method CUSTOMER() is mapped directly from the original no-argument data service operation of the same name. The operation definition as specified in the data service file looks like this: |
Note: | (::pragma function <f:function xmlns:f="urn:annotations.ld.bea.com" visibility="public" kind="read" isPrimary="false" nativeName="CUSTOMER" nativeLevel2Container="SAMPLECUSTOMER" style="table"> <nonCacheable/> </f:function>::) declare function f1:CUSTOMER() as schema-element(t1:CUSTOMER)* external; |
Note: | The entire data service file is shown in Listing 3-6. |
The DASResult.next() method works very much like the Java method Iterator.next(). It returns the next CUSTOMER, which is an SDO DataObject. SDO is a Java-based data programming model (API) and architecture for accessing and updating data. For details on SDO, see Using Service Data Objects (SDO) in the Oracle Data Service Integrator Concepts Guide.
CUSTOMER customer = result.next();
You must call DASResult.dispose() whenever you are finished iterating through a result object. For more information on dispose(), see Disposing of DASResult Objects.
result.dispose();
Tip: | Placing the dispose() call in a try/finally block is a recommended best practice. |
After you obtain a DataObject, you can modify it; however, if you intend to submit these changes back to the Oracle Data Service Integrator server, you must enable change-tracking on the DataObject before making any modifications. The SDOUtil.enableChanges() method lets you enable change-tracking for a single DataObject or an array of DataObjects. For more information on this method, see Working with Data Objects. After the customer object has change-tracking enabled, the generated setters are called to modify certain values in the customer object.
Tip: | Note that the set method below is called on an SDO DataObject. Technically, such methods are part of the SDO API, not the Mediator API. See Chapter 2, “Data Programming Model and Update Framework” for information on SDO. |
SDOUtil.enableChanges(customer);
// Modify customer
customer.setFIRST_NAME("New First Name");
customer.setEMAIL_ADDRESS("first_name@example.com");
Finally, the generated DataAccessService.updateCUSTOMER() method is called with a single parameter: an array of CUSTOMER objects. The method calls its equivalent data service operation to update the database with the newly modified row of data.
das.updateCUSTOMER(new CUSTOMER[] { customer });
Tip: | In this example, the update method generated by Oracle Data Service Integrator accepts an array of DataObjects. It accepts an array because the data service operation (created by the data service developer) accepts an array of data objects. If the data service developer had created an additional update method that accepted a single CUSTOMER, it would not be necessary to put the customer DataObject into an array. |
This section presents a simple example that you can copy, compile, and run. This example uses the Dynamic Mediator API to perform these basic tasks: authenticating the client, retrieving data, modifying data, and updating data on the server.
The topics in this section include:
To set up and run this sample code, follow the basic instructions in Sample Static Mediator Application. The procedures for creating a sample data service, setting up the Java project, and running the program are the same as the Static Mediator sample; however, when using the Dynamic Mediator API, you do not need to generate or reference the Static Mediator Client JAR file. Use the sample Java code shown in Listing 3-10 in your project.
package com.bea.dsp.sample;
import com.bea.dsp.das.DataAccessServiceFactory;
import com.bea.dsp.das.DataAccessService;
import com.bea.dsp.das.DASResult;
import com.bea.dsp.sdo.SDOUtil;
import commonj.sdo.DataObject;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
public class DynamicSampleApp {
public static void main(String[] args) throws Exception {
// Create InitialContext for mediator
Hashtable<String, String> hash = new Hashtable<String, String>();
hash.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
hash.put(Context.PROVIDER_URL,"t3://localhost:7001");
hash.put(Context.SECURITY_PRINCIPAL,"weblogic");
hash.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context ctx = new InitialContext(hash);
// Create DataAccessService handle with Context, dataspace
// name, and data service URI
DataAccessService das = DataAccessServiceFactory.newDataAccessService
(ctx, "MediatorSamples", "ld:Retail/CUSTOMER");
// Invoke the basic 'get all customers' function, which takes
// no arguments
DASResult<Object> result = das.invoke("CUSTOMER", new Object[0]);
// Obtain the first CUSTOMER DataObject - also be sure to
// always dispose() any DASResults
try {
DataObject customer = (DataObject) result.next();
// Enable change-tracking for that CUSTOMER
SDOUtil.enableChanges(customer);
// Modify customer
customer.set("FIRST_NAME", "DynamicClient");
customer.set("EMAIL_ADDRESS", "dynamic@example.com");
// Send changes back to DSP - update function takes an array
// of CUSTOMERs
das.invoke("updateCUSTOMER", new Object[] { customer });
}
finally {
result.dispose();
}
}
}
This section examines the parts of the Java sample in Listing 3-10. This section discusses:
These classes are required by the sample. For detailed information on the classes, refer to the Javadoc on e-docs.
import com.bea.dsp.das.DataAccessServiceFactory;
import com.bea.dsp.das.DataAccessService;
import com.bea.dsp.das.DASResult;
import com.bea.dsp.sdo.SDOUtil;
import commonj.sdo.DataObject;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
A DataAccessService object lets you call methods on a data service. See the Javadoc for more information on this class. The DataAccessServiceFactory class requires three parameters to return the handle:
Hashtable<String, String> hash = new Hashtable<String, String>();
hash.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
hash.put(Context.PROVIDER_URL,"t3://localhost:7001");
hash.put(Context.SECURITY_PRINCIPAL,"weblogic");
hash.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context ctx = new InitialContext(hash);
DataAccessService das = DataAccessServiceFactory.newDataAccessService
(ctx, "MediatorSamples", "ld:Retail/CUSTOMER");
In this example, the invoke() method calls the data service CUSTOMER operation. This operation returns all customer objects from the data service. The invoke() method returns a DASResult object, which works like an iterator. For more information on this return type, see Understanding DASResult. Note that the CUSTOMER operation takes no arguments.
DASResult<Object> result = das.invoke("CUSTOMER", new Object[0])
Note: | The generic type parameter for DASResult is <Object> because data of any type can be returned by the invoke() method of the Dynamic Mediator API. |
The DASResult.next() method works very much like the Java method Iterator.next(). It returns the next object in the result set. Because the CUSTOMER data service method returns SDO-compliant DataObjects, you can cast the return value to DataObject. SDO is a Java-based data programming model (API) and architecture for accessing and updating data. For details on SDO, see Using Service Data Objects (SDO) in the Oracle Data Service Integrator Concepts Guide. See also What is SDO?.
DataObject customer = (DataObject) result.next();
You must call DASResult.dispose() whenever you are finished iterating through a result object. For more information on dispose(), see Disposing of DASResult Objects.
result.dispose();
Tip: | Placing the dispose() call in a try/finally block is a recommended best practice. |
After you obtain a DataObject, you can modify it; however, if you intend to submit these changes back to the Oracle Data Service Integrator server, you must enable change-tracking on the DataObject before making any modifications. The SDOUtil.enableChanges() method lets you enable change-tracking for a single DataObject or an array of DataObjects. For more information on this method, see Working with Data Objects. After the customer object has change-tracking enabled, the Dynamic SDO set() method is called to modify certain values in the customer object. For more information on SDO methods, see Chapter 2, “Data Programming Model and Update Framework.”
SDOUtil.enableChanges(customer);
customer.set("FIRST_NAME", "DynamicClient");
customer.set("EMAIL_ADDRESS", "dynamic@example.com");
Finally, the DataAccessService method invoke() calls the update method on the data service with a single parameter: an array of CUSTOMER objects. The data service operation updates the database with the newly modified row of data.
das.invoke("updateCUSTOMER", new Object[] { customer });
Tip: | In this example, the update method accepts an array of DataObjects. It accepts an array because the data service operation (created by the data service developer) accepts an array of data objects. If the data service developer had created an additional update method that accepted a single CUSTOMER, it would not be necessary to put the customer DataObject into an array. |
This section explains how to use the Data Services Mediator and SDO APIs to create new data objects and submit them to the Oracle Data Service Integrator server. As with previous examples, both the static and dynamic APIs are illustrated.
The Java program in Listing 3-11 creates a new DataObject, modifies it, and updates it on the Oracle Data Service Integrator server.
The sample code in Listing 3-11 is designed to work in the same Java project and with the same data service project that are described in Sample Static Mediator Application. You can run the sample code presented here by following the setup instructions in that section. When you test the data service, you will see a new row has been added to the table.
package com.bea.dsp.sample;
import das.ejb.retail.CUSTOMERDAS;
import retail.customer.CUSTOMER;
import retail.customer.CUSTOMER_KEY;
import com.bea.dsp.das.DASResult;
import com.bea.dsp.sdo.SDOUtil;
import commonj.sdo.helper.HelperContext;
import commonj.sdo.helper.DataFactory;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
public class StaticCreateSample {
public static void main(String[] args) throws Exception {
// Create InitialContext for mediator
Hashtable<String, String> hash = new Hashtable<String, String>();
hash.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
hash.put(Context.PROVIDER_URL,"t3://localhost:7001");
hash.put(Context.SECURITY_PRINCIPAL,"weblogic");
hash.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context ctx = new InitialContext(hash);
// Create DataAccessService handle with Context and dataspace name
CUSTOMERDAS das = CUSTOMERDAS.getInstance(ctx, "MediatorSamples");
// Obtain the SDO HelperContext for this dataspace
HelperContext hctx = das.getHelperContext();
// Could also use:
// HelperContext hctx = HelperContextCache.get("MediatorSamples");
// Get DataFactory from HelperContext
DataFactory factory = hctx.getDataFactory();
// Create an "empty" CUSTOMER DataObject by naming the XML
// schema *type*. For schema global elements that do not
// explicitly specify a type, their type name will be the same
// as the element name.
CUSTOMER customer = (CUSTOMER) factory.create
("ld:Retail/CUSTOMER", "CUSTOMER");
// Have to provide this DataObject with its own name. Note
// that this is the XML schema *name*, not the *type* -
// although as noted, when the global element does not
// explicitly specify a type, the type that is provided for it
// has the same name as the element.
SDOUtil.setElementName(customer, "ld:Retail/CUSTOMER", "CUSTOMER");
// Note that you must NOT enable change-tracking for this
// DataObject using enableChanges(). Change-tracking is only
// for tracking changes to data originally received from the
// DSP server.
// Set fields on new DataObject. Don't set auto-generated
// fields, such as CUSTOMER_ID. May omit optional fields or
// those with default values
customer.setFIRST_NAME("New First Name");
customer.setLAST_NAME("New Last Name");
customer.setCUSTOMER_SINCE("2007-10-18");
customer.setEMAIL_ADDRESS("first_name@example.com");
customer.setTELEPHONE_NUMBER("867-5309");
// Send new DataObject to DSP - create function takes an array
// of CUSTOMERs, and returns CUSTOMER_KEYs
DASResult<CUSTOMER_KEY> result =
das.createCUSTOMER(new CUSTOMER[] { customer });
// Can obtain new customer ID from the returned key - also be
// sure to always dispose() any DASResults.
try {
CUSTOMER_KEY key = result.next();
System.out.println("New customer key: " + key.getCUSTOMER_ID());
}
finally {
result.dispose();
}
// Note that the created DataObject is NOT automatically
// updated based on the generated key values. If you want to
// get a DataObject populated with the new CUSTOMER_ID, you
// need to re-read. This is easier if the data service
// architect provides a getByID() function on the data
// service.
}
}
Two SDO classes are required by this program. A HelperContext provides access to a consistent set of instances of SDO helpers. It represents a helper execution context. The set of helpers returned by the methods in this interface have visibility to the same SDO metadata, that is, they execute in the same “scope.” A DataFactory is a helper for the creation of DataObjects. The created DataObjects are not connected to any other DataObjects. Only Types with DataType false and abstract false may be created.
A DataAccessService object lets you call methods on a data service. See the Javadoc for more information on this class. For the Static Mediator API, DataAccessService (DAS) classes have a factory method named getInstance() to return the handle.
The getInstance() method requires two parameters to return the handle:
Hashtable<String, String> hash = new Hashtable<String, String>();
hash.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
hash.put(Context.PROVIDER_URL,"t3://localhost:7001");
hash.put(Context.SECURITY_PRINCIPAL,"weblogic");
hash.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context ctx = new InitialContext(hash);
CUSTOMERDAS das = CUSTOMERDAS.getInstance(ctx, "MediatorSamples");
To create a DataFactory, you need to first obtain a HelperContext object for the Dataspace.
HelperContext hctx = das.getHelperContext();
DataFactory factory = hctx.getDataFactory();
You could also use this call to return the HelperContext:
HelperContext hctx = HelperContextCache.get("MediatorSamples");
After you create a DataObject, you must explicitly name it. The factory.create() method takes two String parameters. The first is a URI, the location of the data service in the Dataspace project. The second parameter is the XML schema type of the DataObject you are creating. For schema global elements that do not explicitly specify a type, their type name will be the same as the element name.
CUSTOMER customer = (CUSTOMER) factory.create("ld:Retail/CUSTOMER", "CUSTOMER");
Next, you must provide the new DataObject with a name. The SDOUtil.setElementName() method takes these parameters: the DataObject, the namespace URI of the element QName, and the local part of the element QName.
Note that this name is the XML schema name, not the type. However, as noted, for global elements that do not specify a type, the type that is provided has the same name as the element.
SDOUtil.setElementName(customer, "ld:Retail/CUSTOMER", "CUSTOMER");
After you create a new DataObject, you can modify it before submitting it to the server.
Note: | You must not enable change-tracking in this new DataObject using the SDOUtil.enableChanges() method. Change-tracking is only used for tracking changes to data that was originally received from the Oracle Data Service Integrator server. |
customer.setFIRST_NAME("New First Name");
customer.setLAST_NAME("New Last Name");
customer.setCUSTOMER_SINCE("2007-10-18T12:27:41Z");
customer.setEMAIL_ADDRESS("first_name@example.com");
customer.setTELEPHONE_NUMBER("867-5309");
Tip: | You can omit optional fields or fields with default values. |
After the new object is created, the data service operation createCUSTOMER is called from the Static Mediator API. The data service create operation takes an array of objects as input. The Mediator API method returns a CUSTOMER_KEY objects in a DASResult.
DASResult<CUSTOMER_KEY> result = das.createCUSTOMER(new CUSTOMER[] { customer });
To return the CUSTOMER_KEY for the new CUSTOMER object, call the next() method on the DASResult object. Be sure to dispose the DASResult object (result) after it is returned. Placing dispose() in a try/finally block is a recommended best practice.
try {
CUSTOMER_KEY key = result.next();
System.out.println("New customer key: " + key.getCUSTOMER_ID());
}
finally {
result.dispose();
}
Tip: | The newly created local copy of the DataObject (customer , in this example) is not automatically updated with generated keys such as CUSTOMER_ID. If you want to obtain a DataObject populated with CUSTOMER_ID, you need to retrieve the new DataObject from the server by invoking the data service’s read operation. This is easier if the data service developer provides a getByID() operation on the data service. |
The Java program in Listing 3-11 creates a new DataObject, modifies it, and updates it on the Oracle Data Service Integrator server.
The sample code in Listing 3-12 is designed to work in the same Java project and with the same data service project that are described in Sample Dynamic Mediator Application. You can run the sample code presented here by following the setup instructions in that section.
package com.bea.dsp.sample;
import com.bea.dsp.das.DataAccessService;
import com.bea.dsp.das.DataAccessServiceFactory;
import com.bea.dsp.das.DASResult;
import com.bea.dsp.das.HelperContextCache;
import com.bea.dsp.sdo.SDOUtil;
import commonj.sdo.helper.HelperContext;
import commonj.sdo.helper.DataFactory;
import commonj.sdo.DataObject;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
public class DynamicCreateSample {
public static void main(String[] args) throws Exception {
// Create InitialContext for mediator
Hashtable<String, String> hash = new Hashtable<String, String>();
hash.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
hash.put(Context.PROVIDER_URL,"t3://localhost:7001");
hash.put(Context.SECURITY_PRINCIPAL,"weblogic");
hash.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context ctx = new InitialContext(hash);
// Obtain the SDO HelperContext for this dataspace. As with
// StaticCreateSample, I could obtain this from the
// DataAccessService. However, here I'm demonstrating how to
// create an all-new DataObject prior to creating any
// DataAccessService instance. In this case, since I'm using
// the dynamic mediator, I need to first ensure that the
// global HelperContext cache is populated with the schemas
// for my data service.
HelperContextCache.loadSchemasForDataspace
(ctx, "MediatorSamples", "ld:Retail/CUSTOMER");
// Now that the schemas are loaded, I can get the
// HelperContext for the dataspace
HelperContext hctx = HelperContextCache.get("MediatorSamples");
// Get DataFactory from HelperContext
DataFactory factory = hctx.getDataFactory();
// Create an "empty" CUSTOMER DataObject by naming the XML
// schema *type*. For schema global elements that do not
// explicitly specify a type, their type name will be the same
// as the element name.
DataObject customer = factory.create("ld:Retail/CUSTOMER", "CUSTOMER");
// Have to provide this DataObject with its own name. Note
// that this is the XML schema *name*, not the *type* -
// although as noted, when the global element does not
// explicitly specify a type, the type that is provided for it
// has the same name as the element.
SDOUtil.setElementName(customer, "ld:Retail/CUSTOMER", "CUSTOMER");
// Note that you must NOT enable change-tracking for this
// DataObject using enableChanges(). Change-tracking is only
// for tracking changes to data originally received from the
// DSP server.
// Set fields on new DataObject. Don't set auto-generated
// fields, such as CUSTOMER_ID. May omit optional fields or
// those with default values
customer.set("FIRST_NAME", "Dynammic");
customer.set("LAST_NAME", "Mediator");
customer.set("CUSTOMER_SINCE", "2007-10-18");
customer.set("EMAIL_ADDRESS", "dynamic@example.com");
customer.set("TELEPHONE_NUMBER", "867-5309");
// Create DataAccessService handle with Context and dataspace name
DataAccessService das = DataAccessServiceFactory.newDataAccessService
(ctx, "MediatorSamples", "ld:Retail/CUSTOMER");
// Send new DataObject to DSP - create function takes an array
// of CUSTOMERs, and returns CUSTOMER_KEYs
DASResult<Object> result =
das.invoke("createCUSTOMER", new Object[] { customer });
// Can obtain new customer ID from the returned key. Always be
// sure to dispose() any DASResults you get.
try {
DataObject key = (DataObject) result.next();
System.out.println("New customer key: " + key.get("CUSTOMER_ID"));
}
finally {
result.dispose();
}
// Note that the created DataObject is NOT automatically
// updated based on the generated key values. If you want to
// get a DataObject populated with the new CUSTOMER_ID, you
// need to re-read. This is easier if the data service
// architect provides a getByID() function on the data
// service.
}
}
Three SDO classes are required by this program. A DataObject is a representation of some structured data. It is the fundamental component in the SDO (Service Data Objects) package. A HelperContext provides access to a consistent set of instances of SDO helpers. It represents a helper execution context. The set of helpers returned by the methods in this interface have visibility to the same SDO metadata, that is, they execute in the same “scope.” A DataFactory is a helper for the creation of DataObjects. The created DataObjects are not connected to any other DataObjects. Only Types with DataType false and abstract false may be created.
This example also uses the com.bea.dsp.das.HelperContextCache class, which provides access to the global cache of SDO HelperContext objects maintained by Oracle Data Service Integrator. The use of HelperContextCache is described in the next section Creating a DataFactory.
As with the static mediator example discussed previously, we need to obtain the SDO HelperContext for this Dataspace. In the static example, we created the HelperContext from the DataAccessService. However, in this example, we create a new DataObject before we create the DataAccessService instance. To do this, you need to ensure that the global HelperContext cache is populated with the data service schemas.
HelperContextCache.loadSchemasForDataspace
(ctx, "MediatorSamples", "ld:Retail/CUSTOMER");
// Now that the schemas are loaded, get the HelperContext for the dataspace.
HelperContext hctx = HelperContextCache.get("MediatorSamples");
DataFactory factory = hctx.getDataFactory();
Tip: | For detailed information on HelperContextCache, refer to the Javadoc. |
When you create a DataObject, you must explicitly name it. The factory.create() method takes two String parameters. The first is a URI, the location of the data service in the Dataspace project. The second parameter is the XML schema type of the DataObject you are creating. For schema global elements that do not explicitly specify a type, their type name will be the same as the element name.
DataObject customer = factory.create("ld:Retail/CUSTOMER", "CUSTOMER");
Next, you must provide the new DataObject with a name. The SDOUtil.setElementName() method takes these parameters: the DataObject, the namespace URI of the element QName, and the local part of the element QName.
Note that this name is the XML schema name, not the type. However, as noted, for global elements that do not specify a type, the type that is provided has the same name as the element.
SDOUtil.setElementName(customer, "ld:Retail/CUSTOMER", "CUSTOMER");
After you create a new DataObject, you can modify it before submitting it to the server.
Note: | You must not enable change-tracking in this new DataObject using the SDOUtil.enableChanges() method. Change-tracking is only used for tracking changes to data that was originally received from the Oracle Data Service Integrator server. |
customer.set("FIRST_NAME", "New First Name");
customer.set("LAST_NAME", "New Last Name");
customer.set("CUSTOMER_SINCE", "2007-10-18T12:27:41Z");
customer.set("EMAIL_ADDRESS", "first_name@example.com");
customer.set("TELEPHONE_NUMBER", "867-5309");
Tip: | You can omit optional fields or fields with default values. |
You need a DataAccessService handle to call methods on the data service. A DataAccessService object lets you call methods on a data service. See the Javadoc for more information on this class. The DataAccessServiceFactory class requires three parameters to return the handle:
The DataAccessServiceFactory returns the handle.
// Create DataAccessService handle with Context and dataspace name
DataAccessService das = DataAccessServiceFactory.newDataAccessService
(ctx, "MediatorSamples", "ld:Retail/CUSTOMER");
The DataAccessService.invoke() method is used to call the createCUSTOMER data service operation on the server. Note that the createCUSTOMER data service operation is designed to take an array of objects as input. The function returns a CUTOMER_KEYS objects in the DASResult object.
DASResult<Object> result =
das.invoke("createCUSTOMER", new Object[] { customer });
To return the key for the new CUSTOMER object, call the next() method on the DASResult object. Be sure to dispose the DASResult object (result) after it is returned. Placing dispose() in a try/finally block is a recommended best practice.
try {
DataObject key = (DataObject) result.next();
System.out.println("New customer key: " + key.get("CUSTOMER_ID"));
}
finally {
result.dispose();
}
Tip: | The newly created local copy of the DataObject (customer , in this example) is not automatically updated with generated keys such as CUSTOMER_ID. If you want to obtain a DataObject populated with CUSTOMER_ID, you need to retrieve the new DataObject from the server by invoking the data service’s read operation. This is easier if the data service developer provides a getByID() operation on the data service. |
This section discusses various Mediator API topics.
It is recommended that you review and run the sample applications provided in this chapter:
Although the sample code is very basic, it demonstrates common use cases of retrieving, modifying, and updating data. The samples also include details to help you understand the code.
The rest of this chapter discusses additional features of the APIs as well as advanced topics and important reference material.
When called through the Static Mediator API, data service operations that return empty() or that return a single item do not return DASResult; instead, they return void or the single item. See also Understanding DASResult.
This section provides additional information on the Dynamic Mediator API.
The invoke(String method, Object[] args) method dynamically invokes data service operations. When an operation is invoked (getCustomerByCustID(), for example), it returns a DASResult object. All data service functions return a DASResult when called through the Dynamic Mediator API. See also Understanding DASResult.
You can see the invoke() method in use in the Dynamic Mediator API sample in Listing 3-10, DynamicSampleApp.java, on page 3-26:
DASResult<Object> result = das.invoke("updateCustomer", new Object[0]);
More information on the invoke() method is available in Javadoc on e-docs.
SDO provides generic getters and setters for working with data objects. The SDO API can be used with data types that have not yet been deployed at development time. XPath expressions are passed as arguments to the generic methods. For example:
customer.set("EMAIL_ADDRESS", "first_name@example.com");
String name = customer.get("EMAIL_ADDRESS");
See also Specifying XPath Expressions as Arguments and Chapter 2, “Data Programming Model and Update Framework.”
When you generate a Mediator Client JAR file or a Web Services Mediator Client JAR file, the generated DataAccessService subclasses and packages are named according to the following conventions:
Generated DataAccessService subclasses are named <Data_Service_Name>DAS.class. For example, if you generate a Mediator Client JAR file from a data service called Customer.ds, a class called CustomerDAS.class is generated in the JAR file. Package names contain das.ejb.
Generated DataAccessService subclasses are named <Data_Service_Name>DAS.class. For example, if you generate a Web Services Mediator Client JAR from a web service map file called Customer.ws, a class called CustomerDAS.class is generated in the JAR file. Package names contain das.ws.
The mediator APIs that return data sets return an object called DASResult (Data Access Service Result). DASResult is similar to a Java Iterator.
This section includes these topics:
By default, data is returned to the Mediator from the Oracle Data Service Integrator server in small blocks. This “streaming” behavior means that large result sets are never held in memory all at once on either the Oracle Data Service Integrator server or the client application, which optimizes memory utilization. However, this requires that resources on the server be held open until all results have been returned to the client. See Support for Stateless Operations.
For example, the signature for the invoke() method is:
DASResult<Object> invoke(String operation, Object[] args) throws DASException;
Like an Iterator object, DASResult is forward-only; there is no way to return to a previous item nor to restart the iteration.
DASResult includes these standard Java Iterator methods:
Note: | The Java Iterator remove() method is not included because DASResult is a read-only object. |
All complex XML items in the DASResult object are represented as DataObjects. All simple items (which can be returned from library data service operations) in the result are represented by a corresponding Java object, such as Integer, Long, and so on. For information on how types are mapped to schema types, see Mapping Data Service Types to Java Types. See also Making Ad Hoc Queries.
Tip: | All mediator methods that return DASResult never return NULL; if the data service function returns no results, then the DASResult iterates through zero items. That is, hasNext() immediately returns false and next() returns NULL. |
The server is required to hold open resources such as database handles until the code finishes iterating through all of the results. Therefore, you are required to dispose of returned DASResult objects to tell the server to release the resources.
DASResult includes the following methods:
void dispose() – Disposes of resources required by DASResult on the Oracle Data Service Integrator server.
boolean isDisposed() – Returns whether the connection to the Oracle Data Service Integrator server has been closed.
Note: | You must call dispose() when you are finished iterating through a DASResult. If you call dispose() on a result object that has already been disposed, nothing happens, and no error is generated. |
The dispose() method is automatically called for you in the following two cases:
All Dynamic Mediator API methods that return XQuery results return a DASResult object. These methods include:
In the Static Mediator API, all generated methods for data service operations that have plural results are declared to return a DASResult<T>. Plural results are results of data service operations whose XQuery return type is type*
(zero or more instances of the type) or type+
(one or more instances of the type). T is the class of DataObjects that are returned by the DASResult.next().
Generated methods for data service operations that have a maximum of one return value (that is, data service operations whose XQuery return type is type
or type?
) will be declared to return the corresponding Java type directly, rather than a DASResult object. In addition, a data service operation whose return type is empty() will generate a static mediator method with a return type of void. See Mapping Data Service Types to Java Types for more information.
DASResult includes a method T[] getItems(). This method returns the results as an array. This method immediately disposes the DASResult. See Disposing of DASResult Objects.
Java client applications use JNDI to access named objects, such as data services, on a Oracle WebLogic Server. To use any of the Mediator APIs, you need to obtain the WebLogic Server JNDI context for Oracle Data Service Integrator. This context allows the mediator APIs to call data service operations and acquire information from data services. For more information on WebLogic JNDI context objects, see Programming WebLogic JNDI on e-docs.
Use the following call to obtain the JNDI context. The hashtable parameter is explained below.
InitialContext jndiCtxt = new InitialContext(hashtable);
Table 3-5 lists the keys and values that you can insert into the hashtable parameter.
Listing 3-13 shows example code for obtaining the JNDI context.
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL,"t3://machinename:7001");
h.put(Context.SECURITY_PRINCIPAL,<username>);
h.put(Context.SECURITY_CREDENTIALS,<password>);
InitialContext jndiCtxt = new InitialContext(h);
When you invoke a data service operation using the Mediator API, a collection of SDO-compliant data objects is returned in a DASResult object. (See also Understanding DASResult.)
This section discusses working with DataObjects within the context of a Java client. For more details on SDO data objects and the SDO API, see Chapter 2, “Data Programming Model and Update Framework.”
Before you make any changes to a DataObject, you must enable it for change tracking. To do this, pass the DataObject to the com.bea.dsp.sdo.SDOUtil.enableChanges() method. For example:
SDOUtil.enableChanges(customer);
There are two forms of enableChanges(). One takes a DataObject and the other takes an array of DataObjects:
com.bea.dsp.sdo.SDOUtil.enableChanges(DataObject);
com.bea.dsp.sdo.SDOUtil.enableChanges(DataObject[]);
Tip: | When a DataObject that is enabled for changes is returned to the server, it contains its original data and its changed data. The mechanics of handling changed data is somewhat complex; therefore, the SDOUtil.enableChanges() utility method was created to handle those details. |
After you pass a DataObject to enableChanges(), you can make any allowable modifications to the DataObject using the standard SDO APIs. See Chapter 2, “Data Programming Model and Update Framework” for detailed information on the SDO API interfaces.
SDO provides static (typed) and dynamic (untyped) interfaces. For details, see “Static Data Object API” on page 2-4 and “Dynamic Data Object API” on page 2-8.
Example static (typed) method call:
customer.setFIRST_NAME("New First Name");
Example dynamic (untyped) method call:
customer.set("FIRST_NAME", "New First Name");
After an SDO object is enabled for change and modified, it can be passed as an argument to an update method. Oracle Data Service Integrator then handles the details of performing the update. For example, from Listing 3-9, StaticSampleApp.java, on page 3-19:
das.updateCUSTOMER(new CUSTOMER[] { customer });
You can use the API to create a completely new data object. In RDBMS terms this would be considered creating a new record. Data object creation is an advanced topic. For detailed information, see Creating New DataObjects.
This section explains how types in data services are mapped to Java types by the Mediator API. For example, the Static Mediator API generator makes these type conversions when creating a Mediator Client JAR file. This section also helps you understand how argument types passed to Mediator API methods are mapped to corresponding XQuery types.
Topics in this section include:
Table 3-6 specifies how simple XQuery types are converted to Java types by the Mediator API. For example, a data service operation that returns xs:int produces a Java method that returns a Java Integer object. An operation that returns xs:int* (zero or more ints) returns a DASResult<Integer> object. (See also Static Mediator APIs and DASResult.)
Note: | Simple types in are mapped to Java Objects when returned from the mediator in a manner that is identical to the SDO for Java Specification V2.1. You can find this specification online at: www.osoa.org/display/Main/Service+Data+Objects+Specifications. |
Note: | The following XQuery types are discussed by the SDO specification but are not supported for input or output from the Mediator API: xs:ENTITIES, xs:ENTITY, xs:ID, xs:IDREF, xs:IDREFS, xs:language, xs:Name, xs:NCName, xs:NMTOKEN, xs:NMTOKENS, xs:NOTATION. |
The mediator APIs handle date/time conversions in a manner that is consistent with the SDO specification. In the SDO for Java Specification V2.1, all date/time values are mapped to Java Strings. You can find this specification online at: www.osoa.org/display/Main/Service+Data+Objects+Specifications.
Note: | The form of these Strings is the same as the canonical lexical representation of the corresponding schema type according to the XML Schema specification. |
If a data service operation takes an optional argument (for example CUSTOMER?
), you can pass a NULL parameter to a Static or Dynamic Mediator method. You can pass NULL in these situations:
In the Static Mediator API, quantified return types from data service operations are generated based on the following rules:
Any data service parameters that are quantified with *
or +
(for example, xx:int*
) have static mediator methods that are declared to return DASResult<type>.
For example, a data service operation with the following signature:
declare function t1:someFunc($a as xs:int*, $b as xs:double+) as xs:int*
external;
is converted to a method like this:
DataObject<java.lang.Integer> someFunc(Integer[] a, Double[] b);
Data service operations that return unquantified or ?
-quantified types have Static Mediator methods that are declared to return the type directly. In the case of ?
, it is possible that the result of the operation will be 0 instances of the type, in which case the static mediator method returns NULL.
Autoboxing is a Java 1.5 language feature that can help simplify your code when working with Java primitive types and their object wrappers. Autoboxing automatically casts between object wrappers such as Integer and their primitive countertypes. With autoboxing, for instance, you can use an Integer object returned from a Mediator API method in a mathematical expression and you can pass an int to a Mediator API method that takes an Integer object. For detailed information on autoboxing, refer to the Sun’s Java documentation.
In Listing 3-14, an Integer object is retrieved from a DASResult is auto-cast to an int.
Note: | If you use an Integer returned from a static mediator method as an int, but the static mediator method actually returns null, you will get a NullPointerException. This can only occur from a data service operation that is declared to return xs:int? – that is, 0 or 1 integers. (This is not unique to int, but to any use of autoboxing.) |
CustomerDAS custdas = CustomerDAS.getInstance(..);
// Invoke a 0-argument procedure that returns xs:int*
DASResult<Integer> result = custdas.getIDs();
while (result.hasNext()) {
int cust = result.next(); // Note use of autoboxing.
}
result.dispose();
Oracle Data Service Integrator enables you to employ user-derived simple types as parameters or return types in data service operations accessed through the Oracle Data Service Integrator Mediator API.
The Mediator API is the Java API for retrieving artifacts from a data service and returning them to their source. In your Java client, you can call Mediator API methods to connect to a data service and invoke data service operations.
This enables you to create a simple type in a schema which restricts a built-in schema type, and use that type as either a parameter type or a return type for a data service operation, successfully invoking the operation through the Oracle Data Service Integrator Mediator API.
For example, you might declare a simple type called ZipCode that derives from xsd:string, as shown in the following:
<xs:simpleType name="ZipCode">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{5}(-[0-9]{4})?"/>
</xs:restriction>
</xs:simpleType>
Oracle Data Service Integrator enables you to use this type as a parameter type or return type for a data service operation.
The mapping of derived schema types to Java types is defined by the SDO specification and, with one exception, is identical to the mapping of the corresponding built-in schema types. For example, if a user type is derived from xs:string, the Java class is java.lang.String. Similarly, if a user type is derived from xs:byte, the Java class is java.lang.Byte.
The single exception involves using XSD types that are derived from xs:integer and include facets (such as minInclusive, maxInclusive, minExclusive, maxExclusive, or enumeration) constraining the range to be within the range of the Java data type int. Oracle Data Service Integrator maps these XSD types to the Java type java.lang.Integer instead of the default type java.math.BigInteger.
Note that this is also true for user XSD types that derive from the following schema built-in types (which are themselves derived from xs:integer):
Note: | You cannot use types derived from xs:QName. Attempting to use these types in data service operations may cause an exception. |
The Oracle Data Service Integrator native web services feature lets you map data services to web services directly. Client applications access data through web services using the Mediator API. Both the Dynamic and Static Mediator APIs support native web services. See Chapter 4, “Invoking Data Services Through Web Services” for detailed information on the native web services feature.
This section includes these topics:
SDO provides a series of APIs that assist with schema management. These APIs include:
A HelperContext object represents SDO’s concept of scope; all schemas loaded into a particular XSDHelper are available and used when creating DataObjects from the XMLHelper or DataFactory of the same HelperContext.
.A Dataspace represents the basic unit of scope for schemas. A Dataspace will not contain any schemas with conflicting type declarations.
For the web services-based mediator, the scope is defined by the WSDL. All schemas necessary for all operations in a WSDL are included in that WSDL, so the WSDL itself forms a reasonable scope for schemas. See Chapter 4, “Invoking Data Services Through Web Services” for more information.
The Mediator automatically keeps a global cache of HelperContexts, and the key to that cache will be either the Dataspace name or, for the web services case, the WSDL URL. The Mediator will automatically use the HelperContext for the appropriate Dataspace/WSDL when creating new DataObjects for the return values of operations.
You can obtain the HelperContext for a given Dataspace/WSDL and use this HelperContext to create your own DataObjects, query the type system, and so on. See Schema Cache Management for more information on the HelperContext API.
This section describes the process of downloading schemas for DataAccessService objects instantiated from data services and WSDLs (web services).
Note: | Schemas are only downloaded when creating a dynamic DataAccessService. Creating an instance of the Static Mediator API never downloads schemas, because the schemas are already compiled into the Static Mediator Client JAR file. |
Schemas are only loaded if they have not previously been loaded. You can set an optional boolean flag on the newDataAccessService() method that requests the mediator not to download schemas. Use this flag if you intend to download schemas manually using methods described in the next section, or if you plan to load them manually using SDO XSDHelper methods.
Tip: | The schema download feature ensures that you do not need to worry about schemas. The default behavior ensures that schemas are available to clients at the appropriate times. |
Use the following methods for querying and manipulating the mediator cache of HelperContexts. These are static methods on the class com.bea.dsp.das.HelperContextCache.
Note: | It is not possible in SDO 2.1 to “unload” schemas from a HelperContext; therefore the only way to change schema information is to create an entirely new HelperContext. |
Note: | Both loadSchemasForDataspace() and loadSchemasForWSDL() return a boolean indicating whether they actually loaded any schemas; they return false if the schemas for the requested data service or WSDL were previously loaded. |
By default, the Mediator API holds resources on the Oracle Data Service Integrator server open while data is being returned. As discussed in Understanding DASResult, data objects are returned through DASResult one object at a time. Oracle Data Service Integrator refers to this strategy as stateful.
Generally, stateful operations are desirable. Stateful behavior allows both the client and the server to minimize memory consumption. The Oracle Data Service Integrator server will only hold open resources as long as is absolutely necessary, and the client will not use more network round trips than are necessary to transfer data to the server.
However, in some cases, you may want to guarantee that the client uses exactly one network round trip. For instance, if your network connection to the Oracle Data Service Integrator server is highly latent or potentially unreliable, using one round trip minimizes response time and ensures that there is no possibility of resources being left open on the server longer than necessary.
To override the default stateful behavior and return results immediately, use the RequestConfig flag FETCH_ALL_IMMEDIATELY. When this flag is specified, the Mediator uses exactly one network round trip to retrieve all the results at once. In addition, server resources are closed immediately.
Note: | The client must have enough memory to materialize the entire result set immediately. In addition, the Oracle Data Service Integrator server will need to fully materialize the result set in memory when this flag is specified. Therefore, it is very important to use this flag when there is any possibility that the result set cannot be held comfortably in memory on both the client and the server. |
Note: | WebLogic Server specifies a maximum amount of data which can be sent in a single network operation. By default, this amount is 10 MB. If you use FETCH_ALL_IMMEDIATELY and the results are larger than this block size, you may receive a weblogic.socket.MaxMessageSizeExceededException. You can change this 10 MB limit in the WebLogic Server Console by selecting: |
Note: | Environment > Servers > (server) > Protocols > General > Maximum Message Size |
When this flag is enabled, all methods that return a DASResult will return one which is already disposed, as explained in Disposing the Result Object.
If you use FETCH_ALL_IMMEDIATELY, you can still use the normal iterator methods of DASResult, or use getItems() to read all the results at once.
This section discusses API features that let you manage data caching through the Mediator APIs.
Data retrieved by data service operations can be cached for quick access. This is known as a data caching. (See “Configuring the Query Results Cache”, in the Oracle Data Service Integrators Administration Guide for details.) Assuming the data changes infrequently, it’s likely that you’ll want to use the cache capability.
When the RequestConfig.GET_CURRENT_DATA attribute is set to true:
evaluation/cache/data/forcedrefresh
indicates that a GET_CURRENT_DATA operation has been invoked.
You can control the data cache using the RequestConfig.REFRESH_CACHE_EARLY attribute.
If the RequestConfig.GET_CURRENT_DATA property is not enabled, you can use the RequestConfig.REFRESH_CACHE_EARLY property to control whether cached data is used based on the remaining TTL (time-to-live) available for the function’s data cache.
The REFRESH_CACHE_EARLY attribute is of type integer. It is set by invoking the RequestConfig.setIntegerAttribute() method. The setting of REFRESH_CACHE_EARLY to a particular value requires that a cached record must have at least n seconds of remaining TTL before it can be used. If the record is set to expire in less than n seconds, it will not be retrieved. Instead its value is recalculated based on the underlying data and the data cache associated with that function is refreshed. The same REFRESH_CACHE_EARLY value applies to all cache operations during a query evaluation.
Note: | The supplied integer value of REFRESH_CACHE_EARLY should always be positive. Negative values are ignored. |
Oracle Data Service Integrator supports a limited subset of XPath expressions called SDO path expressions. SDO path expressions offer flexibility in how you locate data objects and attributes in the dynamic data API’s accessors.
SDO path uses only SDO property names (which can be different from the element/attribute name from schema/xml) in the selector. If there are alias names assigned, those are also used to match. Each step of the path before the last must return a single DataObject.
customer.get("CUSTOMER_PROFILE[1]/ADDRESS[AddressID=\"ADDR_10_1\"]")
The example gets the ADDRESS at the specified path with the specified addressID. If element identifiers have multiple values, all elements are returned.
The get() method returns an Object. If the result of the expression is a property that isMany, the method returns a List of DataObjects.
You can get a data object’s containing parent data object by using the get() method with XPath notation:
myCustomer.get("..")
You can get the root containing the data object by using the get() method with XPath notation:
myCustomer.get("/")
This is similar to executing myCustomer.getRootObject().
Oracle Data Service Integrator fully supports both the traditional index notation and the augmented notation. See “XPath Expressions in the Dynamic Data Object API” on page 2-9 for details.
The DataAccessServiceFactory.prepareExpression() method lets you create ad hoc queries against the data service. Listing 3-15 shows an example of the preparedExpression() method. For more information on ad hoc queries, see “Using Ad Hoc Queries to Fine-tune Results from the Client” on page 8-25.
PreparedExpression pe = DataAccessServiceFactory.prepareExpression
(ctx, appname, "18 + 25");
DASResult<Object> result = pe.executeQuery();
int answer = (int) result.next();
result.dispose();
This section discusses the transaction behavior of read/write and read-only operations and queries.
The Oracle Data Service Integrator server always creates a new transaction if necessary when executing read/write operations, such as create, update, or delete.
By default, read operations make use of a transaction if one is currently active on the client; however, if no transaction is open, one will not be created. You can change this default by setting an attribute on RequestConfig and passing RequestConfig as a parameter to invoke(). (For detailed information on invoke() see the Javadoc.)
The attribute RequestConfig.ReadTransactionMode lets you set one of the following values to configure transaction behavior of read-only operations.
Use the RequestConfig.setEnumAttribute() method to set the ReadTransactionMode attribute. For example, the following code sets the ReadTransactionMode mode to SUPPORTS.
RequestConfig config = new RequestConfig();
config.setEnumAttribute(RequestConfig.ReadTransactionMode.SUPPORTS);
![]() ![]() ![]() |