Skip navigation.
Downloads | Product Documentation | Support

Client Application Developer's Guide

# Accessing Data Services from Java Clients

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

- Overview of the Data Services Platform Mediator API
- What's in the Data Service Mediator API?
- How to Use the Mediator API
- Getting a WebLogic JNDI Context for DSP
- Using the Static Data Service Interface
- Using the Dynamic Data Service Interface
- Using Navigation Functions

# Overview of the Data Services Platform Mediator API

The BEA AquaLogic Data Services Platform (DSP) Mediator API gives Java client applications an easy-to-use programmatic interface for accessing information from data services. To use the Mediator API, Java applications simply instantiate a remote data service interface and invoke any public functions on the interface, including read, submit, and navigate functions. When a read function or navigation function is invoked through the Mediator API, the client application gets back information as an SDO data object, also known as a data graph.

As discussed in the Enabling SDO Data Source Updates, SDO is the client-side data programming model for DSP. The SDO API consists primarily of functions for getting and manipulating data objects and their properties.

Note: For more information on working with Data Objects, see Client Programming with Service Data Objects (SDO).

Like the SDO data programming interfaces, the Mediator API enables client applications to use either a typed or untyped approach to using data services. The untyped interface lets client applications use data services that are either unknown or not created at client development time.

A data object acquired through the untyped mediator API can be cast to a typed object, as long as its structure is compatible with the schema of the type to which it is being cast. In fact, a development pattern that can streamline programming when working with multiple data services is to use untyped APIs for invoking data service functions and subsequently casting the acquired objects to their appropriate types.

This chapter discusses the typed and untyped Mediator interfaces. The Mediator API contains several more advanced features as well. These are discussed in Advanced Topics. They include:

- Return value filtering, ordering, and truncating. Filter and order features in the Mediator API give client applications flexibility for controlling how results are returned by data service function invocation. For more information, see Applying Filter Data Service Results, and Ordering and Truncating Data Service Results, in Consuming Large Result Sets (Streaming API and Writing Results To a File).
- Streaming DSP information. Most of the data service interfaces return data as XML objects. When a function is invoked, DSP materializes the resulting XML in memory. Certain queries, such as periodic, administrative-related queries used to inventory a data set, for example, are too large for in-memory materialization to be practical. The streaming interfaces provide for a stream-oriented accessor and file-based materialization of the result object. For more information, see Consuming Large Result Sets (Streaming API and Writing Results To a File), in Consuming Large Result Sets (Streaming API and Writing Results To a File).
- Ad hoc query interface. The PreparedExpression interface gives DSP clients the ability to invoke ad hoc XQuery expressions against data service results. The ad hoc query interface is similar in spirit to the prepared expression interface of JDBC. Ad hoc queries return data as XMLObjects.

# What's in the Data Service Mediator API?

The Mediator API exposes data service functionality to Data Services Platform clients. It contains interfaces and classes for instantiating remote interfaces to the data services and executing functions on the interface. The functions defined for the data service are available in the Mediator API.

The generic, untyped 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.ld.dsmediator.client

The API consists of the classes and interfaces listed in the following table.

Table 4-1 Data Services Platform Mediator API

Interface or Class Name | Description
--- | ---
`DataService` | Interface for data services that returns data as Data Objects.
`PreparedExpression` | Interface for preparing and executing ad hoc queries that return information as XML objects. An ad hoc query is one that is defined in the client program, not in the data service.
`DataServiceFactory` | Factory class for creating Untyped data service interface instances.
`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 one that is defined in the client program, not in the data service.

The static mediator interface extends the generic mediator interface, and gives clients a typed approach for instantiating and invoking data service functions. For example, the following class definition represents a typed data service interface:

>     public class dataservices.Customer extends
>               com.bea.ld.dsmediator.client.XMLDataServiceBase { ... }

The typed data service interface is in the SDO Mediator Client JAR files generated from an DSP project.

The exception class for mediator errors is in the following package:

>     	com.bea.ld.dsmediator.client.exception

In addition to an exception for general mediator errors (`SDOMediatorException`) there are exceptions for ad hoc queries (`ServerPrepareException`) and streaming access (`StreamingException`).

Exceptions that are generated by the data source (such as `SQLException`) are wrapped in an SDO Exception, and can be accessed at the label `#sdoException.detail`.

# Setting the Classpath

To develop mediator client programs, include the preceding JARs in the system CLASSPATH of the development computer.

For example, on Microsoft Windows operating system, the command for setting the class path would be:

>         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\liquiddata\lib\ld-client.jar;

