Using XMLink


Chapter 4. Using Java to Call Services

This chapter describes how to write Java clients to call services in an Oracle Tuxedo application. Alternatively, you can use XML to exchange data as described in Chapter 5, "Using XML to Call Services."

For Java documentation of the XMLink interfaces, refer to the Javadoc portion of the online documentation.

The major topics in this chapter are:

The following diagram illustrates the process described in this chapter for using the CCI interface to execute a Tuxedo service which uses an FML buffer. There are additional record types corresponding to the Oracle Tuxedo buffer types. For more information on record types, refer to "Working with Record Objects."

Figure 4-1 Overview of the steps to create an FML record in XMLink


Getting an Oracle Tuxedo Connection

To get a connection to your Oracle Tuxedo application, you need to create an instance of ConnectionFactory. This is the starting point for all interactions with XMLink. You then call the method getConnection on your ConnectionFactory instance, which returns an object of type Connection.

Connection factories can have any JNDI binding name you choose. The JDNI binding name is specified when you created the connection factory and can be the same as the connection factory name.

Example: Connecting with a Java Client

In XMLink 2.0, the following example connects to a Java client and performs JNDI lookup:

// get an initial JNDI naming context
javax.naming.Context initctx = new javax.naming.InitialContext();
// do JNDI lookup to get connection factory
// lookup doesn't return a ConnectionFactory object,
// so a cast is needed
javax.resource.ConnectionFactory cxf =
(javax.resource.ConnectionFactory)
initctx.lookup("ConnectionFactoryName");

// where "ConnectionFactoryName" is the JNDI binding path of a
// predeployed connection factory
// get a connection
Connection cx = cxf.getConnection();

Understanding the ConnectionFactory Interface

The following code shows the interface ConnectionFactory. XMLink's implementation class for ConnectionFactory is ConnectionFactoryImpl.

public interface ConnectionFactory
extends java.io.Serializable, javax.resource.Referenceable
{
public Connection getConnection ()
throws ResourceException;
public Connection getConnection(ConnectionSpec properties)
throws ResourceException;

public RecordFactory getRecordFactory()
throws ResourceException;

public ResourceAdaptorMetadata getMetaData()
throws ResourceException;
}

Note that objects of class ConnectionFactory support the basic Java interfaces Serializable and Referenceable. Refer to the Java SDK documentation for descriptions of those interfaces.

Supplying Connection Parameters

Calling getConnection on an object of type ConnectionFactory returns an object of class Connection, which represents a connection to the Oracle Tuxedo application.

Calling getConnection with the ConnectionSpec parameter allows a client to provide log-in information needed to log in to Oracle Tuxedo. In XMLink, the implementation class for ConnectionSpec is TuxConnectionSpec, which provides set and get methods for the following properties:

The above properties correspond to members of Oracle Tuxedo's TPINIT data structure. Refer to Oracle Tuxedo documentation for tpinit(3c) for more information.

For container-managed sign-on, use getConnection without any parameters. The authentication information in this case is supplied by your application server.

The following code shows the interface Connection. XMLink's implementation class for Connection is ConnectionImpl.

public interface Connection
{
public Interaction createInteraction()
throws ResourceException;
public LocalTransaction getLocalTransaction()
throws ResourceException;
public ConnectionMetaData getMetaData()
throws ResourceException;
public ResultSetInfo getResultSetInfo()
throws ResourceException;
public void close()
throws ResourceException;
}

Calling createInteraction on a Connection instance creates an Interaction instance associated with the Connection instance. A single Connection instance can be associated with multiple Interaction instances. An Interaction instance is what is used to access Oracle Tuxedo services. For a description of the methods supported by Interaction instances, refer to "Calling Oracle Tuxedo Services."

Calling getMetaData on a Connection instance returns an object containing information about the Oracle Tuxedo connection represented by that Connection instance. Such an object supports the following methods:

public String getEISProductName()
public String getEISProductVersion()
public String getUserName()

Getting Information about XMLink

The method getMetaData provides information about the XMLink product. Calling getMetaData returns an object of class ResourceAdapterMetadata. Such objects support the following methods:

	public String getAdapterName()
