Client Application Developer's Guide

     Previous  Next    Open TOC in new window    View as PDF - New Window  Get Adobe Reader - New Window
Content starts here

Accessing Data Services from Java Clients

This chapter describes how your Java client applications can access data services. It covers the following topics:

 


Overview of the AquaLogic Data Services Platform Mediator API

The BEA AquaLogic Data Services Platform Mediator API gives Java client application developers easy-to-use interfaces for using data service routines. To use the Mediator API, simply instantiate a remote data service interface and invoke public methods on the interface. Public methods can include read functions, navigation functions, and procedures.

The return type for the invocations depends on the type of method and whether the static or dynamic interfaces are used, as follows:

The Mediator API provides both static and dynamic interfaces for working with data services.

The Mediator API also supports several advanced features, including:

The Mediator APIs are used to instantiate interfaces to data services and invoke data service functions and procedures. Functions and procedures defined for a data service are available as methods in the Mediator API.

The dynamic Mediator API classes and interfaces are in the following JAR file:

	ld-client.jar 

The Data Service Mediator package is named as follows:

	com.bea.dsp.dsmediator.client

The API consists of the classes and interfaces listed in Table 3-1

Table 3-1 AquaLogic Data Services Platform Mediator API 
Interface or Class Name
Description
DataService
Interface for data services that returns data as Data Objects. The interface includes invoke() method for invoking read and navigation functions; invokeProcedure() for invoking procedures; and submit() method for submitting data object changes.
PreparedExpression
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.
DataServiceFactory
Factory class for creating local interfaces to data services. Can be used for dynamic data service instantiation and ad hoc queries.
StreamingDataService
Interface for data services that returns data as a token stream.
StreamingPreparedExpression
Interface for preparing and executing ad hoc query functions that return information as a stream. An ad hoc query is an XQuery that is passed as a string from within a client program (rather than in the data service).

The static mediator API interface extends the static Mediator interface, as shown in this example of a class declaration for a typed data service:

public class dataservices.Customer extends
com.bea.dsp.dsmediator.client.DataService { ... }

The static data service interface is in the SDO Mediator Client JAR files generated from an AquaLogic Data Services Platform project.

The exception class for Mediator errors (SDOMediatorException) is in the following package:

	com.bea.ld.dsmediator.client.exception

Exceptions that are generated by the data source (such as SQLException) are wrapped in an SDO Exception, and can be accessed by calling getCause() on the SDOMediatorException.

Setting the Classpath

To develop Java-based client programs using the Mediator API, your development environment's classpath must include in the following order the JAR files listed in Table 1-5, AquaLogic Data Services Platform Java Archive (JAR) Files, on page 1-12.

In addition, to use static data APIs, you must include the <app-name>-ld-client.jar file (obtain from your data service architect or administrator).

Note: The listed order is significant and must be followed. The <app-name>-ld-client.jar file is not needed for dynamic SDO.

As an example, for a data service named Demo using static APIs, your classpath on a Microsoft Windows operating system would include:

    set CLASSPATH=%CLASSPATH%;Demo-ld-client.jar;
C:\bea\weblogic81\server\lib\weblogic.jar;
C:\bea\weblogic81\liquiddata\lib\wlsdo.jar;
C:\bea\weblogic81\server\lib\xbean.jar;
C:\bea\weblogic81\server\lib\xqrl.jar;
C:\bea\weblogic81\server\lib\wlxbean.jar;
C:\bea\weblogic81\liquiddata\lib\ld-client.jar;

This classpath assumes that the first item, Demo-ld-client.jar, is in the current directory and that the BEA WebLogic home directory is: C:\bea\weblogic81. Modify the path to the locations appropriate for your system, and change the name of Demo-ld-client.jar to the actual name of the JAR file generated from your AquaLogic Data Services Platform-enabled application.

Mediator API Summary and Reference

Client application developers can take two alternative approaches to working with SDOs:

This chapter discusses the Mediator APIs and how to use in Java client applications. Data Service controls are discussed in Accessing Data Services from WebLogic Workshop Applications.

Client application developers will use some combination of the APIs shown in Table 3-2, depending on your application design and specific goals. Data service developers will also use the SDO Update API (specifically, the UpdateOverride interface) to customize data service functionality.

Table 3-2 Data Service Mediator APIs Package Reference
 
SDO Mediator APIs
SDO Update APIs
Package
com.bea.dsp.dsmediator.client
com.bea.ld.dsmediator.update
Description
DataServiceFactory and other classes. DataService, StreamingDataService, and PreparedExpression interfaces.
DataServiceMediatorContext, DataServiceToUpdate, KeyPair, DataServiceMediator, and UpdatePlan classes. UpdateOverride interface.
Usage note
Instantiate remote interface to a data service.
Submit changed data objects to data service. Override default update processing for a particular data service.
Location
<bea_home>\weblogic81\liquiddata\lib\ld-client.jar
Same as for SDO Mediator APIs.
Javadoc
Both these documents can be accessed from the AquaLogic Data Services Platform edocs home page:
Same as for SDO Mediator APIs.

 


