Skip navigation.
Downloads | Product Documentation | Support

Client Application Developer's Guide

# 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 Data Services Platform Mediator API
- Generating a Static Mediator API JAR File
- Using the Data Service Mediator API
- Obtaining a WebLogic JNDI Context for Data Services Platform
- Using a Static Data Service Mediator API
- Using a Dynamic Mediator API

# Overview of the Data Services Platform Mediator API

The BEA AquaLogic Data Services Platform (DSP) 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:

- Read and navigation functions. When a read function or navigation function is invoked through the Mediator API, the client application gets back information as a data graph that comprises the data objects constructed by the data service.
- Procedures. When a procedure is invoked through the Mediator API, the client application may or may not get back an SDO, depending on the implementation details of the procedure as configured for the data service.

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

- Static Mediator APIs. You can use the static mediator APIs to invoke functions on multiple data services, then cast the acquired objects to the appropriate data types. Static Mediator APIs are generated from a specific data service.
- Dynamic Mediator APIs. Use the dynamic mediator APIs to instantiate and invoke data service functions and procedures by name.

The Mediator API also supports several advanced features, including:

- Ability to filter, sort, and truncate return values. Your client applications can organize or limit returned results in several different ways using the Mediator API's Filter and FilterXQuery classes. For more information, see Filtering, Sorting, and Fine-tuning Query Results.
- Ability to stream data service function results. The static and dynamic interfaces data service materialize data service function call results as XML, in memory. However, in-memory materialization is not always practical. The Mediator API offers several different stream-oriented interfaces. For more information, see Handling Large Result Sets with Streaming APIs.
- Ad hoc query interface. The Mediator API's PreparedExpression interface enables client applications to invoke ad hoc XQuery expressions against data service results. Ad hoc queries can return anything, including simple data. Simple data is not represented as DataObjects (XmlObjects); however, in DSP ad hoc queries can return DataObjects if the returned XML is structured correctly and the appropriate static SDO classes are on the classpath. For more information, see Using Ad Hoc Queries to Fine-tune Results from the Client.

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 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 DSP 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 APIs, your development environment's CLASSPATH must include the JAR files listed in Table 1-5.

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 <app-name>-`ld-client.jar` file is not needed for generic 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 DSP-enabled application.

## Mediator API Summary and Reference

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

- Mediator APIs, which encompass the two Java packages listed in Table 3-2
- Data Service controls, a server-side Java class file that adheres to the Java Control Extension (JCX) standard.

