Modeling CCI Access as Operation Objects

The org.springframework.jca.cci.object package contains support classes that let you access the EIS in a different style: through reusable operation objects, analogous to Spring’s JDBC operation objects (see the JDBC section of the Data Access chapter). This usually encapsulates the CCI API. An application-level input object is passed to the operation object, so it can construct the input record and then convert the received record data to an application-level output object and return it.

This approach is internally based on the CciTemplate class and the RecordCreator or RecordExtractor interfaces, reusing the machinery of Spring’s core CCI support.

Using MappingRecordOperation

MappingRecordOperation essentially performs the same work as CciTemplate but represents a specific, pre-configured operation as an object. It provides two template methods to specify how to convert an input object to an input record and how to convert an output record to an output object (record mapping):

  • createInputRecord(..): to specify how to convert an input object to an input Record

  • extractOutputData(..): to specify how to extract an output object from an output Record

The following listing shows the signatures of these methods:

public abstract class MappingRecordOperation extends EisOperation {

	...

	protected abstract Record createInputRecord(RecordFactory recordFactory,
			Object inputObject) throws ResourceException, DataAccessException {
		// ...
	}

	protected abstract Object extractOutputData(Record outputRecord)
			throws ResourceException, SQLException, DataAccessException {
		// ...
	}

	...

}

Thereafter, ito execute an EIS operation, you need to use a single execute method, passing in an application-level input object and receiving an application-level output object as the result. The following example shows how to do so:

public abstract class MappingRecordOperation extends EisOperation {

	...

	public Object execute(Object inputObject) throws DataAccessException {
	}

	...
}

Contrary to the CciTemplate class, this execute(..) method does not have an InteractionSpec as an argument. Instead, the InteractionSpec is global to the operation. You must use the following constructor to instantiate an operation object with a specific InteractionSpec. The following example shows how to do so:

InteractionSpec spec = ...;
MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec);
...

Using MappingCommAreaOperation

Some connectors use records based on a COMMAREA, which represents an array of bytes that contain parameters to send to the EIS and the data returned by it. Spring provides a special operation class for working directly on COMMAREA rather than on records. The MappingCommAreaOperation class extends the MappingRecordOperation class to provide this special COMMAREA support. It implicitly uses the CommAreaRecord class as the input and output record type and provides two new methods to convert an input object into an input COMMAREA and convert the output COMMAREA into an output object. The following listing shows the relevant method signatures:

public abstract class MappingCommAreaOperation extends MappingRecordOperation {

	...

	protected abstract byte[] objectToBytes(Object inObject)
			throws IOException, DataAccessException;

	protected abstract Object bytesToObject(byte[] bytes)
		throws IOException, DataAccessException;

	...

}

Automatic Output Record Generation

As every MappingRecordOperation subclass is based on CciTemplate internally, the same way to automatically generate output records as with CciTemplate is available. Every operation object provides a corresponding setOutputRecordCreator(..) method. For further information, see automatic-output-generation.

Summary

The operation object approach uses records in the same manner as the CciTemplate class.

Table 1. Usage of Interaction execute methods
MappingRecordOperation method signature MappingRecordOperation outputRecordCreator property execute method called on the CCI Interaction

Object execute(Object)

Not set

Record execute(InteractionSpec, Record)

Object execute(Object)

Set

boolean execute(InteractionSpec, Record, Record)

Example of MappingRecordOperation Usage

In this section, we show how to use MappingRecordOperation to access a database with the Blackbox CCI connector.

The original version of this connector is provided by the Java EE SDK (version 1.3), which is available from Oracle.

First, you must do some initializations on the CCI InteractionSpec to specify which SQL request to execute. In the following example, we directly define the way to convert the parameters of the request to a CCI record and the way to convert the CCI result record to an instance of the Person class:

public class PersonMappingOperation extends MappingRecordOperation {

	public PersonMappingOperation(ConnectionFactory connectionFactory) {
		setConnectionFactory(connectionFactory);
		CciInteractionSpec interactionSpec = new CciConnectionSpec();
		interactionSpec.setSql("select * from person where person_id=?");
		setInteractionSpec(interactionSpec);
	}

	protected Record createInputRecord(RecordFactory recordFactory,
			Object inputObject) throws ResourceException {
		Integer id = (Integer) inputObject;
		IndexedRecord input = recordFactory.createIndexedRecord("input");
		input.add(new Integer(id));
		return input;
	}