Generating a Static Mediator API JAR File

Client applications can access the classes representing a static data service interface using the JAR (Java Archive) file generated from the AquaLogic Data Services Platform project. Client application developers must obtain this JAR file (typically, from the data services architect or the AquaLogic Data Services Platform administrator) and add the JAR file in the classpath of their development environment.

The naming convention for the generated, static Mediator client JAR file is:

<AppName>-ld-client.jar

Building the Client JAR

Once the data service application has been built into an EAR file, the client version of the data service — the <AppName>-ld-client.jar file — can be generated from the EAR. The client version includes wrapper classes that allow the client to call the data service functions through a dynamic or a static API.

The necessary JAR file can be generated in either of two ways:

Table 3-3 describes the available arguments that can be used to generate a static client JAR mediator.

Table 3-3 Generating a Static Client JAR Mediator from a Data Services EAR
Argument
Description
<archive>
Fully qualified name of the generated EAR file. The generated name is derived from the name of the application.
<outdir>
Directory in which to generate the client JAR file. Optional parameter; if unspecified, the current directory is used.
<tmpdir>
Directory in which to produce the temporary, expanded EAR file contents. Although this parameter is optional, BEA recommends that you always create and specify a temporary directory, since all contents will be deleted at the end of the process. If <tmpdir> is not specified, the current directory will be used.

Using the Data Service Mediator API

To use the Data Service Mediator API to invoke data service functions and procedures, create a Java class as follows:

  1. Import the com.bea.dsp.dsmediator.client package.
  2. Create a JNDI context for the WebLogic Server that hosts the AquaLogic Data Services Platform application.
  3. Note: For more information, see Obtaining a WebLogic JNDI Context for AquaLogic Data Services Platform. For complete information about WebLogic Server contexts, see:
    
    http://download.oracle.com/docs/cd/E13222_01/wls/docs81/javadocs/weblogic/jndi/WLInitialContextFactory.html
  4. Instantiate remote interfaces for the data service. You can use either a static or dynamic mediator API interface. The dynamic interface in that the data service name is passed as an argument. For example:
  5. DataService ds = DataServiceFactory.newDataService(
    JndiCntxt, "RTLApp", "ld:DataServices/RTLServices/Customer");

    Here is the same operation using a static interface:

    CUSTOMER ds =  CUSTOMER.getInstance(JndiCntxt, "RTLApp");
  6. Invoke a function or procedure on the data service.
  7. The following is the operation using the dynamic interface to invoke a read function on a data service:

    Object[] params = new Object { "CUSTOMER1" };
    DataObject[] myCustomer =
    (DataObject[]) ds.invoke("getCustomerByCustID", params);

    Here is the same operation using a static interface:

    CUSTOMERDocument myCust = ds.getCustomerByCustID("CUSTOMER1");

Obtaining a WebLogic JNDI Context for AquaLogic Data Services Platform

Java client applications use JNDI to access named objects, such as data services, on a WebLogic Server. A single JNDI call is made to obtain an initial context, which is then passed to the data services factory class. Once you have the server context, you can invoke functions and acquire information from data services.

To get the WebLogic Server context, set up the JNDI initial context by specifying the INITIAL_CONTEXT_FACTORY and PROVIDER_URL environment properties:

A local client (that is, a client that resides on the same computer as the WebLogic Server) may bypass these steps by using the settings in the default context obtained by invoking the empty initial context constructor; that is, by calling new InitialContext( ).

At this stage, the client may also authenticate itself by passing its security context to the corresponding JNDI environment properties SECURITY_PRINCIPAL and SECURITY_CREDENTIALS.

The code excerpt below is an example of a remote client obtaining a JNDI initial context using a hashtable.

   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);

Be sure to replace the machine name and username/password with values appropriate for your environment.

Invoking Functions and AquaLogic Data Services Platform Procedures

The Dynamic Mediator API provides two different methods (see Table 3-4) for invoking functions and procedures, respectively:

In your code, you must use the appropriate method call — invoke() or invokeProcedure() — for the functions and procedures, respectively, to avoid raising exceptions, as noted in Table 3-4.

Table 3-4 Method Signature of the Data Service Mediator API (Client Mediator API)
Method Signature
Description
invoke(String method, Object[] args)
Method to invoke a data service's read and navigate functions. Using invoke() with a AquaLogic Data Services Platform procedure raises an exception.
invokeProcedure(String method, Object[] args)
Method to invoke a data service's procedures (stored procedures, Web services, and Java code that have side effects). Using invokeProcedure() with a read or navigation function raises an exception.
submit(DataObject sdo)
Method to submit changes to the Mediator service. Assumes that a change summary exists as part of the DataObject.