This chapter discusses the Mediator APIs and how to use in Java client 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 | Data Services API Javadoc. | Same as for SDO Mediator APIs.
Javadoc location | `http://e-docs.bea.com/aldsp/docs21/Javadoc/index.html` | 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 DSP project. Client application developers must obtain this JAR file (typically, from the data services architect or the 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 API.

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

- From WebLogic Workshop, with the top folder of the application selected, right-click and select Build SDO Mediator Client from the pop-up menu. (This menu option is available from the root folder of the application only.)
- From the command prompt of the data service development machine by using the Ant script, as follows:

1. At a command prompt, navigate to the directory.
2. Execute the shell or command file script to set the environment for your machine. For Windows use setWLSEnv.cmd; for UNIX use `setWLSEnv.sh`.

These scripts can be found in the following location:

>     <beahome>/weblogic81/server/bin

3. Run the Ant script, passing in the name of a temporary directory as one of the parameters as shown below:

>     ant -Doutdir=<output-directory> -Darchive=<archive> -Dtmpdir=\tmp\clientbld -fld_clientapi.xml

Table 3-3 Arguments for Generating a Mediator Static Client JAR from 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.

For example:

>     ant -Doutdir="c:\myApp" =Darchive=C:\bea\user_projects\applications\myApp.ear -Dtmpdir=c:\temp -fld_clientapi.xml

Executing the command as shown in this example produces the client JAR file, as follows:

>     C:\myApp-ld-client.jar

## 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 DSP application.

Note: For more information, see Obtaining a WebLogic JNDI Context for Data Services Platform. For complete information about WebLogic Server contexts, see:

>     http://e-docs.bea.com/wls/docs81/javadocs/weblogic/jndi/WLInitialContextFactory.html

3. Instantiate remote interfaces for the data service. You can use either a static or dynamic mediator API interface. The dynamic interface is generic; the data service name is passed as an argument. For example:

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

4. Invoke a function or procedure on the data service.

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

Note:

## Obtaining a WebLogic JNDI Context for 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:

- The value of INITIAL_CONTEXT_FACTORY should be set to:

>      weblogic.jndi.WLInitialContextFactory

- The value of PROVIDER_URL should reflect the location (URI) of the WebLogic Server hosting DSP (for example,`t3://localhost:7001`).

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 DSP Procedures

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

- invoke( ). Dynamically invokes read and navigate functions on a data service. When a read or navigate function is invoked (getCustomerByCustID( ), for example), the function returns an array of data objects.
- invokeProcedure( ). Use invokeProcedure() to invoke procedures that have been registered with a data service. You can use the dynamic Mediator API to invoke a DSP procedure as in the following example, passing in the name of the procedure:

`ds.invokeProcedure("updateCustomerAddress", params);`

Table 3-4 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 DSP 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.

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.

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

When using static mediator APIs, the distinction between invoking a DSP function and a DSP procedure 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 DSP 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 DSP-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 DSP 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:

- The server context object
- The name of the DSP application that is deployed on the server

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 Dynamic 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 (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:

- Submit( ) method. The submit( ) method takes a DataObject as its parameter. (The static submit () would take Customer.) In either style, though, a submit() method is used to save changes to the data objects served by the data service.
- The prepareExpression( ) method. The prepareExpression() method lets you create ad hoc queries against the data service.

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 DSP 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:

- The server context object.
- The name of the DSP application that is deployed on the server.
- The DSP URI pointing to the location of the data service inside the DSP application.

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 next table:

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 SDO 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 DSP 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 generic SDO dynamic 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 DSP-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 DSP 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:

- The server context object
- The name of the DSP application that is deployed on the server

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 Dynamic 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:

- Submit( ). The submit( ) method takes a DataObject as its parameter. (The static submit() would take Customer.) In either style, though, a submit() method is used to save changes to the data objects served by the data service.
- prepareExpression( ). The prepareExpression( ) method lets you create ad hoc queries against the data service.

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 DSP 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 generic SDO, DataObjects depend on the XML schema to determine:

- Data types
- Default values
- Data structure of input XML data

The generic 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.

Generic SDOs are created using a createRootDataObject( ) method:

>     DataObject /* root SDO document */ createRootDataObject()

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

- Creating the root data object
- Using navigation functions
- Updating data on the back end
- Submitting a changed SDO to the server
- Deleting a data object
- Creating a data object on the client side

Listing 3-6 Common Generic SDO Operations

>     DataService custDS = DataServiceFactory.newDataService(context,"RTLApp","ld:DataServices/CustomerDB/CUSTOMER");
>     DataObject root =(DataObject)custDS.invoke("getCustomerByCustID", new Object[]{"CUSTOMER1"})[0];
>     DataObject myCustomer = root.getDataObject(0);
>     String name = myCustomer.getString("name");

>     //use navigation function
>     DataObject order = (DataObject)custDS.invoke("getApplOrder", new Object[]{root})[0];

>     // update
>     myCustomer.setString("Street","Lake Drive");
>     ((DataObject)myCustomer.getList("ADDRESS").get(0)).setString("City", "Hayward");

>     // submit the changed SDO to server
>     custDS.submit( root );

>     // Delete a DataObject
>     ((DataObject)myCustomer.getList("ADDRESS").get(0)).delete();
>     custDS.submit( root );

>     // create new SDO object on client side
>     DataService custDS =     DataServiceFactory.newDataService(context,"RTLApp","ld:DataServices/CustomerDB/CUSTOMER");

>     DataObject root = custDS.createRootDataObject ();
>     root.createDataObject("CUSTOMER_PROFILE").setString("FirstName","helloW");
>     custDS.submit( root );

#### How XML Schemas Are Made Available to Generic 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.

- Downloading schemas through an EJB metadata API. As in the first code fragment in Listing 3-6, the client-side query contains a Qname and arity as an identifier for the function. A generic SDO-capable mediator uses the identifier to locate the primary schema from the metadata EJB, then recursively resolves its dependent schemas, loading them from server to client upon request. Once all schemas are prepared on the client side, the schemas are processed (compiled) and made available to the calling routine.
- Downloading schemas through a Web service client. A WSDL file generated from WebLogic Workshop contains all the schema definitions in the specified data service. In this case they are simply processed and made available to provide typing services to the calling routine.

#### 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 DSP applications, caches should not be shared across multiple applications.)

The following schema caching APIs are available:

- SchemaTypeObject cache. The SchemaTypeObject is composed of a key:value pair.

key: Qname composed of root element name and target namespace
value: compiled schema type object

- Cache debugging APIs. The following APIs are more fully described in Javadoc:

>     SchemaTypeCache.dump( String dsName ) //dump contents for specified DS
>     SchemaTypeCache.dump();                //dump entire contents

- Flush schema cache APIs. The following APIs are more fully described in Javadoc:

>     public void flush()
>     public void clear( String dsName )

Schema Cache Management Scenarios

The following represents several schema cache management scenarios:

- Using multiple caches. In this case each data service has its own schema type.

>       // Note: each DS will have its own schema type cache.

>       SchemaTypeCache custSchemaTypes = new SchemaTypeCache();
>       SchemaTypeCache addrSchemaTypes = new SchemaTypeCache();
>       DataService custDS = DataServiceFactory.newDataService( context,
>            "RTLApp",                                                                                            "ld:DataServices/Customer",                                                                                             custSchemaTypes );
>       DataService addrDS = DataServiceFactory.newDataService( context,                                   "RTLApp",                                                                                    "ld:DataServices/Address",                                                                                               addrSchemaTypes );

- Using a single cache for multiple data services. In this case a single cache is used across multiple data services.

>     // Note: User manages cache across multiple DS's.

>       SchemaTypeCache schemaTypes = new SchemaTypeCache();
>                DataService custDS = DataServiceFactory.newDataService( context,                                                                                                "RTLApp",                                                                                            "ld:DataServices/Customer",                                                                                             schemaTypes );
>       DataService addrDS = DataServiceFactory.newDataService( context,                                                                                                 "RTLApp",                                                                                            "ld:DataServices/Address",                                                                                           schemaTypes);

- No schema cache specified. In such a case a cache is created implicitly and stored on the data service object automatically. There is no API available to inspect, flush, or edit cache entries.

>     DataService custDS = DataServiceFactory.newDataService( context, "RTLApp", "ld:DataServices/Customer" );
>     DataService addrDS = DataServiceFactory.newDataService( context, "RTLApp",      "ld:DataServices/Address" );

## Bypassing the Cache When Using the Mediator API

Data retrieved by data service functions can be cached for quick access. (See Configuring the Query Results Cache, in the DSP Administration Guide for details.) Assuming the data changes infrequently, it's likely that you'll want to use the cache capability. However, you can bypass the cache and obtain data directly from the data sources by passing the GET_CURRENT_DATA attribute within a function call, as shown in Listing 3-7. As a by-product, the cache is also refreshed.

Listing 3-7 Cache Bypass Example When Using Mediator API

>     DataService ds = DataServiceFactory.newDataService(
>     import com.bea.dsp.RequestConfig;
>     getInitialContext(), 																			// Initial Context
>     "Evaluation", 																			// Application Name
>     "ld:DataServices/CustomerManagement/CustomerProfile" 					// Data Service URI
>     );
>     Object[] params = {"CUSTOMER3"};
>     RequestConfig config = new RequestConfig();
>     attr.enableFeature(RequestConfig.GET_CURRENT_DATA);

>     CustomerProfileDocument doc = (CustomerProfileDocument) ds.invoke("getCustomerProfile",params.config);

# Step-by-Step: A Java Client Programming Example

This section describes common Java client application programming tasks:

- Step 1. Instantiating and Populating Data Objects
- Step 2: Accessing Data Object Properties
- Step 3: Modifying, Adding, and Deleting Data Objects and Properties
- Step 4: Submitting Changes to the Data Service

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.

- Static data service imports. To instantiate a data object using a static data service instance, you must import the packages that contain the generated typed interfaces. These are contained in the `<`appname`>-ld-client.jar` file generated from WebLogic Workshop (or by using DSP's Ant or Java generation tools). Using the static SDO API is a two-step process:

- Place the JAR file (`<`appname`>-ld-client.jar`) in the classpath of your development environment.
- Import the data types that you will be using in your code into your Java class file. For example:

>     import dataservices.myservice.MyCustomer;

Note: Static data services packages are always lowercase.

- Dynamic data service imports. To instantiate a data object using a dynamic API, you must import the DataServiceFactory class and invoke the newDataService method (see Table 3-8).

>     import com.bea.dsp.dsmediator.client.DataServiceFactory;

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-2, 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-9 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 Static and Dynamic Mediator APIs to Instantiate Data Objects (SDOs)

####

Static Mediator API | Dynamic Mediator API
--- | ---
`CUSTOMERDocument[] custDoc = ds.getCustomerView("CUSTOMER3");` | `CUSTOMERDocument [] custDoc = (CUSTOMERDocument[]) 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();` | `CUSTOMERDocument.CUSTOMER cust = (CUSTOMERDocument.CUSTOMER)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().

## Step 3: Modifying, Adding, 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

You can modify data object property values using either dynamic or static set() methods.

Table 3-11 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.

### Adding 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 adding 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 add a data object to an array of objects.

Listing 3-9 Adding a New Data Object to 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");

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

- Foreign key fields in the data object are automatically populated by DSP, based on the value of the corresponding foreign key in the container object.
- In a database schema, tables often use auto-generated values as their primary key. When adding an object to such a database, the primary key is generated and returned to the client through the submit() call.

If added objects correspond to relational records in back-end data sources, and if the records have auto-generated primary key fields, the fields are generated in the database source and returned to the client in a property array. The properties include name-value items corresponding to the column name and new auto-generated key value.

See Primary-Foreign Key Relationships Mapped Using a KeyPair for more information.

### Deleting Data Objects

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

Listing 3-10 Deleting a Data Object

>     CUSTOMERDocument.CUSTOMER[] custs =
>          custDoc[0].getCUSTOMERArray();
>              for (int i=0; i < custs.length; i++){
>                   if (custs[i].getFIRSTNAME().equals("Clark") &&
>                   custs[i].getLASTNAME().equals("Kent"))
>              {
>                custs[i].delete();
>                custDS.submit(custDoc);

>             }

>         }

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-11.

Listing 3-11 Static Interface

>     CUSTOMER custDS = CUSTOMER.getInstance(ctx, "RTLApp");
>     CUSTOMERDocument[] custDoc = (
>        CUSTOMERDocument[]) custDS.CUSTOMER();
>     custDoc[0].getCUSTOMER().setLASTNAME("Nimble");
>     custDS.submit(CustDoc);

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

Listing 3-12 Dynamic Interface

>     DataService ds = DataServiceFactory.newDataService(new InitialContext(), "RTLApp");
>     DataObject[] custDoc = (DataObject[])custDS.invoke("CUSTOMER", null);
>     custDoc[0].getCustomer().set("LastName", "Nimble");
>     custDS.submit(myCust,"ld:DataServices/CustomerDB/CUSTOMER");

# Examining a Java Client Application

Listing 3-13 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-13 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-13 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-13 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 DSP is deployed. The static Mediator API is used in this call to instantiate the actual Customer data service interface (rather than the generic DataService interface):

>     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.

2. 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:

>     CustomerDocument[] myCustomer =
>                   	( CustomerDocument[]) ds.invoke("CUSTOMER", null);

3. 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();`
4. New values for the FIRSTNAME and LASTNAME property of the CUSTOMER are set using the static data API:

>     myCustomer[0].getCUSTOMER().setFIRSTNAME("J.B.");
>     myCustomer[0].getCUSTOMER().setLASTNAME("Kwik");

5. 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:

>     custDS.submit(myCustomer);

6. 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 DSP Javadoc, described under DSP Mediator API Javadoc on page 1-13.

See Invoking Functions and DSP 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.