public String getAdapterShortDescription()
public String getAdapterVendorName()
public String getAdapterVersion()
public String getSpecVersion()
public String getInteractionSpecsSupported()
public boolean supportsExecuteWithInputAndOutputRecord()
public boolean supportsExecuteWithInputRecordOnly()
public boolean supportsLocalTransactionDemarcation()

The return values for these methods provide the name and version information for XMLink.

Calling getRecordFactory

The getRecordFactory method of the ConnectionFactory interface returns the RecordFactory interface. This object can be used to generate Record instances that, in turn, can be used to hold input data for Oracle Tuxedo services. For information about working with Record instances, refer to "Working with Application Data."

Specifying Transaction Access

Calling the getLocalTransaction method of the ConnectionFactory interface returns an object that allows access to Oracle Tuxedo transaction management functions. Each Connection instance can be associated with only a single LocalTransaction instance. For a description of the methods supported by the LocalTransaction interface, refer to "Managing Oracle Tuxedo Transactions."


Managing Oracle Tuxedo Transactions

Access to Oracle Tuxedo transaction demarcation functions is achieved through objects of type LocalTransaction. You get such an object by calling the getLocalTransaction method on a Connection instance. A Connection instance can only be associated with one LocalTransaction instance at a time.

A LocalTransaction instance supports the following methods:

public void begin()
public void commit()
public void rollback()

The method begin corresponds to the Oracle Tuxedo function tpbegin. The method commit corresponds to the Oracle Tuxedo function tpcommit. The method rollback corresponds to the Oracle Tuxedo function tpabort.

Warning: Transaction support is available in Oracle Tuxedo version 7.1 and higher.


Calling Oracle Tuxedo Services

Access to Oracle Tuxedo services is provided by objects of type Interaction. To create Interaction instances, call the createInteraction method on a Connection instance. Each Interaction instance retains an association with the Connection instance it was created by.

Understanding the Interaction Interface

The following code defines the Interaction interface:

	public interface Interaction
{
public void close() throws ResourceException;

public boolean execute(InteractionSpec ispec,
Record input,
Record output) throws ResourceException;

public Record execute(InteractionSpec ispec,
Record input) throws ResourceException;
}

The getConnection method returns the Connection that the Interaction instance is associated with.

The close method terminates the Interaction instance. Calling this method also frees all Tuxedo record buffers used during the interaction. However, the Java record still has a copy of the data, so it may in fact be used later for a different interaction.

There are two versions of the execute method:

In either version of the execute method, the Oracle Tuxedo service being called is specified by the first parameter, which takes an object of type InteractionSpec.

For a code example, see "Example: Calling Services using Java."

Understanding the TuxInteractionSpec Interface

For XMLink, a class called TuxInteractionSpec implements InteractionSpec and supports the property set needed by XMLink. So in order to call an Oracle Tuxedo service, you must first create a TuxInteractionSpec object, and then set its properties to determine which Oracle Tuxedo service it specifies.

TuxInteractionSpec objects support the following properties:

The following methods work with these properties:

	public void setFunctionName(string name)
public string getFunctionName()
public void setInteractionVerb(int mode)
public int getInteractionVerb()
public void setExecutionTimeout(int milliseconds)
public int getExecutionTimeout()

After creating a new InteractionSpec instance:

For a code example, see "Example: Calling Services using Java."


Working with Application Data

Oracle Tuxedo applications send and receive data using typed buffers, which allows the Oracle Tuxedo platform to transfer data between different operating systems. XMLink automatically converts data provided to it in the form of a Java Record instance to the appropriate Oracle Tuxedo buffer type for the service being called, and then converts the return data into a Java Record instance.

XMLink defines four record classes corresponding to the Oracle Tuxedo buffer types and one class for Prolifics's Panther for JetNet buffer type.

Table 4-1

XMLink record types

Oracle Tuxedo buffer types

FML

FMLRecord

FML32

FML32Record

STRING

StringRecord

CARRAY

CArrayRecord

Panther for JetNet buffer type

JAMFLEX