For more information, see information on Data Services API Javadocs at AquaLogic Data Services Platform Mediator API Javadoc on page 1-13.

When using static mediator APIs, the distinction between invoking AquaLogic Data Services Platform functions and procedures is hidden. Read and navigate functions, as well as procedures, are named based on the function name, with no indication as to whether (or not) they are side-effecting procedures.

Static and Dynamic Mediator APIs

Once you have obtained an initial context to the server containing AquaLogic Data Services Platform artifacts, you can instantiate a remote interface for a data service. If you know the data service type at development time, you can use the static data service interface, which uses static data objects.

Alternatively, the dynamic interface lets you use data services specified at runtime. The static interface gives you a number of advantages, including type validation and code completion when using development tools, such as Eclipse or your favorite development tool.

Using a Static Data Service Mediator API

To use the static data service interface, you must have the SDO Mediator Client JAR file that was generated from the specific AquaLogic Data Services Platform-enabled application. (If you do not have the JAR file, contact your administrator to acquire it.)

Add the JAR file to your client application's build path and import the data service package into your Java class file that will be the basis for your client application.

For example, to use a data service named Customer in a AquaLogic Data Services Platform project named customerApp, use the following import statement:

	import customerapp.Customer; 

With the imported factory classes and interfaces available in your Java application, you can instantiate the interface to the specific data service by invoking the getInstance() method with the following arguments:

Once you have a remote data service instance, you can invoke functions and procedures on the data service. For example, consider the data service shown in Figure 3-5.

Figure 3-5 Customer Data Service


Based on the data service shown in Figure 3-5, the generated artifacts for a typed client interface would include static methods for both dynamic data APIs and the static Mediator APIs (see Listing 3-1). As shown in Listing 3-1, each read and navigate function from the data service results in a static data API method, such as getCustomer( ) and getApplOrder( ).

Listing 3-1 Generated Static Methods of the Customer DataService Class
getCustomer()
getCustomerByCustID(String)
getCustomerByCustIDToFile(String, String)
getCustomerByZip(String)
getCustomerByZipToFile(String, String)
getCase(CUSTOMERPROFILEDocument)
getCreditCard(CUSTOMERPROFILEDocument)
getApplOrder(CUSTOMERPROFILEDocument)
getElecOrder(CUSTOMERPROFILEDocument)
getCustomerByLoginID(String)

See Static Data API for more information about generated SDO data API methods, such as those listed above (getCustomer() and get CustomerByLoginID(), for example).

There are several DataService methods that are part of the dynamic API which are inherited by all static DataService classes. These include:

Listing 3-2 shows a small but complete example of using the static interface.

Listing 3-2 Mediator Client Sample Using the Static Interface to a Data Service
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import dataservices.rtlservices.Customer; //
import retailerType.CUSTOMERPROFILEDocument;

public class MyTypedCust
{
public static void main(String[] args) throws Exception {
//Get access to AquaLogic Data Services Platform data service
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL,"t3://localhost:7001");
h.put(Context.SECURITY_PRINCIPAL,"weblogic");
Context context = new InitialContext(h);

// Use the typed Mediator API
Customer customerDS = Customer.getInstance(context, "RTLApp");
CUSTOMERPROFILEDocument[] myCust =
     customerDS.getCustomerByCustID("CUSTOMER2");
System.out.println(" CUST" + myCustomer);
}
}

Using a Dynamic Mediator API

The dynamic data service interface is useful for programming with data services that are unknown or do not exist at development time. It is useful, for example, for developing tools and user interfaces that work across data services.

With the dynamic interface, names of specific data services are passed as parameters in the generic get( ) and set( ) method calls. For example:

DataService ds = DataServiceFactory.newDataService(
context, "RTLApp", "ld:DataServices/RTLServices/Customer");
Object[] params = {"CUSTOMER2"};
DataObject myCustomer = (DataObject)ds.invoke("getCustomerByCustomerID", params);
System.out.println(myCustomer.get("Customer/LastName"));

A data object returned by the dynamic interface can be downcast to a static object, as follows:

     DataService ds = 
DataServiceFactory.newDataService(
context, "RTLApp", "ld:DataServices/Customer");
Object[] params = {"CUSTOMER2"};
CUSTOMERDocument myCustomer =
(CUSTOMERDocument) ds.invoke("getCustomer", params);
System.out.println(myCustomer.getCUSTOMER().getCUSTOMERNAME() );
Note: This code example only works if the generated static SDO mediator JAR is on the classpath at compile time and at runtime.

For a dynamic data service, use the newDataService( ) method of the DataServiceFactory class. In the method call, pass the following arguments:

Listing 3-3 shows a full example.

Listing 3-3 Mediator Client Sample Using the Dynamic Mediator API Data Service Interface
import com.bea.ld.dsmediator.client.DataService;
import com.bea.ld.dsmediator.client.DataServiceFactory;
import commonj.sdo.DataObject;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;

public class MyUntypedCust
{
public static void main(String[] args) throws Exception {

//Get access to Liquid Data
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL,"t3://localhost:7001");
h.put(Context.SECURITY_PRINCIPAL,"weblogic");
h.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context context = new InitialContext(h);

// Use the dynamic (untyped) Mediator API
DataService ds =
DataServiceFactory.newDataService(context, "RTLApp",
"ld:DataServices/RTLServices/Customer");
DataObject myCustomer = (DataObject) ds.invoke("getCustomer", null);
System.out.println(" Customer Information: \n" + myCustomer);
}
}

Static and Dynamic SDO APIs

You can invoke data service functions using either static or dynamic SDO APIs. The dynamic API is often called generic SDO, since you do not need to materialize the SDO object on the client side through a JAR file. Instead, you simply invoke the appropriate set( ) or get( ) method based on your knowledge of underlying schema of the data service.

Each approach has its advantages and disadvantages, as described in the Table 3-6, below:

Table 3-6 Static vs. Dynamic Mediator APIs
Method
Advantages
Disadvantages
Static (typed)
  • Runtime type validation
  • Code completion in most IDEs
  • Requires generation of [App]-ld-client JAR file
Dynamic (untyped), using generic SDO
  • Easily adapt to schema changes
  • Unnecessary to compile Java classes from their schema
  • Less overhead
  • No runtime type checking

The static and dynamic Mediator API options are described in detail in:

Using a Static Data Service Mediator API

Using a Dynamic Mediator API

Using the Static SDO API

Once you have obtained an initial context to the server containing AquaLogic Data Services Platform artifacts, you can instantiate a remote interface for a data service. If you know the data service type at development time, you can use the static data service interface, which uses static data objects. (Alternatively, the dynamic SDO interface lets you use data services specified at runtime. It is described under the topic Using a Dynamic Mediator API.)

A static interface gives you a number of advantages, including type validation and code completion when using development tools, such as Eclipse or your favorite development tool.

To use the static data service interface, you must have the SDO Mediator Client JAR file that was generated from the specific AquaLogic Data Services Platform-enabled application that contains the query functions you want to use with your client application. (If you do not have the JAR file, contact your administrator to acquire it.)

Add the JAR file to your client application's build path and import the data service package into your Java class file that will be the basis for your client application.

For example, to use a data service named Customer in a AquaLogic Data Services Platform project named customerApp, use the following import statement:

	import customerapp.Customer; 

With the imported factory classes and interfaces available in your Java application, you can instantiate the interface to the specific data service by invoking the getInstance( ) method with the following arguments:

Once you have a remote data service instance, you can invoke functions and procedures on the data service. For example, consider the data service shown in Figure 3-5.

Figure 3-7 Sample Customer Data Service


The generated artifacts for a static client interface would include typed methods for both dynamic data APIs and the static Mediator APIs. As shown in Listing 3-1, each read and navigate function from the data service results in a static data API method, such as getCustomer( ) and getApplOrder( ).

Listing 3-4 Generated Static Methods for the Customer DataService Class
getCustomer()
getCustomerByCustID(String)
getCustomerByCustIDToFile(String, String)
getCustomerByZip(String)
getCustomerByZipToFile(String, String)
getCase(CUSTOMERPROFILEDocument)
getCreditCard(CUSTOMERPROFILEDocument)
getApplOrder(CUSTOMERPROFILEDocument)
getElecOrder(CUSTOMERPROFILEDocument)
getCustomerByLoginID(String)

See Static Data API for more information about generated SDO data API methods, such as those listed above.

There are several DataService methods that are part of the dynamic API which are inherited by all static DataService classes including the following methods:

Listing 3-2 shows a small but complete example using a static interface.

Listing 3-5 Mediator Client Sample Using a Static Interface to a Data Service
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import dataservices.rtlservices.Customer; //
import retailerType.CUSTOMERPROFILEDocument;

public class MyTypedCust
{
public static void main(String[] args) throws Exception {
//Get access to AquaLogic Data Services Platform data service
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL,"t3://localhost:7001");
h.put(Context.SECURITY_PRINCIPAL,"weblogic");
Context context = new InitialContext(h);

// Use the typed Mediator API
Customer customerDS = Customer.getInstance(context, "RTLApp");
CUSTOMERPROFILEDocument[] myCust =
     customerDS.getCustomerByCustID("CUSTOMER2");
System.out.println(" CUST" + myCustomer);
}
}

Using the Dynamic SDO API