	protected Object extractOutputData(Record outputRecord)
			throws ResourceException, SQLException {
		ResultSet rs = (ResultSet) outputRecord;
		Person person = null;
		if (rs.next()) {
			Person person = new Person();
			person.setId(rs.getInt("person_id"));
			person.setLastName(rs.getString("person_last_name"));
			person.setFirstName(rs.getString("person_first_name"));
		}
		return person;
	}
}

Then the application can execute the operation object, with the person identifier as an argument. Note that you could set up the operation object as a shared instance, as it is thread-safe. The following executes the operation object with the person identifier as an argument:

public class MyDaoImpl extends CciDaoSupport implements MyDao {

	public Person getPerson(int id) {
		PersonMappingOperation query = new PersonMappingOperation(getConnectionFactory());
		Person person = (Person) query.execute(new Integer(id));
		return person;
	}
}

The corresponding configuration of Spring beans could be as follows in non-managed mode:

<bean id="managedConnectionFactory"
		class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory">
	<property name="connectionURL" value="jdbc:hsqldb:hsql://localhost:9001"/>
	<property name="driverName" value="org.hsqldb.jdbcDriver"/>
</bean>

<bean id="targetConnectionFactory"
		class="org.springframework.jca.support.LocalConnectionFactoryBean">
	<property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="connectionFactory"
		class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
	<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
	<property name="connectionSpec">
		<bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
			<property name="user" value="sa"/>
			<property name="password" value=""/>
		</bean>
	</property>
</bean>

<bean id="component" class="MyDaoImpl">
	<property name="connectionFactory" ref="connectionFactory"/>
</bean>

In managed mode (that is, in a Java EE environment), the configuration could be as follows:

<jee:jndi-lookup id="targetConnectionFactory" jndi-name="eis/blackbox"/>

<bean id="connectionFactory"
		class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
	<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
	<property name="connectionSpec">
		<bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
			<property name="user" value="sa"/>
			<property name="password" value=""/>
		</bean>
	</property>
</bean>

<bean id="component" class="MyDaoImpl">
	<property name="connectionFactory" ref="connectionFactory"/>
</bean>

Example of MappingCommAreaOperation Usage

In this section, we show how to use the usage of the MappingCommAreaOperation to access a CICS with ECI mode with the IBM CICS ECI connector.

First, we need to initialize the CCI InteractionSpec to specify which CICS program to access and how to interact with it, as the following example shows:

public abstract class EciMappingOperation extends MappingCommAreaOperation {

	public EciMappingOperation(ConnectionFactory connectionFactory, String programName) {
		setConnectionFactory(connectionFactory);
		ECIInteractionSpec interactionSpec = new ECIInteractionSpec(),
		interactionSpec.setFunctionName(programName);
		interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE);
		interactionSpec.setCommareaLength(30);
		setInteractionSpec(interactionSpec);
		setOutputRecordCreator(new EciOutputRecordCreator());
	}

	private static class EciOutputRecordCreator implements RecordCreator {
		public Record createRecord(RecordFactory recordFactory) throws ResourceException {
			return new CommAreaRecord();
		}
	}

}

We can then subclass the abstract EciMappingOperation class to specify mappings between custom objects and Records, as the following example shows:

public class MyDaoImpl extends CciDaoSupport implements MyDao {

	public OutputObject getData(Integer id) {
		EciMappingOperation query = new EciMappingOperation(getConnectionFactory(), "MYPROG") {

			protected abstract byte[] objectToBytes(Object inObject) throws IOException {
				Integer id = (Integer) inObject;
				return String.valueOf(id);
			}

			protected abstract Object bytesToObject(byte[] bytes) throws IOException;
				String str = new String(bytes);
				String field1 = str.substring(0,6);
				String field2 = str.substring(6,1);
				String field3 = str.substring(7,1);
				return new OutputObject(field1, field2, field3);
			}
		});

		return (OutputObject) query.execute(new Integer(id));
	}

}

The corresponding configuration of Spring beans could be as follows in non-managed mode:

<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
	<property name="serverName" value="TXSERIES"/>
	<property name="connectionURL" value="local:"/>
	<property name="userName" value="CICSUSER"/>
	<property name="password" value="CICS"/>
</bean>

<bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean">
	<property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="component" class="MyDaoImpl">
	<property name="connectionFactory" ref="connectionFactory"/>
</bean>

In managed mode (that is, in a Java EE environment), the configuration could be as follows:

<jee:jndi-lookup id="connectionFactory" jndi-name="eis/cicseci"/>

<bean id="component" class="MyDaoImpl">
	<property name="connectionFactory" ref="connectionFactory"/>
</bean>