This chapter describes how your Java client applications can access data services. It covers the following topics:
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
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.
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.
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.
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
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:
setWLSEnv.sh
.These scripts can be found in the following location:
<beahome>/weblogic81/server/bin
ant -Doutdir=<output-directory> -Darchive=<archive> -Dtmpdir=\tmp\clientbld -fld_clientapi.xml
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
Table 3-3 describes the available arguments that can be used to generate a static client JAR mediator.
To use the Data Service Mediator API to invoke data service functions and procedures, create a Java class as follows:
com.bea.dsp.dsmediator.client
package. 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
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");
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"
);
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.
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.
invoke(String method, Object[] args) |
|
invokeProcedure(String method, Object[] args) |
|
submit(DataObject sdo) |
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.
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.
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.
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( ).
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.
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);
}
}
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.
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);
}
}
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:
The static and dynamic Mediator API options are described in detail in:
Using a Static Data Service Mediator 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.
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( ).
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.
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);
}
}
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:
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 );
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.
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:
key: Qname composed of root element name and target namespace
value: compiled schema type object
SchemaTypeCache.dump( String dsName ) //dump contents for specified DS
SchemaTypeCache.dump(); //dump entire contents
public void flush()
public void clear( String dsName )
Schema Cache Management Scenarios
The following represents several schema cache management scenarios:
// 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 );
// 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);
DataService custDS = DataServiceFactory.newDataService( context, "RTLApp", "ld:DataServices/Customer" );
DataService addrDS = DataServiceFactory.newDataService( context, "RTLApp", "ld:DataServices/Address" );
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.
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);
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.
When the GET_CURRENT_DATA attribute is set to True:
evaluation/cache/data/forcedrefresh
indicates that a GET_CURRENT_DATA operation has been invoked.
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. |
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.
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:
${WLS_HOME}/liquiddata/lib/wls90interop.jar
in PRE_CLASSPATH
on the AquaLogic Data Services Platform 2.x server by editing setDomainEnv.{cmd|sh}
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. |
${WL_HOME}/liquiddata/lib/wls90interop.jar
first in the classpath of WebLogic Workshop by modifying the value for the -cp
key in Workshop.cfg
${WL_HOME}/liquiddata/bin/ld_clientapi.xml
with the following arguments on the command line:
-Dapproot=<application directory>
-Dxbeanjarpath=${WEBLOGIC9x}/server/lib/xbean.jar
${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.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. |
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.
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.
<
appname>-ld-client.jar
file generated from WebLogic Workshop (or by using AquaLogic Data Services Platform's Ant or Java generation tools). Using the static SDO API is a two-step process:import com.bea.dsp.dsmediator.client.DataServiceFactory;
Table 3-8 lists static and dynamic mediator API interfaces.
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.
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.
DataObject cust = custDoc[0].get("CUSTOMER"); |
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.
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( ).
Return types from data service functions can be quantified based on the semantics shown in Table 3-11.
By default, change tracking on the data graph is enabled so that any changes made to object values are recorded in the change summary.
Table 3-12 provides examples showing how you can modify data object property values using either dynamic or static set( ) methods.
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.
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.
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");
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":
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");
If the data source associated with the object being added is an RDBMS, note these additional considerations:
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 the topic "Primary-Foreign Key Relationships Mapped Using a KeyPair" in the Handling Updates Through Data Services chapter of the Building Queries and Data Views.)
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.
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.
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.
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.
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);
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.
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:
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.
CustomerDocument[] myCustomer =
( CustomerDocument[]) ds.invoke("CUSTOMER", null);
myCustomer[0].getCUSTOMER().getFIRSTNAME();
myCustomer[0].getCUSTOMER().setFIRSTNAME("J.B.");
myCustomer[0].getCUSTOMER().setLASTNAME("Kwik");
custDS.submit(myCustomer);
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.