The dynamic data service interface — or generic SDO — is ideal for programming with data services that are unknown or do not exist at development time.

With dynamic SDO, DataObjects depend on the XML schema to determine:

The dynamic SDO correctly supports SDO APIs through get( ) and setType( ) on DataObject.

Such a SDO definition consists of a single generic DataGraph and a number of DataObject classes.

Dynamic SDOs are created using a createRootDataObject( ) method:

DataObject /* root SDO document */ createRootDataObject()

The code fragment in Listing 3-6 illustrates these familiar operations using dynamic SDO:

How XML Schemas Are Made Available to Dynamic SDO Operations

XML schemas are not available at client side, the dynamic mediator must download schemas from the server. The internal mean of downloading schemas varies depending on whether you have an EJB client or a Web service client.

Schema Type Caching

Generating the SchemaTypeSystem can be a costly operation. For this reason schema caching functionality is provided that allows for schema reuse and lifecycle management through flush and clear APIs.

If no cache is passed in to get a data service instance on the client side, then an internal cache is created. The default lifetime of the internal schema cache is the same as the lifetime of the data service instance.

You can create a cache object per data service or for multiple data services. All caches are thread-safe. (As there is no differentiator across multiple AquaLogic Data Services Platform applications, caches should not be shared across multiple applications.)

The following schema caching APIs are available:

Schema Cache Management Scenarios

The following represents several schema cache management scenarios:

Bypassing the Data Cache When Using the Mediator API

Data retrieved by data service functions can be cached for quick access. This is known as a data caching. (See Configuring the Query Results Cache, in the AquaLogic Data Services Platform Administration Guide for details.) Assuming the data changes infrequently, it's likely that you'll want to use the cache capability.

You can bypass the data cache by passing the GET_CURRENT_DATA attribute within a function call, as shown in Listing 3-7. GET_CURRENT_DATA returns a Boolean value. As a by-product, the cache is also refreshed.

Listing 3-7 Cache Bypass Example When Using Mediator API
dataServices.customermanagement.CustomerProfile customerProfileDS = customerDS=dataServices.customermanagement.CustomerProfile.getInstance(ctx,appName);

RequestConfig config = new RequestConfig();

attr.enableFeature(RequestConfig.GET_CURRENT_DATA);

CustomerProfileDocument customerProfileDoc customerProfileDS.CustomerProfile(params,config);

Client Management of the Data Cache

When invoking a AquaLogic Data Services Platform query you can more precisely control the behavior of the data cache by using the REFRESH_CACHE_EARLY attribute in conjunction with GET_CURRENT_DATA attribute.

Setting the GET_CURRENT_DATA Attribute

When the GET_CURRENT_DATA attribute is set to True:

SETTING the REFRESH_CACHE_EARLY Attribute

If the GET_CURRENT_DATA property is set to False or is not invoked, you can use the REFRESH_CACHE_EARLY 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 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.

 


Accessing Data Services Via WebLogic Server 9.2 Clients

This section describes how to make AquaLogic Data Services Platform 2.x and WebLogic Server 9.2 interoperable. Once interoperability is set up, a WebLogic Server 9.2 client can access AquaLogic Data Services Platform 2.x data services.

Sample Customer Data Service

Notes: AquaLogic Data Services Platform 2.x runs under WebLogic Server 8.x.

Follow these steps to enable interoperability between a WebLogic Server 9.2 client and AquaLogic Data Services Platform 2.x:

Interoperability Steps

  1. Put ${WLS_HOME}/liquiddata/lib/wls90interop.jar in PRE_CLASSPATH on the AquaLogic Data Services Platform 2.x server by editing setDomainEnv.{cmd|sh}
  2. Note: After this change, all the non JDK 1.5 based clients of the AquaLogic Data Services Platform 2.x server will need to have wls90interop.jar in their classpath ahead of all other classes.
  3. Put ${WL_HOME}/liquiddata/lib/wls90interop.jar first in the classpath of WebLogic Workshop by modifying the value for the -cp key in Workshop.cfg
  4. Build the application inside Workshop.
  5. Build the client-side JAR using ${WL_HOME}/liquiddata/bin/ld_clientapi.xml with the following arguments on the command line:
  6. -Dapproot=<application directory>

    -Dxbeanjarpath=${WEBLOGIC9x}/server/lib/xbean.jar

  7. Copy ${WL_HOME}/liquiddata/lib/wlsdo90interop.jar, ld-client.jar and <appName>-ld-client.jar into WEB-INF/lib or classpath on the 9.2 server side.
  8. Note: If you are using the WebLogic 9.0 release, you will need to upgrade the XMLBeans V2 shipped with WLS 9.0. You will need to use XMLBeans from WLS 9.1 or above.

 


Step-by-Step: A Java Client Programming Example

This section describes common Java client application programming tasks:

Client application development encompasses the SDO data APIs; client Mediator APIs (which are used to instantiate a local proxy to the remote server); and possibly the Update SDO API (to submit changed data objects to the data service). Thus, the steps in this section include calls using the Mediator APIs—getInstance( ) and submit( ), for example, as well as SDOs.

Step 1. Instantiating and Populating Data Objects

Working with SDO data objects from a client application starts by obtaining an interface (either static or dynamic) to the data service. Depending upon the approach you take, you must import the generated static data type interfaces or dynamic data interfaces, as well as the data service interfaces.

Table 3-8 lists static and dynamic mediator API interfaces.

Table 3-8 Static and Dynamic Mediator API Interfaces
Static Mediator API
Dynamic Mediator API

Customer cust = Customer.getInstance(
  context, "MyApp");

DataService ds =
DataServiceFactory.newDataService(
  context, "MyApp",  "ld:DataServices/CustomerDB/CUSTOMER");

Instantiating a local interface for an static mediator API is done by passing the context, the application name, and the data service name to the DataServiceFactory class. For the static mediator API, the local interface is instantiated using the getInstance() method (after establishing a JNDI context).

Once the local interface is constructed, you can invoke data service functions to obtain a data object.

As discussed in Data Services Platform and Service Data Objects (SDOs) on page 2-1, the returned data object is associated with a data graph. The data graph also provides a handle to the root data object of the data graph.

Table 3-11 shows both a static and dynamic approach to populating data objects. The static data API example shows how to instantiate the root node of the data graph, in this case, using the data that comprises a logical data service function (getCustomerView( ) ). The example is selecting information about Customer3.

In the dynamic example, the root node of a data graph is being populated with an array of all customers available through the data service.

Table 3-9 shows the static and dynamic mediator APIs available to instantiate SDOs.

Table 3-9 Static and Dynamic Mediator APIs
Static Mediator API
Dynamic Mediator API
CUSTOMERDocument[] custDoc =
ds.getCustomerView("CUSTOMER3");

DataObject[] custDoc =
  ds.invoke("CUSTOMER", null);

Step 2: Accessing Data Object Properties

After obtaining a data object, you can access its properties using either its generated static data API or the dynamic data API. Table 3-10 shows side-by-side comparisons of using the static and dynamic methods to access properties. The static interface returns a single CUSTOMER object, while the dynamic interface returns a generic data object.

Table 3-10 Static and Dynamic Mediator API Property Acquisition Examples
Static Mediator API
Dynamic Mediator API
CUSTOMERDocument.CUSTOMER cust = custDoc[0].getCUSTOMER();
String lastName = cust.getLASTNAME();

DataObject cust = custDoc[0].get("CUSTOMER");
String lastName = (String) cust.get("LAST_NAME");

With the static interface, the type name (as a string) is passed as a parameter to the dynamic get( ) method. The returned object can be then cast to the necessary type.

If the return type is unbounded, you need to cast the returned object to a List. To traverse all objects in an unbounded type you must use an iterator, as shown in Listing 3-8.

Listing 3-8 Using an Iterator to Traverse a List of Returned Data Objects
List addressList = (List) cust.get("ADDRESS");
Iterator iterator = addressList.iterator();
while ( iterator.hasNext() ){
CUSTOMERDocument.CUSTOMER.ADDRESS address =       (CUSTOMERDOcument.CUSTOMER.ADDRESS) iterator.next();
}
}

You can identify properties in SDO accessor arguments by element name. Accessor methods can take property identifiers specified as XPath expressions, as follows:

   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 identical values, all elements are returned.

For example, the ADDRESS also has a CustomerID (a customer can have more than one address), so all addresses would be returned. (Note that the get( ) method returns a DataObject, so you will need to cast the returned object to the appropriate type. For unbounded objects, you must use a List.)

Note: For specifying index position, note that SDO supports regular XPath notation (one-based) and Java-style (zero-based). See XPath Support in the Dynamic Data API for more information.

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.getDataGraph( ).getRootObject( ).

Quantifying Return Types

Return types from data service functions can be quantified based on the semantics shown in Table 3-11.

Table 3-11 Quantifying Return Type Symbols and Their Definition
Quantifier Symbol
Definition
Comments
+
Same semantics as star (*)
Returns all.
?
Same semantics as unqualified except that there is additional built-in logic to handle the possibility of empty results.
When the results are empty, the return value of the static mediator method will be either:
  • Null. Null is returned if the Java return type is non-primitive (such as typed subclasses of DataObject, BigDecimal, String, and so forth).
  • Best value. Best value is returned if the Java return value is a primitive (such as int, float, and so forth).
"Best value" is the same as is returned when RequestConfig prevents results from being sent, for instance in the OUTPUT_FILENAME case.
For integer numeric types, best value is the corresponding MIN_VALUE; for floating point numeric types, best value is NaN (not a number); for Boolean, best value is False.

