Passing data objects between CICS Java environments: Part 2: Techniques for passing Java data objects

Java is becoming a popular programming language for CICS applications. CICS provides multiple Java environments, each with its own strengths, so passing data objects between the different CICS Java environments is an important topic. This three-part article series gives you the details. Part 2 uses an example Java class to show you how to pass Java data objects between applications running in a CICS-pooled JVM, an Axis2-based JVM, a CICS Dynamic Scripting environment, and a CICS OSGi-based JVM.

Introduction

Part 1 of this three-part series described the different Java™ environments that IBM® CICS® provides:

  • The traditional pooled JVM, where only one CICS-based Java transaction runs in a JVM at a time.
  • The CICS JVM server environment, where multiple Java program requests can be dispatched to the same JVM at the same time. Here are the CICS-provided Java environments that use the JVM server (but can't be mixed into a single JVM server):
    • The OSGi-based environment, which provides the OSGi dynamic module system with functionality such as dynamically adding and removing Java classes to the JVM, providing the OSGi registry, exposing only the interfaces to your Java module instead of all classes in the module, and specifying module dependencies to almost eliminate typical ClassNotFound problems.
    • The Axis2 open-source Web service engine, which lets you expose a POJO quickly and easily using JAX-WS annotations, and write handlers in the Axis2 style.
    • CICS Dynamic Scripting, which provides a quick way to develop Web applications using the PHP and Groovy scripting languages. The PHP and Groovy interpreters are implemented in Java, so there is a Java bridge to let you instantiate Java objects and invoke CICS programs
  • CICS Transaction Gateway, which facilitates communications from a Java program running in a non-CICS environment to a CICS TS program.

As you would expect, if you are working with Java classes within a single CICS-based Java program, you work with them as normal: you instantiate the object and invoke the object's methods. However, when your application is coded in Java, but you want to leverage the strengths of the different CICS-provided Java environments, or you want to distribute parts of your Java-based application over multiple CICS regions, using a Java-like approach to transporting Java data objects is preferable to the traditional series-of-bytes field-oriented approach to transporting data. This traditional approach to passing data is needed and performs well if you are using multiple programming languages, which is one of the strengths of CICS.

Part 1 described that the CICS version of a call, the EXEC CICS LINK command, which lets you invoke another program written in the same or a different programming language in a CICS environment. The CICS-architected facilities for passing data between CICS programs are the COMMAREA and channels and containers. The COMMAREA is a block of storage up to 32 KB, while a container is a named block of storage with no size limit. Containers are grouped into a channel, which can be passed from one CICS program to another. The COMMAREA, and channels and containers are mutually exclusive -- a program can receive data using only one of the two techniques at a time. When you LINK from program A to program B, program A can pass a COMMAREA or a channel, but not both.

Part 1 stated that if a CICS-based application in only written in Java, and you need to move from one CICS Java environment to another, it is simpler to stay in a Java paradigm and pass Java data objects between the CICS-based Java environments. You can do that by serializing a Java data object into a CICS container, passing the container to another Java program running in a different CICS Java environment, and then deserializing the object in the target Java program.

You could also pass data in a series-of-bytes, field-oriented structure, but passing Java data objects between the CICS-provided Java environments lets you leverage the strengths of the different CICS-provided Java environments, and also lets you avoid:

  • Creating a COBOL copybook layout for the data to be passed from between Java programs
  • Running utilities to create a Java data object from the COBOL copybook
  • The need for the Java programmer to understand COBOL
  • The need to maintain the required COBOL copybooks in your source repository
  • The need to perform production turnover on the COBOL copybook
  • The need to update the COBOL copybook if the contents of the passed data changes (and the need to go through the subsequent repository updates and production turnover)

Using an object-oriented façade on top of CICS

This article shows you how to use CICS data-passing facilities to pass Java data objects between Java programs running in the CICS-provided Java environments. You can provide an object-oriented façade on top of CICS data passing facilities to create a Java-oriented way to pass Java data objects. At the bottom of this article, you can download a sample class that implements a façade, and it can be used as-is or as the basis of your own façade implementation. When passing Java data objects from an originating Java program running in one CICS-provided Java environment to a different CICS-provided Java environment, a façade enables you to pass Java data objects using code like this:

String abc = "Some information to Pass";
Employee emp = new Employee("John Doe");
DDW_CicsObjectTransporter transporter = 
    new DDW_CicsObjectTransporter();
transporter.addObject("abc",abc)
    .addObject("emp",emp)
    .execute("TARGET");

// and after the program return

String errorString = (String)transporter.getObject("error");
If (errorString != null) {
    processError();
} else {
    Manager newManager = (Manager)transporter.getObject("mgr");
    // process/display Manager object that was returned
}

Here is some sample code for the responding Java program to return data using the facade:

DDW_CicsObjectTransporter transporter = new DDW_CicsObjectTransporter();
Employee emp = (Employee)transporter.getObject("emp");
// work with employee object
transporter.removeObject("abc");    // can remove object in the transporter
Manger mgr = new Manger("Joe Bloggs");
transporter.addObject("mgr",mgr);  // add new or changed objects
return;

The Java classes included with this article provide a façade that enables the above interactions. You may be able to use these classes as-is in your environment, but please read the rest of the article to understand what these classes do and how they may affect application performance.

This article series describes CICS Java environments available up through CICS TS V4.2. It is intended for Java programmers developing programs for CICS, but will also be beneficial to CICS application programmers who are starting to use Java as well as CICS systems programmers.

Java object serialization

Passing Java data objects between CICS-provided Java environments requires serialization of the Java data object. The concepts of object serialization are well understood by most Java programmers, and there are several good articles on Java object serialization as well as several examples of the Java code for object serialization on the Internet. Therefore this article will only mention some highlights of Java object serialization, and these highlights will help if you read some of the articles on Java object serialization.

To serialize your object to a byte array, look at the writeObject() method of the ObjectOuputStream. To deserialize your object from a byte array, look at the readObject() method of the ObjectInputStream.

For an object to be serialized, it must implement the java.io.Serializable interface. Java objects such as String and arrays implement this interface, but java.lang.Object (the mother of all objects) does not. Therefore, depending on the inheritance structure of the objects you want to pass, you may need to have your objects implement the Serializable interface. You can implement multiple interfaces in a single Java object so this shouldn't present a problem. Even if your object implements the Serializable interface, it doesn't mean that everything your object references can be serialized. You will want to ensure that your object and all objects it references (and the objects they reference) implement Serializable.

You will also need to ensure that the same version of the class exists in the LINKing CICS JVM environment and the LINKed-to CICS JVM environment. As part of implementing the Serializable interface, include a field defined as private static final long with a name of serialVersionUID. As you change your object, you should change the serialVersionUID. When the serialVersionUID is included in the object, the value associated with this field is stored in the serialized object. If there is an attempt to deserialize an object where the local class definition does not agree with the serialVerisionUID in the serialize object, deserialization will fail, with a meaningful error message. Use of the serialVersionUID field will avoid potential out-of-sync situations that may cause unexpected results. Use of the serialVersionUID is a best practice when using object serialization.

The Java data object transporter classes provided with this article verify that you have supplied a serialVersionUID in the object to be passed, but the provided transporter classes do not validate that you have supplied a serialVersionUID field in the objects referenced by the passed object. Therefore you need to verify that referenced objects contain a serialVersionUID field. In the provided Java class, you can turn off serialVersionUID checking by using the setCheckForSerialVersionUIDBeforeSerialization(false) method.

If your object has special serialization considerations and Java's normal serialization won't work for your object, you can specify how your object is serialized and deserialized by implementing the writeObject() and readObject() methods. These two methods, when implementing the Serializable interface, will be invoked if present during object serialization and deserialization.

Serialization is not trivial and does require CPU cycles. Since applications running in a CICS environment are expected to have fast response time, do not needlessly serialize and deserialize objects. Additionally, you should benchmark the use of serialized Java data objects for your application. The overall affect of serialization in your application will vary depending on the size, complexity, and number of objects being serialized.

Writing and reading serialized Java objects to and from CICS channels and containers in a CICS TS Java environment

Once you have serialized the object to be passed into a byte array, you need to place the byte array into a container associated with a channel. In the receiving Java program you need to get the byte array from a container. To get or put data from or into a container, you must first get a reference to a channel. There are three ways to get a CICS channel reference:

  • Access the channel that was passed to you (also known as the current channel) with the following command:
    Channel myChannel = Task.getTask().getCurrentChannel();
  • Create a channel, which you can then pass to another program, with the following command:
    Channel myChannel = Task.getTask().createChannel(THECHANNELNAME);
  • Access the current channel or any channel that was created with the specified name at the current CICS link level:
    Channel myChannel = Task.getTask().getChannel(THECHANNELNAME);

For the getCurrentChannel() and getChannel() methods, if the myChannel variable is null, the specified channel doesn't exist at this CICS link level, so you will need to create a channel for passing the serialized Java data object.

If you are going to add containers to a channel (pass serialized objects to another Java program in a different CICS Java environment), you could use the current channel, but you should be cautious, because whatever is already in the channel plus the containers you add to the channel will be passed to the target program. If the target program is in a different CICS region, then all container content will be transmitted to the CICS region containing the target program (which could add to application response time). If the containers are passed to a different Java environment within the same CICS region, then passing the container content will be a memory-to-memory transfer.

After you have a reference to a channel, you can use the following code to put data in a container, get data from a container, and delete a container. There are two types of CICS containers -- CHAR and BIT, and you will want to use BIT containers in this case. Both channel and container names can be from 1 to 16 characters.

byte[] myByteArray = "some bytes".getBytes();   // to be passed
Container myContainer = myChannel.createContainer(containerName);
myContainer.put(myByteArray);                   // this is a BIT container

Container myContainer = myChannel.getContainer(containerName);
byte[] myByteArray = myContainer.get() ;        // get the byte array from the container

myChannel.deleteContainer(containerName);       // delete the container

When working with channels and containers in your CICS-based Java program, various exceptions can be thrown, so place the above code snippets in try/catch blocks.

Invoking another CICS program from a CICS-based Java program

CICS-based Java programs that use other Java programs within the same CICS-provided Java environment should use normal Java techniques -- instantiating an object of the proper type and invoking the appropriate methods on those objects.

Invoking another CICS-based Java program that runs in a CICS-provided Java environment that is different from the current CICS-provided Java environment (or runs in a different CICS-provided Java environment under the control of a different CICS region) requires the equivalent of an EXEC CICS LINK to the other Java program. The Java equivalent of an EXEC CICS LINK command is straightforward, as shown below:

Program aProg = new Program();
aProg.setName(targetProgram);
aProg.link(theChannel);

In the above code snippet, the targetProgram is a variable containing the name of the CICS program to be LINKed to. The link(theChannel) method attempts the LINK, passing the specified channel and all containers in that channel. Place the above code snippet in a try/catch block.

From a theoretical perspective, that's all there is too it -- just serialize the object into a byte array, place the byte array into a container associated with a channel, LINK to the target program, and then deserialize the byte array passed in the container into a data object that can be used by your program.

There are just enough steps that the best approach is to code a set of generic classes to do these steps for you. While researching the concepts presented in this article, we wrote a set of classes that you can use to see the steps and the syntax of the commands, and can extend for your own use. (These Java classes have not been submitted to any official IBM testing and are provided "as-is" to illustrate concepts described in this article.) While the approach described in this article will make life easier for the Java application developer, performance will vary. So if you use the techniques described in this article, benchmark your results to see if your application performs at acceptable levels, or if, instead of using serialized objects, you should convert data from Java data objects to a field-oriented series-of-bytes structure when passing data between your Java programs (in other words, use the traditional CICS data passing technique, which is needed when passing data to CICS-based programs coded in multiple languages and not just in Java).

Using the provided Java classes to pass Java objects between CICS Java programs

When passing Java objects between CICS-based JVM environments, the originating CICS Java environment will be:

  • CICS dynamic scripting application
  • Axis2-based Web service wrapper program
  • CICS Transaction Gateway Java client program
  • CICS-based OSGi environment
  • CICS-based pooled JVM environment

The target CICS-based JVM environments where the target Java business logic program will be invoked are:

  • CICS-based OSGi environment
  • CICS-based pooled JVM environment

Again, the CICS-based OSGi environment is the strategic location for Java-based business logic in CICS, because it maximizes the flexibility for your business logic, and enables reuse by letting you expose Java-based business logic using a variety of CICS interoperability options, including Web services, RESTful interface, WebSphere MQ, and CICS dynamic scripting.

The source code for the sample object transporter should help you understand the approach of passing serialized objects in a CICS-provided Java environment. You can use the object transporter provided, or you can enhance the object transporter to accommodate any special requirements.

Passing Java objects from a CICS pooled JVM, OSGi-based JVM, or Axis2 JVM

Passing Java objects from a CICS pooled JVM, OSGi-based JVM, or Axis2-based JVM using the supplied Java class is the same, but how you specify where the supplied Java class resides is different for each environment. The DDW_CicsObjectTransporter object provided with this article lets you LINK to a target Java program named TARGET, passing objects by using the following approach:

String abc = "Some information to Pass";
Employee emp = new Employee("John Doe");
DDW_CicsObjectTransporter transporter = 
    new DDW_CicsObjectTransporter();
transporter.addObject("abc",abc).
    addObject("emp",emp).
    execute("TARGET");

// and after the program returns

String errorString = (String)transporter.getObject("error");
If (errorString != null) {
    processError();
} else {
    Manager newManager = (Manager)transporter.getObject("mgr");
    // process/display Manager object that was returned
}

In the DDW_CicsObjectTransporter class provided with this article, when responding to the code example above:

  1. The provided DDW_CicsObjectTransporter class (the transporter) uses a channel name of DDWddwObjTrnsptr. If the transporter was passed a channel of this name or the transporter has already created a channel with this name, then it will be used. Otherwise, the transporter will create a channel named DDWddwObjTrnsptr. Therefore, if you instantiate a second transporter, it will use the same channel named DDWddwObjTrnsptr, since one will already exist from the first class instance.
  2. The execute() method will cause a LINK request to the specified program.
  3. When a addObject(String containerName, Object obj) method is invoked, the obj is serialized and put it in a container whose name is containerName.
  4. The transporter verifies that that object reference by obj contains a field named serialVersionUID, but the transporter does not verify that the objects referenced by obj contain a serialVerisionUID field. This check does consume CPU cycles, so if you would like to avoid this check, you can invoke transporter.setCheckForSerialVersionUIDBeforeSerialization(false).
  5. If the getObject(String containerName) method is invoked, the byte array in the specified container is deserialized and the object is returned. The getObject() method returns an object of type Object, so you must typecast the returned Object into the type of object that you are expecting.
  6. If you invoke the removeObject(String containerName) method, the transporter will invoke a deleteContainer(containerName) method on the DDWddwObjTrnsptr channel to remove the container.
  7. The convention when using channels and containers for error situations is to return an error container (with some agreed-upon name) with an indication of what caused the error. Therefore the TARGET business logic program should add an error object to the transporter if it detects an error situation (such as "customer not found"). The above sample code tests for an error container named error. If it exists, the request had an error, so you would then process the error. If no error container is present, the request was successful.
  8. If the remote transporter in the TARGET program has an error serializing or deserializing, or getting, adding, or removing containers, the remote DDW_CicsObjectTransporter throws an exception and places an error container named DDWddwObjTErrorM in the channel. If the local transporter finds this error container, any transporter method request will throw an exception stating the cause of the error in the remote transporter.
  9. Likewise, if the local transporter has a problem serializing or deserializing and object, or getting, adding, or removing containers, the transporter throws an exception stating the cause of the error. Once any error occurs in the transporter, any transporter method request to the transporter will throw an exception stating the original problem. If you don't like this behavior, you can invoke the
    transporter.checkForTransporterErrorsInChannel(false) and
    transporter.writeTransporterErrorsToChannel(false) methods.
  10. Some of the error messages provided when native Java deserialization has a problem deserializing objects referenced by other objects do not provide a good indication as to the cause of the error. The transporter can only pass along the information that it is given.
  11. If you have problems with the transporter, you can use the transporter.setDebugLevel(9) method to have the transporter write messages to the CSMT Transient Data destination (specify 0 if you don't want debug messages -- the larger the debug number, the more detailed the messages). To write to stdout instead of a TD queue, use:
    transporter.setPrintToTdQueue(false).
    If you would like to send the messages to a TD queue other than CSMT, use:
    transporter.setTdQueueForPring("NAME").
    You can also set the debug level in the transporter constructor to see transporter initialization messages. Use:
    DDW_CicsObjectTransporter transporter = new
    DDW_CicsObjectTransporter(9);
    .

Your CICS-based Java environment will need access to the DDW_CicsObjectTransporter class contained in the com.ibm.ddw.cics.object.transporter.jar file provided with this article:

  • For a CICS-pooled JVM environment, add the JAR file to the CLASSPATH_SUFFIX variable in your CICS Java PROGRAM's JVMProfile file (PROGRAMs in a pooled JVM environment and JVMProfile files are described in Part 1 of this series).
  • For a CICS-provided Axis2 JVM environment, package the com.ibm.ddw.cics.object.transporter package with your application.
  • For a CICS OSGi-based environment, you can take three different approaches to make the object transporter available to your OSGi bundles:
    1. Include the source code in the com.ibm.ddw.cics.object.transporter package as one of the packages in your OSGi bundle that uses the transporter.
    2. Add the com.ibm.ddw.cics.object.transporter.jar file with your application in your bundle's root directory and specify the JAR file location in the CLASSPATH option of your OSGi bundle manifest.
    3. Place the com.ibm.ddw.cics.object.transporter.jar file in a common z/OS UNIX System Services file system location and specify the JAR file location in the CLASSPATH option of OSGi bundle's manifest.

The advantage of Option 3 is that you know that all of your OSGi bundles will access the same version of the object transporter code, but if you are concerned with OSGi bundle portability, consider one of the first two options. When deciding between Option 1 or 2, if you want to look at the object transporter as a black box implementation and not deal with source code, use Option 2.

The com.ibm.ddw.cics.object.transporter package cannot be used as a standalone OSGi bundle, and cannot be included in the OSGI_BUNDLES= parameter in the OSGi JVMServer's JVMProfile file, since the OSGi visibility rules will cause the object transporter to get a ClassNotFoundException when the object transporter tries to deserialize objects. You could import every package that will use the object transporter into the object transporter's OSGi bundle, but doing so is impractical because you would constantly have to change the object transporter's OSGi bundle for every new use of the object transporter. The com.ibm.ddw.cics.object.transporter JAR file includes both source and .class files and is included with this article.

Passing Java objects from a CICS dynamic scripting environment

As indicated above, CICS dynamic scripting provides a PHP and Groovy interpreter implemented in Java, and runs in a CICS JVM server. A PHP-to-Java bridge lets you instantiate and invoke Java classes and methods. You can also instantiate and invoke Java classes and methods using Groovy. Therefore DDW_CicsObjectTransporter can be used to pass Java objects from a PHP or Groovy script to a CICS-provided pooled or OSGi-based JVM, thus letting you write your presentation logic in PHP or Groovy, and keep your Java-based business logic in a CICS-pooled or OSGi-based JVM environment.

Although it is not shown below, you can also use Java as a programming language in your CICS dynamic scripting application. Using the object transporter in a Java program running in a CICS dynamic scripting environment is similar to the object transporter usage for a CICS-pooled JVM environment, except that the com.ibm.ddw.cics.object.transporter.jar file must be placed in your CICS dynamic scripting application's lib directory, and a zero resolve performed on your application.

Here is a simple example of using DDW_CicsObjectTransporter from a simple PHP script:

<html>
<head><title>Link to a CICS Java program with objects</title></head>
<body>
<?php
try {
    // create some Java objects using the PHP to Java bridge
    $someString = "some string";
    $myManager = new Java('com.ddw.transporter.test.Manager');
    $myManager->setManagerName('Roy');
    $staffArray = new Java('java.util.ArrayList');
    $aStaff = new Java('com.ddw.transporter.test.Staff', "Dennis");
    $staffArray->add($aStaff);
    $myManager->setStaff($staffArray->toArray());

    // transport objects to/from specified CICS Java program
    $transporter = new
Java('com.ibm.ddw.cics.object.transporter.DDW_CicsObjectTransporter');
    $transporter->addObject("someString", $someString);
    $transporter->addObject("myManager", $myManager);
    $transporter->execute("PROGRAMB");    // invoke CICS Java program
    $responseData = $transporter->getObject("responseData");
    echo $responseData;
} catch (Exception $e1) {
    echo 'Exception: '.$e1->getMessage();
}
?>
</body>
</html>

In the above code example, passing objects is similar to the CICS-provided Axis2, pooled, and OSGi environments.

  • You are adding an array of Staff to a Manager object, adding the Manager object to the transporter, and then invoking the CICS Java program named PROGRAMB.
  • The try/catch block tests for an exception, but the transporter will only throw a DDW_CicsObjectTransporterException, so you could have specifically tested for that exception.

To utilize DDW_CicsObjectTransporter in your CICS dynamic scripting application, add com.ibm.ddw.cics.object.transporter.jar to your application's lib directory and perform a zero resolve.

Receiving Java objects into a CICS-pooled JVM or an OSGi-based JVMServer

You can use the following code with DDW_CicsObjectTransporter to receive Java objects into a CICS-based Java program running in a CICS-pooled JVM or a CICS OSGi-based JVM server:

DDW_CicsObjectTransporter transporter = 
    new DDW_CicsObjectTransporter();
Employee emp = (Employee)transporter.getObject("emp");
// work with employee object
transporter.removeObject("abc");    // can also remove objects
Manger mgr = new Manger("Joe Bloggs");
transporter.addObject("mgr",mgr);  // add new or changed objects
return;

In the above code example:

  1. The provided DDW_CicsObjectTransporter class (the transporter) uses a channel name of DDWddwObjTrnsptr. In this case, since this Java program was invoked from a remote CICS-based Java program by a transporter, the current channel (the channel that was passed to this program) has a name of DDWddwObjTrnsptr. You can use the transporter's getObject(), removeObject(), and addObject() methods.
  2. Any objects that are added to the transporter will flow back to the LINKing program.
  3. Although it would be somewhat confusing, you could instantiate a second or third transporter. All transporter instances would use the current channel since it has a name of DDWddwObjTrnsptr. There would be no conflicts.
  4. Although not shown, you could use the execute() method to LINK to another Java program in yet another CICS-based Java environment.
  5. The convention when using channels and containers for error situations is to return an error container (with some agreed-upon name) with an indication of what caused the error. Therefore the LINKed-to program (this program) should add an error container to the channel if an error was encountered. You could add code like this:
    String error = "The customer named "+theCustomerName+" was not found");
    transporter.addObject("error", error);
    .
    And in the LINKing program you would access the error data like this:
    String error = (String)transporter.getObject("error");
    // null if no ‘error'if (error != null)
    {// handle the error here or throw new ApplicationException(error);}
  6. If this transporter has an error serializing or deserializing, or getting, adding, or removing containers, it throws an exception and places an error container named DDWddwObjTErrorM in the channel. If LINKing program's transporter finds this error container, any requests to that transporter will throw an exception stating the cause of the error in this (the remote) transporter.
  7. If you invoke any methods on this transporter after it has detected a serialization or deserialization problem, it will throw an exception stating the original problem that caused the initial error. If you don't like this behavior, you can invoke the
    transporter.checkForTransporterErrorsInChannel(false)
    method and if you don't want to pass transporter errors back to the LINKing program's transporter, invoke the
    transporter.writeTransporterErrorsToChannel(false) method.
  8. Some of the error messages provided when native Java deserialization has a problem deserializing objects referenced by other objects do not provide a good indication as to the cause of the error. The transporter can only pass along the information that it is given.
  9. If you have problems with the transporter, you can use the transporter.setDebugLevel(9) to have the transporter write messages to the CICS CSMT Transient Data destination. Specify 0 if you don't want debug messages -- the larger the debug number, the more detailed the messages. To write to stdout instead of a TD queue, use:
    transporter.setPrintToTdQueue(false).
    If you would like to send the messages to a TD queue other than CSMT, use:
    transporter.setTdQueueForPring("NAME").
    You can also set the debug level in the transporter constructor to see transporter initialization messages. Use:
    DDW_CicsObjectTransporter transporter = new
    DDW_CicsObjectTransporter(9);

Your receiving CICS-based Java environment will need access to the DDW_CicsObjectTransporter class included in the com.ibm.ddw.cics.object.transporter.jar file provided with this article.

  • For a CICS-pooled JVM environment, add the JAR file to the CLASSPATH_SUFFIX variable in your CICS Java PROGRAM's JVMProfile file (PROGRAMs in a pooled JVM environment and JVMProfile files are described in Part 1 of this series).
  • For a CICS OSGi-based environment, you can take three different approaches to make the object transporter available to your OSGi bundles:
    1. Include the source code in the com.ibm.ddw.cics.object.transporter package as one of the packages in your OSGi bundle that uses the transporter.
    2. Add the com.ibm.ddw.cics.object.transporter.jar file with your application (in your bundle's root directory) and specify the JAR file's location in the CLASSPATH option of your OSGi bundle manifest.
    3. Place the com.ibm.ddw.cics.object.transporter.jar file in a common z/OS UNIX System Services file system location and specify the JAR file's location in the CLASSPATH option of OSGi bundle's manifest.

The advantage of Option 3 is that you know that all of your OSGi bundles will access the same version of the object transporter code, but if you are concerned with OSGi bundle portability, consider one of the first two options. When deciding between Option 1 or 2, if you want to look at the object transporter as a black box implementation and not deal with source code, use Option 2.

The com.ibm.ddw.cics.object.transporter package cannot be used as a standalone OSGi bundle, and cannot be included in the OSGI_BUNDLES= parameter in the OSGi JVMServer's JVMProfile file, since the OSGi visibility rules will cause the object transporter to get a ClassNotFoundException when the object transporter tries to deserialize objects. You could import every package that will use the object transporter into the object transporter's OSGi bundle, but doing so is impractical because you would constantly have to change the object transporter's OSGi bundle for every new use of the object transporter. The com.ibm.ddw.cics.object.transporter JAR file includes both source and .class files and is included with this article.

Performance

One of the first comments from the subject matter experts who reviewed this article concerned performance. There is a cost to serialize and deserialize objects. When using the provided object transporter, you should benchmark your application early in the development life cycle to verify that your application has the performance characteristics you need. Although passing data between CICS-based Java programs using the traditional field-oriented series-of-bytes approach requires you to run additional utilities and know some details of a procedural language such as COBOL or PL/I, this traditional field-oriented series-of-bytes approach to passing data may perform better.

Error checking was added to the sample transporter provided with this article and most of the error checking is enabled by default. To improve the performance of the provided transporter sample code, you can use the methods listed below to disable some of the error checking. Also, by default, when version mismatch errors are detected, a warning is issued and the transporter continues to function. If you would like the transporter to become disabled if a remote transporter at a different level is detected, use the setStopTransporterActivityIfLocalRemoteVersionsDifferent(true) method.

setCheckForTransporterErrorsInChannel(false);       
   // Don't check for errors from previous requests
setCheckForSerialVersionUIDBeforeSerialization(false); 
   // Don't take time to check for serialVersionUID field on objects to be serialized
setCheckTheTransporterLevelWithWhichWeAreCommunicating(false); 
   // Don't take time to check if local and remote transporter are at same level

As indicated before, only use the object transporter when moving between CICS-based Java environments. When staying within a single CICS-based Java environment, use normal Java programming, instantiating objects and invoking methods.

The DDW_CicsObjectTransporter class is intended to implement a technique familiar to the Java application programmer, and to make passing objects simple. Since the object transporter is so easy to use for a Java application programmer, it will be very easy to get really sloppy. If you pass objects that contain more information than you need to pass, or the objects you pass reference other objects that don't need to be passed, you will be wasting time serializing data needlessly and will be transferring extra data needlessly; both of which will increase your response time needlessly.

Debugging object passing issues

You can set the debug message level on the object transporters using an int field with a value of 1 to 9 as an argument to the constructor, or by using the transporter.setDebugLevel(5) method. The higher the number, the more detailed the messages are. If you want the messages to go to stdout, invoke transporter.setPrintToTdQueue(false), or else the message will go to the CSMT transient data destination. If you want to change the destination, invoke transporter.setTdQueueForPrint("ABCD").

You can also set the debug level in the transporter constructor to see transporter initialization messages. Use:
DDW_CicsObjectTransporter transporter = new
DDW_CicsObjectTransporter(9);
.

Workload distribution

It is typical in a large CICS environment to distribute your application requests across multiple CICS regions for availability and scalability. CICS normally performs workload distribution on a by-program basis. Since techniques described in the article provide an easy way to pass objects to a CICS-based Java program, you could use these Java object passing techniques and still have CICS perform workload distribution of your Java programs.

Language interoperability

One of the strengths of CICS is its ability to interoperate with business logic written in several different programming languages. The techniques in this article assume that you need to pass data between two CICS-based Java program running in two different CICS-provided Java environments. Since languages other than Java do not understand serialized Java objects, you cannot use the object transporter approach to interoperate with an Assembler, C, C++, COBOL, or PL/I CICS program. Within a CICS environment, to interoperate between Java and Assembler, COBOL, or PL/I, you must move data into a format that can be understood by the Assembler, COBOL, or PL/I program -- a field-oriented series-of-bytes format.

Java object serialization

For a discussion of the pros and cons or the detailed aspects of object serialization (either native Java serialization or some of the available open-source serialization/deserialization routines), see the appropriate Java documentation.

Conclusion

This article described the steps for passing Java data objects between Java programs running in the different CICS Java environments that CICS provides:

  • Serialize the Java data object into a byte array.
  • Place the byte array into a CICS container associated with a channel.
  • Invoke the equivalent to the EXEC CICS LINK command to pass the channel holding the containers whose content is the serialized Java data objects
  • In the LINKed-to program, get the byte array from the CICS container and deserialize the byte array into a usable Java data object.

Finally, the article described the provided Java classes that implement the techniques described in the article. These Java classes have not been submitted to any official IBM testing and are provided "as-is" solely to illustrate the concepts described in this article. The techniques described in this article will make life easier for the Java application developer, and as in any environment, performance will vary. If you use these techniques, benchmark your results to determine if your application performs at acceptable levels, or whether, instead of using serialized objects, you should convert data from Java data objects to a field-oriented series-of-bytes structure when passing data between your Java programs.

Part 3 will show you how to pass Java data objects from a CICS Transaction Gateway Java client program to a CICS Transaction Server Java environment.

Acknowledgements

The author would like to thank the following IBM colleagues for their help in reviewing this article and providing suggestions for content and improvement:

  • Steve Fowlkes, Certified IT Specialist, CICS Technical Support, IBM USA
  • Leigh Compton, Certified IT Specialist, CICS Advanced Technical Skills Team, IBM USA
  • Mark Cocker, CICS Technical Strategy and Planning, IBM UK
  • Phil Wakelin, CICS Technical Strategy and Planning, Java and Access to CICS, IBM UK

Download

DescriptionNameSize
Code samplecom.ibm.ddw.cics.object.transporter.zip28 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Java technology
ArticleID=784970
ArticleTitle=Passing data objects between CICS Java environments: Part 2: Techniques for passing Java data objects
publish-date=01112012