Client Programming with Service Data Objects (SDO)
This chapter describes the Liquid Data client-side data programming model and framework based on Service Data Objects (SDO). It introduces SDO and describes common programming tasks undertaken with SDO. It covers the following topics:
What is Service Data Objects (SDO) Programming?
The Service Data Object (SDO) specification defines a Java-based programming architecture and API for data access. A central goal of SDO is to provide client applications with a unified programming model for working with data in a disconnected way, regardless of its physical source or format. SDO thereby simplifies the way applications use data.
SDO specifies a data programming API as well as an architecture. The architecture part of the specification describes the components for enabling data access, such as mediators which serve as the intermediary between the client and back-end sources. In SDO terms, Liquid Data is a member.
In terms of client data programming, SDO has characteristics in common with other data access technologies, such as JDBC and Java Data Objects (JDO). Like JDO, SDO provides a static API for accessing data through typed accessors (for example, getCustomerName() ). Like JDBC’s RowSet interface, SDO has a dynamic API for accessing data through untyped accessors (for example, getString(«CUSTOMER_NAME») ). What distinguishes SDO from other technologies, however, is that SDO gives applications both a static and a dynamic API for accessing data, along with a disconnected model for accessing externally persisted data.
SDO and Liquid Data
Liquid Data implements the SDO specification as its Java client programming model. In concrete terms, this means that when a client application invokes a read function on a data service through the Data Service Mediator API (also called the Mediator API) or Liquid Data control, it gets the information back as a data object. A data object is the fundamental component of the SDO programming model and represents a unit of structured information.
The role of data objects, along with other key components in the SDO framework, are summarized as follows:
- Data Object. A data object holds values as properties, which can be either simple values (such as the CUSTOMERID property shown in Figure 2-1) or other data objects (such as ORDERS). Static methods are generated for reading, setting, and adding data object properties.
- Properties. Properties are the attributes of a data object. A property can be either a simple type or a complex type. A simple type corresponds to a leaf node in an XML document tree, usually of a primitive type such as String or int . A complex type corresponds to a branch node in the tree, and contains another data object.
- Data Graph. A structure for holding data objects, a data graph consists of a single root object, any number of additional objects and properties, the XML schema for the object, and a change summary (a log of data changes). Data users exchange information with the Liquid Data server components by passing data graphs back and forth.
- Data Service Mediator. A mediator resides between SDO clients and data sources and acts as the intermediary between them. It receives data requests and change submissions from the client and relays information back to the client in the form of a data graph.
According to the SDO specification, there can be many types of mediators, each intended for a particular type of query language or back-end system. Liquid Data includes the Data Service Mediator, a mediator specialized for acting as the intermediary between Liquid Data data services and clients.
Note: For more on the Data Service Mediator API, see Accessing Data Services from Java Clients.
Figure 2-1 SDO Components with Data Graph
Data objects are passed between the mediator and client applications in a data graph. A data graph can have only a single root object (for example, CUSTOMERDocument in Figure 2-1). For result involving repeating objects, therefore, a single root element prefixed by «ArrayOf» is introduced to serve as the data graph root node. For more information, see ArrayOf Types.
Liquid Data leverages XMLBeans technology to generate static interfaces from XML. As a result, many features of the underlying XMLBeans technology are available in SDO as well. For more information on XMLBeans, see http://xmlbeans.apache.org .
Furthermore, all SDO types inherit from XmlObject, so factory classes for creating instances and parsing data objects are present from the inherited XmlObject interface.
Looking at an SDO Client Application
This section presents a simple example (Listing 2-1) of an SDO client application. The example gets information from Liquid Data through the Mediator API by instantiating a remote interface to a data service and invoking data service functions. It extracts information for a customer, modifies it, and submits the change to the mediator for update to the source or sources.
Listing 2-1 Sample SDO Client Application
import java.util.Hashtable;
import javax.naming.InitialContext;
import dataServices.customerDB.customer.ArrayOfCUSTOMERDocument;
import dataservices.customerdb.CUSTOMER;
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);
// get the Customer data service and run dynamic invocation of data service
CUSTOMER custDS = CUSTOMER.getInstance(context, "RTLApp");
ArrayOfCUSTOMERDocument myCustomer =
(ArrayOfCUSTOMERDocument) custDS.invoke("CUSTOMER", null);
// get and show customer name
String existingFName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getFIRSTNAME();
String existingLName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getLASTNAME();
System.out.println(" \n---------------- \n Before Change: \n");
System.out.println(existingFName + existingLName);
// change the customer name
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setFIRSTNAME("J.B.");
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setLASTNAME("Kwik");
custDS.submit(myCustomer,"ld:DataServices/CustomerDB/CUSTOMER");
// re-query and print new name
myCustomer = (ArrayOfCUSTOMERDocument) custDS.invoke("CUSTOMER",null);
String newFName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getFIRSTNAME();
String newLName =
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getLASTNAME();
String newName = newFName.concat(newLName);
System.out.println(" \n---------------- \n After Change: \n");
System.out.println(newFName + newLName); >
>
The example above includes the following processing steps:
In the sample, an SDO is acquired through the Data Service Mediator API and modified through the SDO static API. Keep in mind that you can acquire data objects through the Liquid Data control as well. Therefore, it is useful to note the difference in the sample between mediator API calls and SDO calls.
The data service interface is instantiated and invoked through mediator API calls as follows:
ArrayOfCUSTOMERDocument myCustomer =
( ArrayOfCUSTOMERDocument) ds.invoke("CUSTOMER", null);
Once the data object is created, its properties are accessed using the SDO static interface (which returns the actual type of that node):
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).getFIRSTNAME();
As mentioned elsewhere, the SDO client data programming model includes both static and dynamic interfaces. The equivalent call using the dynamic interface would be as follows:
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).get("FIRSTNAME");
(This will returns an Object instance that you will need to cast to the type ordinarily returned by the static interface.)
Finally, the change is submitted to the data service mediator for propagation to the back-end source through the submit() function in the mediator interface.
Although code for handling exceptions is not shown in the example, a runtime error in SDO throws an SDOException . If an exception is generated by a data source, it is wrapped in an SDOException .
Note: For more information on the Mediator API, see Accessing Data Services from Java Clients.
Looking at a Data Graph
Data objects and data graphs can be serialized and printed to standard output. In fact, viewing a printed data graph when developing client applications can help you understand how data objects are composed.
Listing 2-2 shows a data graph associated with a modified data object. The printout is produced by the following code:
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setFIRSTNAME("J.B.");
myCustomer.getArrayOfCUSTOMER().getCUSTOMERArray(0).setLASTNAME("Nimble");
System.out.println(myCustomer.getDataGraph());
Notice the data graph features in the following listing examples:
- Metadata, in the form of an XML schema description, applicable to the contained data.
- A change summary, which shows the former value of a changed property (the FIRSTNAME and LASTNAME is changed from J.B. Nimble to Jack B. Quick).
- The contents of the data object itself, in this case, is a customer record.
For more information on data graphs, see Working with Data Graphs.
Listing 2-2 Serialized Data Graph
targetNamespace="ld:DataServices/CustomerDB/CUSTOMER"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:single="ld:DataServices/CustomerDB/CUSTOMER">
CUSTOMER1
Jack B. Quick
2001-10-01
Jack@hotmail.com
2145134119
295-13-4119
1970-01-01
AIR
1
0
1
Jack
CUSTOMER2
Kevin
Smith
2001-10-01
JOHN_2@yahoo.com
3607467964
087-46-7964
1978-09-21
AIR
1
0
1
Jerry
. . .
XML Schema-to-Java Type Mapping
Liquid Data client developers can use the Liquid Data Console to view the XML schema types associated with data services. (See Figure 2-2) The return type tab indicates, for example, whether an element is a string, int, or complex type.
Figure 2-2 Return Types Display in Liquid Data Console
The Table 2-3 shows XML schema types correspondence to SDO Java types.
Table 2-3 Schema to Java Data Type Mapping