Step 3: Modifying, Inserting, and Deleting Data Objects and Properties

By default, change tracking on the data graph is enabled so that any changes made to object values are recorded in the change summary.

Modifying Data Object Properties

Table 3-12 provides examples showing how you can modify data object property values using either dynamic or static set( ) methods.

Table 3-12 Examples of Static and Dynamic Mediator API Setting of Properties
Static Mediator API
Dynamic Mediator API
cust.setLASTNAME("Smith");
cust.set("LAST_NAME", "Smith");

Both approaches take string arguments for the new property values; both approaches result in changing the customer object's last name to Smith. The static mediator API example assumes that you have instantiated the static interface on the data service.

Inserting New Data Objects

You can create new a data object by using an addNew() method (a static data API). A new data object can be added to a root data object or, more commonly, as a new element in a data object array. (New arrays can also be added to data objects.) When inserting an object to an array, you must be sure to set any and all required fields for the new object, as specified by its XML schema, before calling submit().

Listing 3-9 shows how to insert a data object into an array of objects.

Listing 3-9 Inserting a New Data Object into an Array
CUSTOMERDocument.CUSTOMER newCust = custDoc[0].addNewCUSTOMER();
  int idNo = custDoc.length;
  newCust.setCUSTOMERID("CUSTOMER" + String.valueOf(idNo));
  newCust.setFIRSTNAME("Clark");
  newCust.setLASTNAME("Kent");
  newCust.setCUSTOMERSINCE(java.util.Calendar.getInstance());
  newCust.setEMAILADDRESS("kent@dailyplanet.com");
  newCust.setTELEPHONENUMBER("555-555-5555");
  newCust.setSSN("509-00-3683");
  newCust.setDEFAULTSHIPMETHOD("Air");
Inserting a New Data Object

A variation on the theme is to create a completely new data object. In RDBMS terms this would be considered creating a new record.

Listing 3-10 illustrates creation of a data object called customer "from scratch":

Listing 3-10 Creating a New Data Object
queryPlansDataServices.customer.CUSTOMERDocument customerDocument= queryPlansDataServices.customer.CUSTOMERDocument.Factory.newInstance();
queryPlansDataServices.customer.CUSTOMERDocument.CUSTOMER

customer=customerDocument.addNewCUSTOMER();
customer.setLASTNAME("KAY_99");
customer.setFIRSTNAME("JOHN_99");
customer.setCUSTOMERID("CUSTOMER_99");
customer.setCITY("SAN JOSE");
customer.setCUSTOMERSINCE(Calendar.getInstance());
customer.setEMAILADDRESS("m@a.com");
customer.setTELEPHONENUMBER(new BigInteger("4085708612")); customer.setZIPCODE(new BigInteger("95131")); customer.setSTATE("CA");
RDBMS Considerations

If the data source associated with the object being added is an RDBMS, note these additional considerations:

Deleting Data Objects

To delete a data object, you must delete it from the data graph that contains it. For example, Listing 3-11 searches a CUSTOMER array for a specific customer's name and deletes that customer.

Listing 3-11 Deleting a Data Object
dataServices.customer.CUSTOMERDocument[] customers=customerDS.CUSTOMER();
for (int i=0; i < customers.length; i++){
if (customers[i].getCUSTOMER().getFIRSTNAME().equals("JOHN_99") && customers[i].getCUSTOMER().getLASTNAME().equals("KAY_99")) {
customers[i].getCUSTOMER().delete();
customerDS.submit(customers[i]);
}
}

When you remove an object from its container, only the reference to the object is deleted, not the values; values are deleted later, during Java garbage collection.

The data object interface (DataObject in the commonj.sdo package) provides the delete( ) method for deleting objects.

Deleting an object is a cascade-style operation; that is, children of the deleted object are deleted as well. However, note that the deleted object only—not its children—is tracked in the change summary as having been deleted.

Step 4: Submitting Changes to the Data Service

To submit data changes, call the submit() method on the data service bound to an object, passing the root changed object as in:

custDS.submit(myCustomer);

A basic example of a submit operation is shown in Listing 3-12.

Listing 3-12 Static Interface
CUSTOMER custDS = CUSTOMER.getInstance(ctx, "RTLApp");
CUSTOMERDocument[] custDoc = (
CUSTOMERDocument[]) custDS.CUSTOMER();
custDoc[0].getCUSTOMER().setLASTNAME("Nimble");
custDS.submit(CustDoc);

Listing 3-13 demonstrates making changes to a data object using the dynamic interface.

Listing 3-13 Dynamic Interface
import com.bea.dsp.dsmediator.client.DataService;
import com.bea.dsp.dsmediator.client.DataServiceFactory;
import commonj.sdo.DataObject;