Note that this assumes that the first item, `Demo-ld-client.jar`, is in the current directory and that the WebLogic home directory is `C:\bea\weblogic81`. If different on your system, modify the path to the locations where these resides on your system.

Also note that when developing your own applications, you will need to substitute the name of `Demo-ld-client.jar` with the name of the JAR file generated from your DSP-enabled application.

# Creating the Mediator Client JAR File from the Command Line

Client applications can access the classes representing the typed data service interface using a JAR (Java Archive) file generated from the DSP project. The JAR file needs to be on the client application development machine. The file is named in the form:

`<``AppName``>-ld-client.jar`

There are two ways to generate the JAR file:

- From the Workshop UI, as described in the Data Services Developer's Guide.
- From the command line of the data service development machine.

In most cases, the JAR file would be generated by the Data Services Platform administrator and distributed to data client programmers.

This section describes how to generate the client JAR from the command line. To perform the procedure, first build the EAR file and then the client JAR file.

## Build an EAR File

To create a mediator client JAR file, you first need to create an EAR (Enterprise Archive) file from the DSP application. An EAR file is similar to a JAR file — it contains a set of deployable application artifacts.

To build an EAR file, perform the following steps:

1. Open the application by opening the Workshop application file in WebLogic Workshop, typically found in a directory location such as the following:

`<``bea_home``>\user_projects\applications\`<WLDomain>`\`<AppName>`.work`

2. Build the application by pressing the F7 key, or select Build Application from the Build menu. This builds the entire application. For your own applications, you will only need to build and deploy the DSP specific artifacts, such as web services, schemas, and so on.
3. Select build Ear from the Build menu. The following file is produced:

`<``bea_home``>\user_projects\applications\`<WLDomain>`\`<AppName>`.ear`

4. If it is not already running, start the WebLogic Server. From the Tools menu, select WebLogic Server —>Start WebLogic Server.

For more information about building an EAR file, refer to the Data Services Developer's Guide.

## Build the Client JAR

To use data service of a DSP project in a client application, you need to generate a client version of the DSP project Java archive. The client version includes wrapper classes that allow the client to call the data service functions through a dynamic API.

Generate the client JAR file from the EAR file you created earlier (see Build an EAR File above) by performing the following steps:

1. At a command prompt, navigate to the directory:

`<``bea_home``>\weblogic81\liquiddata\lib\sdoclientmediator`

The directory contains several files, including a build script and a ANT build configuration file. You should not have to modify these files.

2. Run the build script using the format:

ld_client_gen <LocationOfArchive> [<LocationOfDirectory>] [<LocationForTempDir>]

Where <LocationOfArchive>, <LocationOfDirectory>, and <LocationForTempDir> arguments are defined as described in the following table.

Argument | Description
--- | ---
<LocationOfArchive> | The fully qualified name of the EAR file you generated in step 4 of the Build an EAR File instructions.
<LocationOfDirectory> | The folder where you want the generated client JAR file to be placed. This is an optional parameter. If not specified, the current directory is used.
<LocationForTempDir> | The folder where you want the temporary, expanded EAR directory to be placed. This is an optional parameter. If not specified, the current directory is used.

For example:

>     ld_client_gen  C:\bea\user_projects\applications\danube\Demo\Demo.ear C:\test

When you run `ld_client_gen`, the following file is produced:

>     C:\test\Demo-ld-client.jar

When working with your own applications, "Demo" in the generated name will be replaced by a name derived from your EAR file.

# How to Use the Mediator API

To use the Data Service Mediator API to invoke data services, follow these general steps in your application:

- Step 1: Import the `com.bea.ld.dsmediator.client` package.
- Step 2: Create a JNDI context for the WebLogic Server that hosts the DSP application. (For more information, see Getting a WebLogic JNDI Context for DSP.)

For complete information, see:

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

- Step 3: Instantiate remote interfaces for the data service. You can use either the typed data service interface or untyped data service interface. The untyped data service interface is generic—the data service name is passed as an argument. For example:

>     DataService ds = DataServiceFactory.newXmlService(
>                   JndiCntxt, "RTLApp", "ld:DataServices/RTLServices/Customer");
>     Object params[] = {"CUSTOMER1"};
>     DataObject myCustomer =
>                     (DataObject) ds.invoke("getCustomerByCustID", params);

Here is the same operation using the typed interface:

>     Customer ds =  Customer.getInstance(JndiCntxt, "RTLApp");

- Step 4: Invoke a function on the data service. The following is the operation using the untyped interface:

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

Here is the same operation using the typed interface:

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

When a read or navigate function is invoked, the function returns an SDO data object. For more information, see Client Programming with Service Data Objects (SDO).

# Getting a WebLogic JNDI Context for DSP

In general, WebLogic JNDI services allow client applications to access named objects on a WebLogic Server. For DSP, you use JNDI calls to obtain references to remote data services. (Only one JNDI call is needed because the call is created in the context and is passed to the data services factory.) 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>);

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

# Using the Static Data Service Interface

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

To use the static data service interface, you must have the SDO Mediator Client JAR file that was generated from the desired 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 application. For example, to use a data service named Customer in a DSP project named data services, use the following import statement:

>     	import dataservices.Customer;

From there, you can instantiate the desired data service interface using the `getInstance()` function. In the function call, pass 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 on the data service. Any public function defined on the data service is available in the generated class. For example, consider the public data service functions shown in Figure 4-2.

Figure 4-2 Customer Data Service

The list of methods that are generated for the typed data service are listed below. Notice that methods are created for each function in the data service, such as `getCustomer()` and `getApplOrder()`

>     Customer(Context, String)
>     getInstance(Context, String)
>     prepareExpress(Context, String, String)
>     submit(DataObject)
>     getCustomer()
>     getCustomerToFile(String)
>     getCustomerByCustID(String)
>     getCustomerByCustIDToFile(String, String)
>     getCustomerByZip(String)
>     getCustomerByZipToFile(String, String)
>     getCase(CUSTOMERPROFILEDocument)
>     getCaseToFile(CUSTOMERPROFILEDocument, String)
>     getCreditCard(CUSTOMERPROFILEDocument)
>     getCreditCardToFile(CUSTOMERPROFILEDocument, String)
>     getApplOrder(CUSTOMERPROFILEDocument)
>     getApplOrderToFile(CUSTOMERPROFILEDocument, String)
>     getElecOrder(CUSTOMERPROFILEDocument)
>     getElecOrderToFile(CUSTOMERPROFILEDocument, String)
>     getCustomerByLoginID(String)
>     getCustomerByLoginIDToFile(String, String)

Several additional functions are generated as well. The `submit()` function is used to save changes to the data objects served by the data service. The "ToFile" functions, such as `getCustomerToFile()` and `getAllCustomersToFile()`, are also generated for each function defined in the data service. It allows a client to write results returned from the function call to a file specified as an argument. For more information, see Consuming Large Result Sets (Streaming API and Writing Results To a File), in Consuming Large Result Sets (Streaming API and Writing Results To a File).

Another function that is automatically provided is the `prepareExpression(`) function. This function is for creating ad hoc queries against the data provided by the data service.

Listing 4-1 shows a small but complete example of using the typed interface.

Listing 4-1 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.ArrayOfCUSTOMERPROFILEDocument;

>     public class MyTypedCust
>     {
>       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");
>         Context context = new InitialContext(h);

>         // Use the Mediator API
>         Customer customerDS = xds.getInstance(context, "RTLApp");
>         ArrayOfCUSTOMERPROFILEDocument myCust =
>           (ArrayOfCUSTOMERPROFILEDocument)xds.getCustomerByCustID("CUSTOMER2");
>         System.out.println(" CUST" + myCustomer);
>       }
>     }

# Using the Dynamic Data Service Interface

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.

In the dynamic interface, specific data service names are passed as parameters of the function calls instead of explicitly reflected in the function call names themselves. Like the Mediator API, the SDO API has both static (or strongly typed) and dynamic interfaces for working with data. In most cases, the static Mediator API would be used alongside the equivalent dynamic SDO interfaces. Both have the same use case—working with data when the type is unknown beforehand, for example:

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

A data object returned by the dynamic interface can be down cast to a typed object, as follows:

>          DataService ds =
>              DataServiceFactory.newDataService(
>                     context, "RTLApp", "ld:DataServices/Customer");
>           Object params = {"CUSTOMER2"};
>           CUSTOMERDocument myCustomer =
>                                  (CUSTOMERDocument) ds.invoke("getCustomer", params);
>           println(myCustomer.getCUSTOMER().getCUSTOMERNAME());

For an 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 4-2 shows a full example.

Listing 4-2 Mediator Client Sample Using the Dynamic Interface to a Data Service

>     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 Mediator API
>           DataService ds =
>             DataServiceFactory.newXmlService(context, "RTLApp",
>                                          "ld:DataServices/RTLServices/Customer");
>           DataObject myCustomer = (DataObject) ds.invoke("getCustomer", null);
>           System.out.println(" Customer Information: \n" + myCustomer);
>         }
>     }

# Using Navigation Functions

A navigation function lets you get data from a related data service. Relationships between data services serve to model a logical connection between them. They also streamline your client programming because you can invoke the relationship function from an instance of the current data service. For example, from a Customer data service you can get a credit card list for a customer instance, as in the following:

>     Customer myCustomer = Customer.getInstance(ctx, "RTLApp");
>     CUSTOMERPROFILEDocument custProfileDoc =
>                                   CUSTOMERPROFILEDocument.Factory.newInstance();
>     ArrayOfCREDITCARDDocument cc = myCustomer.getCreditCard(custProfileDoc);

A navigation function is called with an object of the calling data service being passed as an argument, as shown in the sample.

To use a navigation function, include the interface for the related data service in the import statements of your application, as shown in Listing 4-3.

Listing 4-3 Calling a Navigation Function Sample

>     import java.util.Hashtable;
>     import javax.naming.Context;
>     import javax.naming.InitialContext;

>     import dataservices.rtlservices.Customer;
>     import retailerType.CUSTOMERPROFILEDocument;
>     import retailerType.CUSTOMERPROFILETYPE;
>     import retailerType.ArrayOfCREDITCARDDocument;

>     public class CustomerClientNavigation
>     {
>         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);

>             Customer myCustomer = Customer.getInstance(context, "RTLApp");

>             CUSTOMERPROFILEDocument custProfileDoc =
>                                   CUSTOMERPROFILEDocument.Factory.newInstance();

>             CUSTOMERPROFILETYPE newCustProfile =
>                                          custProfileDoc.addNewCUSTOMERPROFILE();

>             CUSTOMERPROFILETYPE  myCustPfl =
>                                     myCustomer.getCustomerByCustID("CUSTOMER0").
>                                                getArrayOfCUSTOMERPROFILE().
>                                                  getCUSTOMERPROFILEArray(0);

>             newCustProfile.setFirstName(myCustPfl.getFirstName());
>             newCustProfile.setLastName(myCustPfl.getLastName());
>             newCustProfile.setADDRESSArray(myCustPfl.getADDRESSArray());
>             newCustProfile.setCustomerID(myCustPfl.getCustomerID());
>             newCustProfile.setCustomerSince(myCustPfl.getCustomerSince());
>             newCustProfile.setEmailAddress(myCustPfl.getEmailAddress());
>             newCustProfile.setEmailNotification(myCustPfl.getEmailNotification());
>             newCustProfile.setDefaulShippmentMethod(
>                                                myCustPfl.getDefaulShippmentMethod());

>             newCustProfile.setOnlineStatement(myCustPfl.getOnlineStatement());
>             newCustProfile.setLoginID(myCustPfl.getLoginID());
>             custProfileDoc.setCUSTOMERPROFILE(newCustProfile);

>             // Navigate to CreditCard data service
>             ArrayOfCREDITCARDDocument cc = myCustomer.getCreditCard(custProfileDoc);

>             System.out.println(cc);

>       }

>     }

## Bypassing a Function's Data Cache When Using the Mediator API

Data retrieved by data service functions can be cached. See Configuring the Query Results Cache, in the Data Services Platform Administration Guide.

However, the following scenario is very common: most of the time you can use cached data because it changes infrequently; however, on occasion, your application must fetch data directly the data source. At the same time, you want to update your cache with the most up-to-date information. A typical example would be to refresh the cache at the beginning of every week or month.

You can accomplish this by passing the attribute GET_CURRENT_DATA with your function call.

To bypass the Data Services Platform cache when using the Mediator API, your application will need to signal Liquid Data to retrieve results directly from the data source, rather than from its cache. You need to set the query attribute GET_CURRENT_DATA before you call a read function.

Listing 4-4 shows sample Java code that causes the query function to get data from the original source, rather than the cache. As a byproduct, the cache is also refreshed.

Listing 4-4 Cache Bypass Example When Using Mediator API

>     DataService ds = DataServiceFactory.newXmlService(
>     getInitialContext(), 																			// Initial Context
>     "Evaluation", 																			// Application Name
>     "ld:DataServices/CustomerManagement/CustomerProfile" 					// Data Service Name
>     );
>     String params[] = {"CUSTOMER3"};
>     QueryAttributes attr = new QueryAttributes();
>     attr.enableFeature(QueryAttributes.GET_CURRENT_DATA);
>     ds.setQueryAttributes(attr);

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