JAMFLEXRecord

To call a Tuxedo service, you must create a Record object of the type corresponding to the buffer type the service takes as its argument. You get Record instances by calling the methods of a RecordFactory interface.

Understanding the RecordFactory Interface

Calling getRecordFactory on a ConnectionFactoryImpl instance returns an instance of RecordFactoryImpl. RecordFactoryImpl objects implement the RecordFactory interface.

The following code defines the RecordFactory interface:

  public interface RecordFactory

public MappedRecord createMappedRecord(String recordName)
throws ResourceException;

public IndexedRecord createIndexedRecord(String recordName)
throws ResourceException;

XMLink's RecordFactoryImpl class also supports another method:

    public Record createRecord(String recordName)
throws ResourceException;

To create an FMLRecord or FML32Record instance, use the createMappedRecord method of a RecordFactory instance. The createMappedRecord method takes a string argument used to identify what kind of record should be created. Use the strings "FML", "FML32" or "JAMFLEX" as the argument to the createMappedRecord method to create an FMLRecord, FML32Record or JAMFLEXRecord instance, respectively.

To create a StringRecord or CArrayRecord instance, use the createRecord method of a RecordFactory interface. As with the createMappedRecord method, the method's string argument is used to determine which type of record to return. Use the strings "STRING" and "CARRAY" as the argument to the createRecord method to create a StringRecord or CArrayRecord instance, respectively.

The createMappedRecord method returns a MappedRecord and the createRecord returns a Record. Since what you actually want are instances of FMLRecord, FML32Record, StringRecord, CArrayRecord or JAMFLEXRecord, you will have to cast the return values from createMappedRecord and createRecord to the appropriate types.

For example:

	// create an FML32Record instance, using a previously 
// acquired RecordFactory instance called rcf
FML32Record fml32r = (FML32Record)
rcf.createMappedRecord("FML32");

// create a StringRecord instance
StringRecord strr = (StringRecord) rcf.createRecord("STRING");

XMLink client code will not typically need to use the createIndexedRecord method. RecordFactoryImpl objects do implement this method to create the ArrayRecord objects returned by the getField method of FMLRecord and FML32Record objects. For more information, see "FMLRecord and FML32Record Objects."

Working with Record Objects

The following code defines the Record interface:

	public interface Record extends Cloneable
{
public String getRecordName();
public void setRecordName(String Name);

public void setRecordShortDescription(String description);
pubic String getRecordShortDescription();

public boolean equals(Object other);
public int hashcode();

Public Object clone() throws CloneNotSupportedException;
}

The Record objects supported by XMLink, objects of class FMLRecord, FML32Record, CArrayRecord, StringRecord and JAMFLEXRecord all support the methods listed above. In addition they also support methods specific to themselves, and client code that accesses them will primarily do so by means of those specific methods.

FMLRecord and FML32Record Objects

FMLRecord and FML32Record objects are instances of MappedRecord, and therefore implement the methods of the Map interface in addition to those of Record. Specifically, these classes extend the standard Java class HashMap.

However, underlying each instance of FMLRecord or FML32Record is a Tuxedo FML buffer or FML32 buffer. FML buffers are made up of "fields". Client code should NOT use the methods of the Map interface to modify the contents of an FMLRecord or an FML32Record, because to do so will put the Record out of sync with the Oracle Tuxedo FML buffer that it represents. To update the contents of an FMLRecord or an FML32Record, use the following method, that is specific to these classes, and define interaction at the level of FML fields:

	public void addIn(String name, String value)

The addIn method is used to populate the fields in an FML buffer. The name parameter is the name of the field to which a value is to be added. The value is added as a new occurrence to the field. To add multiple occurrences to a field, call addIn repeatedly with a single field name.

Using the following addIn method allows you to create embedded FML for Oracle Tuxedo versions 7.1+ which contain support for this feature:

	public void addIn(String name, FML32Record value)

Note that the field values are all input as strings. The underlying FML buffer will be populated with whatever data types are appropriate to the FML file definition, based on the field names. If any conversion is needed, it is performed by Tuxedo.