DataService custDS =
DataServiceFactory.newDataService(getInitialContext(), "MyDSPApp",
"ld:MyDSPAppDataServices/CUSTOMER");
DataObject[] custDocs = (DataObject[])custDS.invoke("CUSTOMER",
null);
DataObject custDoc=custDocs[0];
DataObject customer=(DataObject)custDoc.get("CUSTOMER");
customer.set("LAST_NAME","Nimble");
custDS.submit(custDoc);

 


Examining a Java Client Application

Listing 3-14 shows a complete example that recaps many of the steps described above. The example SDO client application shows how the static mediator API is used to create a handle to the CUSTOMER data service.

The client application extracts information about a customer, modifies the information, and then submits the changes. In addition to demonstrating some of the basics of SDO client programming, Listing 3-14 also shows how the Mediator API is used to obtain a handle to the data service, and how the Update Mediator API is used to submit the changes to the data service.

Listing 3-14 Sample Client Application
import java.util.Hashtable;
import javax.naming.InitialContext;
import dataservices.customerdb.CUSTOMER;
public class ClientApp {
   public static void main(String[] args) throws Exception {
      Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL,"t3://localhost:7001");
h.put(Context.SECURITY_PRINCIPAL,"weblogic");
h.put(Context.SECURITY_CREDENTIALS,"weblogic");
Context context = new InitialContext(h);
      // create a handle to the Customer data service
CUSTOMER custDS = CUSTOMER.getInstance(context, "RTLApp");
// use dynamic data API to instantiate an SDO (shaped as a "Customer")
CUSTOMERDocument[] myCustomer =
(CUSTOMERDocument[]) custDS.invoke("CUSTOMER", null);
     // get and show customer name
String existingFName =
myCustomer[0].getCUSTOMER().getFIRSTNAME();
String existingLName =
myCustomer[0].getCUSTOMER().getLASTNAME();
     System.out.println(" \n---------------- \n Before Change: \n");
System.out.println(existingFName + existingLName);
     // change the customer name
myCustomer[0].getCUSTOMER().setFIRSTNAME("J.B.");
myCustomer[0].getCUSTOMER().setLASTNAME("Kwik");
custDS.submit(myCustomer,"ld:DataServices/CustomerDB/CUSTOMER");
     // re-query and print new name
     myCustomer = (CUSTOMERDocument[]) custDS.invoke("CUSTOMER",null);
String newFName =
myCustomer[0].getCUSTOMER().getFIRSTNAME();
String newLName =
myCustomer[0].getCUSTOMER().getLASTNAME();
     System.out.println(" \n---------------- \n After Change: \n");
System.out.println(newFName + newLName); }
}

Listing 3-14 highlights how to use the SDO data APIs and the Mediator API, as follows:

  1. The application instantiates the remote interface to the Customer data service, passing a JNDI context that identifies the WebLogic Server where AquaLogic Data Services Platform is deployed. The static Mediator API is used in this call to instantiate the actual Customer data service interface (rather than the dynamic DataService interface):
  2. CUSTOMER custDS = CUSTOMER.getInstance(context, "RTLApp");

    The custDS serves as a handle for the CUSTOMER data service that is executing on the RTLApp WebLogic Server application.

  3. The program uses the Mediator API to invoke a read function on the Customer data service, pouring the results into an array of CUSTOMERDocument objects:
  4. CustomerDocument[] myCustomer = 
    ( CustomerDocument[]) ds.invoke("CUSTOMER", null);
  5. Once the data object is created, its properties can be accessed using SDO's static data API (the static interface), which returns the actual type of that node:

    myCustomer[0].getCUSTOMER().getFIRSTNAME();
  6. New values for the FIRSTNAME and LASTNAME property of the CUSTOMER are set using the static data API:
  7. myCustomer[0].getCUSTOMER().setFIRSTNAME("J.B.");
    myCustomer[0].getCUSTOMER().setLASTNAME("Kwik");
  8. The change is submitted to the data service (by using the Client Mediator API's submit() method) for propagation to the back-end data sources:
  9. custDS.submit(myCustomer);
  10. The Mediator API's invoke() method is executed once more, and the results (now showing the changed data) are printed to output.
Note: The invoke( ) method is for read and navigation functions only. For data service procedures, use the invokeProcedure( ) method available in the DataService interface. For details on the Mediator API see AquaLogic Data Services Platform Javadoc, described under AquaLogic Data Services Platform Mediator API Javadoc on page 1-13.
Note: See Invoking Functions and AquaLogic Data Services Platform Procedures for more information about procedures.

Although code for handling exceptions is not shown in the example, an SDO runtime error throws an SDOMediatorException. The SDOMediatorException class is also used to wrap data source exceptions.


  Back to Top       Previous  Next