Sample: Getting FML Data

The following sample illustrates one method of getting data after calling execute().

Warning: The getField method is for internal use only. Use this example as a basis for getting data from FML buffers.

//Call tuxedo service
rcout = (FMLRecord)iact.execute(tuxl,rc);
//Get the fields from the Record object.
Iterator it = rcout.entrySet().iterator();
while ( it.hasNext()) {
Map.Entry me = (Map.Entry)it.next();
String fieldName = (String)me.getKey();
IndexedRecord fieldValue = (IndexedRecord)me.getValue();
Iterator it2 = fieldValue.iterator();
System.out.println(fieldName);
while (it2.hasNext())
{
System.out.println(" " + it2.next());
}
}

Note: In this example, if rcout were an FML32Record, it might contain embedded FML. In that case, it2.next() might return an FML32Record, rather than a string and different processing would be required.

CArrayRecord and StringRecord Objects

In addition to the basic methods of the Record interface, as listed above, CArrayRecord and StringRecord objects support the following methods:

		public String getContents()
public void addIn (String name, String value)

The getContents method returns the contents of the Record.

The addIn method takes two String arguments, the value of the second argument will be appended to the Record's contents. The first argument is ignored. A Record can be filled either by one single call to addIn or by a series of such calls.

For CArrayRecord, the output record used for InteractionImpl.execute() must contain initial data. For example:

CArrayRecord out = (CArrayRecord)rcf.createRecord("CARRAY");
out.addIn("", "|");

Character Encoding Support

Java strings are in Unicode. By default, XMLink passes to Tuxedo only the low order byte of each Unicode character. For characters in English and other European languages, this is sufficient since only the low order byte is significant.

Since other languages may need additional support, each of XMLink's Record classes offers setEncoding() and getEncoding() methods. The default encoding is ISO-8859-1, which is a single byte per character encoding. A multibyte encoding, such as UTF8, can be used for foreign characters. The Tuxedo server must be able to support and decode/encode according to the specified encoding setting.


Example: Calling Services using Java

Here is an sample outline of client code combining the connection code from earlier in the chapter with an example of interacting with a Connection instance to call a Tuxedo service:

// get an initial JNDI naming context
javax.naming.Context initctx = new javax.naming.InitialContext();
// do JNDI lookup to get connection factory
// lookup doesn't return a ConnectionFactory object,
// so a cast is needed
javax.resource.ConnectionFactory cxf =
(javax.resource.ConnectionFactory)
initctx.lookup("ConnectionFactoryName");

// where "ConnectionFactoryName" is the JNDI binding path of a
// predeployed connection factory
// get a connection
Connection cx = cxf.getConnection();
// cxf represents previously acquired ConnectionFactory instance
// first get a LocalTransaction instance
LocalTransaction ltx = cx.getLocalTransaction();
// mark the beginning of a Tuxedo transaction
ltx.begin();
// get an Interaction instance
Interaction iact = cx.createInteraction();
// create a new InteractionSpec object
TuxInteractionSpec tux1 = new TuxInteractionSpec;
// set the properties of the InteractionSpec instance
// note that InteractionVerb defaults to SYNC_SEND_RECEIVE,
// so the second line below isn't really necessary
tux1.setFunctionName("TUXServiceName");
tux1.setInteractionVerb(SYNC_SEND_RECEIVE);
tux1.setExecutionTimeout(1000)
// get a RecordFactory instance
RecordFactory rcf = cxf.getRecordFactory()
// create a Record Instance to hold input data
FMLRecord rc = (FMLRecord) rcf.createMappedRecord("FML");
// Populate Record instance with data
rc.addIn ("FirstField", "value1");
rc.addIn ("FirstField", "value2");
rc.addIn ("FirstField", "value3");
rc.addIn ("SecondField", "anothervalue");
// and so forth
// call the TUXEDO service
MappedRecord ret = iact.execute(tux1, rc);
// call other services in the transaction by creating
// new InteractionSpec instances and input records
// and calling execute as needed
// mark the end of the TUXEDO transaction
ltx.commit();
//close the InteractionInstance
iact.close();