J2EE provides a specification to standardize access to EIS: JCA (Java Connector Architecture). This specification is divided into several different parts:
SPI (Service provider interfaces) that the connector provider must implement. These interfaces constitute a resource adapter which can be deployed on a J2EE application server. In such a scenario, the server manages connection pooling, transaction and security (managed mode). The application server is also responsible for managing the configuration, which is held outside the client application. A connector can be used without an application server as well; in thid case, the application must configure it directly (non-managed mode).
CCI (Common Client Interface) that an application can use to interact with the connector and thus communicate with an EIS. An API for local transaction demarcation is provided as well.
The aim of the Spring CCI support is to provide classes to access a CCI connector in typical Spring style, leveraging's Spring general resource and transaction management facilities.
Important note: The client side of connectors doesn't alway use CCI. Some connectors expose their own APIs, only providing JCA resource adapter to use the system contracts of a J2EE container (connection pooling, global transactions, security). Spring does not offer special support for such connector-specific APIs.
The base resource to use JCA CCI is the ConnectionFactory interface. The connector used must provide an implementation of this interface.
To use your connector, you can deploy it on your application server and fetch the ConnectionFactory from the server's JNDI environment (managed mode). The connector must be packaged as a RAR file (resource adapter archive) and contain a ra.xml file to describe its deployment characteristics. The actual name of the resource is specified when you deploy it. To access it within Spring, simply use Spring's JndiObjectFactoryBean to fetch the factory by its JNDI name.
Another way to use a connector is to embed it in your application (non-managed mode), not using an application server to deploy and configure it. Spring offers the possibility to configure a connector as a bean, through a provided FactoryBean (LocalConnectionFactoryBean). In this manner, you only need the connector library in the classpath (no RAR file and no ra.xml descriptor needed). The library must be extracted from the connector's RAR file, if necessary.
Once you got access to your ConnectionFactory instance, you can inject it into your components. These components can either be coded against the plain CCI API or leverage Spring's support classes for CCI access (e.g. CciTemplate).
Important note: When you use a connector in non-managed mode, you can't use global transactions because the resource is never enlisted / delisted in the current global transaction of the current thread. The resource is simply not aware of any global J2EE transactions that might be running.
In order to make connections to the EIS, you need to obtain a ConnectionFactory from the application server if you are in a managed mode, or directly from Spring if you are in a non-managed mode.
In a managed mode, you access it from JNDI; its properties will be configured in the application server.
<bean id="eciConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>eis/cicseci</value> </property> </bean>
In non-managed mode, you must configure the ConnectionFactory you want to use in the configuration of Spring as a JavaBean. The LocalConnectionFactoryBean class offers this setup style, passing in the ManagedConnectionFactory implementation of your connector, exposing the application-level CCI ConnectionFactory.
<bean id="eciManagedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> <property name="serverName"><value>TXSERIES</value></property> <property name="connectionURL"><value>tcp://localhost/</value></property> <property name="portNumber"><value>2006</value></property> </bean> <bean id="eciConnectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> <property name="managedConnectionFactory"> <ref local="eciManagedConnectionFactory"/> </property> </bean>
Important note: You can't directly instantiate a specific ConnectionFactory. You need to go through the corresponding implementation of the ManagedConnectionFactory interface for your connector. This interface is part of the JCA SPI specification.
JCA CCI allow the developer to configure the connections to the EIS using the ConnectionSpec implementation of your connector. In order to configure its properties, you need to wrap the target connection factory with a dedicated adapter, ConnectionSpecConnectionFactoryAdapter. So, the dedicated ConnectionSpec can be configured with the property connectionSpec (as an inner bean).
This property is not mandatory because the CCI ConnectionFactory interface defines two different methods to obtain a CCI connection. Some of the ConnectionSpec properties can often be configured in the application server (in managed mode) or on the corresponding local ManagedConnectionFactory implementation.
public interface ConnectionFactory implements Serializable, Referenceable { ... Connection getConnection() throws ResourceException; Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException; ... }
Spring provided a ConnectionSpecConnectionFactoryAdapter that allows for specifying a ConnectionSpec instance to use for all operations on a given factory. If the adapter's connectionSpec property is specified, the adapter uses the getConnection variant without argument, else the one with the ConnectionSpec argument.
<bean id="managedConnectionFactory" class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory"> <property name="connectionURL"> <value>jdbc:hsqldb:hsql://localhost:9001</value> </property> <property name="driverName"><value>org.hsqldb.jdbcDriver</value></property> </bean> <bean id="targetConnectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> <property name="managedConnectionFactory"> <ref local="managedConnectionFactory"/> </property> </bean> <bean id="connectionFactory" class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter"> <property name="targetConnectionFactory"> <ref bean="targetConnectionFactory"/> </property> <property name="connectionSpec"> <bean class="com.sun.connector.cciblackbox.CciConnectionSpec"> <property name="user"><value>sa</value></property> <property name="password"><value/></property> </bean> </property> </bean>
If you want to use a single CCI connection, Spring provides a further ConnectionFactory adapter to manage this. The SingleConnectionFactory adapter will open a single connection lazily and close it when this bean is destroyed at application shutdown. This class will expose special Connection proxies that behave accordingly, all sharing the same underlying physical connection.
<bean id="eciManagedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> <property name="serverName"><value>TEST</value></property> <property name="connectionURL"><value>tcp://localhost/</value></property> <property name="portNumber"><value>2006</value></property> </bean> <bean id="targetEciConnectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> <property name="managedConnectionFactory"> <ref local="eciManagedConnectionFactory"/> </property> </bean> <bean id="eciConnectionFactory" class="org.springframework.jca.cci.connection.SingleConnectionFactory"> <property name="targetConnectionFactory"> <ref local="targetEciConnectionFactory"/> </property> </bean>
Important note: This ConnectionFactory adapter cannot directly be configured with a ConnectionSpec. Use an intermediary ConnectionSpecConnectionFactoryAdapter that the SingleConnectionFactory talks to if you require a single connection for a specific ConnectionSpec.
One of the aims of the JCA CCI support is to provide convenient facilities for manipulating CCI records. The developer can specify the strategy to create records and extract datas from records, for use with Spring's CciTemplate. The following interfaces will configure the strategy to use input and output records if you don't want to work with records directly in your application.
In order to create an input Record, the developer can use a dedicated implementation of the RecordCreator interface.
public interface RecordCreator { Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException; }
As you can see, the createRecord method receives a RecordFactory instance as parameter, which corresponds to the RecordFactory of the ConnectionFactory used. This reference can be used to create IndexedRecord or MappedRecord instances. The following sample shows how to use the RecordCreator interface and indexed/mapped records.
public class MyRecordCreator implements RecordCreator { public Record createRecord(RecordFactory recordFactory) throws ResourceException { IndexedRecord input = recordFactory.createIndexedRecord("input"); input.add(new Integer(id)); return input; } };
An output Record can be used to receive data back from the EIS. Hence, a specific implementation of the RecordExtractor interface can be passed to Spring's CciTemplate for extracting data from the output Record.
public interface RecordExtractor { Object extractData(Record record) throws ResourceException, SQLException, DataAccessException; }
The following sample shows how to use the RecordExtractor.
public class MyRecordExtractor implements RecordExtractor { public Object extractData(Record record) throws ResourceException { CommAreaRecord commAreaRecord = (CommAreaRecord) record; String str = new String(commAreaRecord.toByteArray()); String field1 = string.substring(0,6); String field2 = string.substring(6,1); return new OutputObject(Long.parseLong(field1), field2); } };
This is the central class of the core CCI support package (org.springframework.jca.cci.core). It simplifies the use of CCI since it handles the creation and release of resources. This helps to avoid common errors like forgetting to always close the connection. It cares for the lifecycle of connection and interaction objects, letting application code focus on generating input records from application data and extracting application data from output records.
The JCA CCI specification defines two distinct methods to call operations on an EIS. The CCI Interaction interface provides two execute method signatures:
public interface javax.resource.cci.Interaction { ... boolean execute(InteractionSpec spec, Record input, Record output) throws ResourceException; Record execute(InteractionSpec spec, Record input) throws ResourceException; ... }
Depending on the template method called, CciTemplate will know which execute method to call on the interaction. In any case, a correctly initialized InteractionSpec instance is mandatory.
CciTemplate.execute can be used in two ways:
With direct Record arguments. In this case, you simply need to pass the CCI input record in, and the returned object be the corresponding CCI output record.
With application objects, using record mapping. In this case, you need to provide corresponding RecordCreator and RecordExtractor instances.
With the first approach, the following methods of the template will be used. These methods directly correspond to those on the Interaction interface.
public class CciTemplate implements CciOperations { ... public Record execute(InteractionSpec spec, Record inputRecord) throws DataAccessException { ... } public void execute(InteractionSpec spec, Record inputRecord, Record outputRecord) throws DataAccessException { ... } ... }
With the second approach, we need to specify the record creation and record extraction strategies as arguments. The interfaces used are those describe in the previous section on record conversion. The corresponding CciTemplate methods are the following:
public class CciTemplate implements CciOperations { ... public Record execute(InteractionSpec spec, RecordCreator inputCreator) throws DataAccessException { ... } public Object execute(InteractionSpec spec, Record inputRecord, RecordExtractor outputExtractor) throws DataAccessException { ... } public Object execute(InteractionSpec spec, RecordCreator creator, RecordExtractor extractor) throws DataAccessException { ... } ... }
Unless the outputRecordCreator property is set on the template (see the following section), every method will call the corresponding execute method of the CCI Interaction with two parameters: InteractionSpec and input Record, receiving an output Record as return value.
CciTemplate also provides methods to create IndexRecord and MappedRecord outside a RecordCreator implementation, through its createIndexRecord and createMappedRecord methods. This can be used within DAO implementations to create Record instances to pass into corresponding CciTemplate.execute methods.
public class CciTemplate implements CciOperations { ... public IndexedRecord createIndexedRecord(String name) throws DataAccessException { ... } public MappedRecord createMappedRecord(String name) throws DataAccessException { ... } ... }
Spring's CCI support provides a abstract class for DAOs, supporting injection of a ConnectionFactory or a CciTemplate instances. The name of the class is CciDaoSupport: It provides simple setConnectionFactory and setCciTemplate methods. Internally, this class will create a CciTemplate instance for a passed-in ConnectionFactory, exposing it to concrete data access implementations in subclasses.
public abstract class CciDaoSupport { ... public void setConnectionFactory(ConnectionFactory connectionFactory) { ... } public ConnectionFactory getConnectionFactory() { ... } public void setCciTemplate(CciTemplate cciTemplate) { ... } public CciTemplate getCciTemplate() { ... } ... }
If the connector used only supports the Interaction.execute method with input and output records as parameters (that is, it requires the desired output record to be passed in instead of returning an appropriate output record), you can set the outputRecordCreator property of the CciTemplate to automatically generate an output record to be filled by the JCA connector when the response is received. This record will be then returned to the caller of the template.
This property simply holds an implementation of the RecordCreator interface, used for that purpose. The RecordCreator interface has already been discussed in a previous section. The outputRecordCreator property must be directly specified on the CciTemplate. This could be done in the application code:
cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator());
or in the Spring configuration, if the CciTemplate is configured as a dedicated bean instance:
<bean id="eciOutputRecordCreator" class="eci.EciOutputRecordCreator"/> <bean id="cciTemplate" class="org.springframework.jca.cci.core.CciTemplate"> <property name="connectionFactory"> <ref local="eciConnectionFactory"/> </property> <property name="outputRecordCreator"> <ref local="eciOutputRecordCreator"/> </property> </bean>
Note: As the CciTemplate class is thread-safe, it will usually be configured as a shared instance.
The following table summarizes the mechanism of the CciTemplate class and the corresponding methods called on the CCI Interaction interface:
Table 20.1. Usage of Interaction execute methods
CciTemplate method signature | CciTemplate outputRecordCreator property | execute method called on the CCI Interaction |
---|---|---|
Record execute(InteractionSpec, Record) | not set | Record execute(InteractionSpec, Record) |
Record execute(InteractionSpec, Record) | set | boolean execute(InteractionSpec, Record, Record) |
void execute(InteractionSpec, Record, Record) | not set | void execute(InteractionSpec, Record, Record) |
void execute(InteractionSpec, Record, Record) | set | void execute(InteractionSpec, Record, Record) |
Record execute(InteractionSpec, RecordCreator) | not set | Record execute(InteractionSpec, Record) |
Record execute(InteractionSpec, RecordCreator) | set | void execute(InteractionSpec, Record, Record) |
Record execute(InteractionSpec, Record, RecordExtractor) | not set | Record execute(InteractionSpec, Record) |
Record execute(InteractionSpec, Record, RecordExtractor) | set | void execute(InteractionSpec, Record, Record) |
Record execute(InteractionSpec, RecordCreator, RecordExtractor) | not set | Record execute(InteractionSpec, Record) |
Record execute(InteractionSpec, RecordCreator, RecordExtractor) | set | void execute(InteractionSpec, Record, Record) |
CciTemplate also offers the possibility to work directly with CCI connections and interactions, in the same manner as JdbcTemplate and JmsTemplate. This is useful when you want to perform multiple operations on a CCI connection or interaction, for example.
The interface ConnectionCallback provides a CCI Connection as argument, in order to perform custom operations on it, plus the CCI ConnectionFactory which the Connection was created with. The latter can be useful for example to get an associated RecordFactory instance and create indexed/mapped records, for example.
public interface ConnectionCallback { Object doInConnection(Connection connection, ConnectionFactory connectionFactory) throws ResourceException, SQLException, DataAccessException; }
The interface InteractionCallback provides the CCI Interaction, in order to perform custom operations on it, plus the corresponding CCI ConnectionFactory.
public interface InteractionCallback { Object doInInteraction(Interaction interaction, ConnectionFactory connectionFactory) throws ResourceException, SQLException, DataAccessException; }
Note: InteractionSpec objects can either be shared across multiple template calls and newly created inside every callback method. This is completely up to the DAO implementation.
In this section, the usage of the CciTemplate will be shown to acces to a CICS with ECI mode, with the IBM CICS ECI connector.
Firstly, some initializations on the CCI InteractionSpec must be done to specify which CICS program to access and how to interact with it.
ECIInteractionSpec interactionSpec = new ECIInteractionSpec(); interactionSpec.setFunctionName("MYPROG"); interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE);
Then the program can use CCI via Spring's template and specify mappings between custom objects and CCI Records.
public class MyDaoImpl extends CciDaoSupport implements MyDao { public OutputObject getData(InputObject input) { ECIInteractionSpec interactionSpec = ...; OutputObject output = (ObjectOutput) getCciTemplate().execute(interactionSpec, new RecordCreator() { public Record createRecord(RecordFactory recordFactory) throws ResourceException { return new CommAreaRecord(input.toString().getBytes()); } }, new RecordExtractor() { public Object extractData(Record record) throws ResourceException { CommAreaRecord commAreaRecord = (CommAreaRecord)record; String str = new String(commAreaRecord.toByteArray()); String field1 = string.substring(0,6); String field2 = string.substring(6,1); return new OutputObject(Long.parseLong(field1), field2); } }); return output; } }
As discussed previously, callbacks can be used to work directly on CCI connections or interactions.
public class MyDaoImpl extends CciDaoSupport implements MyDao { public OutputObject getData(InputObject input) { ObjectOutput output = (ObjectOutput) getCciTemplate().execute( new ConnectionCallback() { public Object doInConnection(Connection connection, ConnectionFactory factory) throws ResourceException { ... } }); } return output; } }
Important note: With a ConnectionCallback, the Connection used will be managed and closed by the CciTemplate, but any interactions created on the connection must be managed by the callback implementation.
For a more specific callback, you can implement an InteractionCallback. The passed-in Interaction will be managed and closed by the CciTemplate in this case.
public class MyDaoImpl extends CciDaoSupport implements MyDao { public String getData(String input) { ECIInteractionSpec interactionSpec = ...; String output = (String) getCciTemplate().execute(interactionSpec, new InteractionCallback() { public Object doInInteraction(Interaction interaction, ConnectionFactory factory) throws ResourceException { Record input = new CommAreaRecord(inputString.getBytes()); Record output = new CommAreaRecord(); interaction.execute(holder.getInteractionSpec(), input, output); return new String(output.toByteArray()); } }); return output; } }
For the examples above, the corresponding configuration of the involved Spring beans could look like this in non-managed mode:
<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> <property name="serverName"><value>TXSERIES</value></property> <property name="connectionURL"><value>local:</value></property> <property name="userName"><value>CICSUSER</value></property> <property name="password"><value>CICS</value></property> </bean> <bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> <property name="managedConnectionFactory"> <ref local="managedConnectionFactory"/> </property> </bean> <bean id="component" class="mypackage.MyDaoImpl"> <property name="connectionFactory"><ref local="connectionFactory"/></property> </bean>
In managed mode (that is, in a J2EE environment), the configuration could look as follows:
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"><value>eis/cicseci</value></property> </bean> <bean id="component" class="MyDaoImpl"> <property name="connectionFactory"><ref local="connectionFactory"/></property> </bean>
The org.springframework.jca.cci.object package contains support classes that allow you to access the EIS in a different style: through reusable operation objects, analogous to Spring's JDBC operation objects (see JDBC chapter). This will usually encapsulate the CCI API: an application-level input object will be 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.
Note: This approach is internally based on the CciTemplate class and the RecordCreator / RecordExtractor interfaces, reusing the machinery of Spring's core CCI support.
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 a 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
Here are 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, in order to 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 result:
public abstract class MappingRecordOperation extends EisOperation { ... public Object execute(Object inputObject) throws DataAccessException { ... }
As you can see, contrary to the CciTemplate class, this execute method does not have an InteractionSpec as argument. Instead, the InteractionSpec is global to the operation. The following constructor must be used to instantiate an operation object with a specific InteractionSpec:
InteractionSpec spec = ...; MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec); ...
Some connectors use records based on a COMMAREA which represents an array of bytes containing parameters to send to the EIS and 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 such special COMMAREA support. It implicitly uses the CommAreaRecord class as input and output record type, and provides two new methods to convert an input object into an input COMMAREA and the output COMMAREA into an output object.
public abstract class MappingCommAreaOperation extends MappingRecordOperation { ... protected abstract byte[] objectToBytes(Object inObject) throws IOException, DataAccessException; protected abstract Object bytesToObject(byte[] bytes) throws IOException, DataAccessException; ... }
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 the previous "automatic output record generation" section.
The operation object approach uses records in the same manner as the CciTemplate class.
Table 20.2. Usage of Interaction execute methods
MappingRecordOperation method signature | MappingRecordOperarion 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) |
In this section, the usage of the MappingRecordOperation will be shown to access a database with the Blackbox CCI connector.
Note: The original version of this connector is provided by the J2EE SDK (version 1.3), available from Sun.
Firstly, some initializations on the CCI InteractionSpec must be done to specify which SQL request to execute. In this sample, 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 argument. Note that operation object could be set up as shared instance, as it is thread-safe.
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 look as follows in non-managed mode:
<bean id="managedConnectionFactory" class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory"> <property name="connectionURL"> <value>jdbc:hsqldb:hsql://localhost:9001</value> </property> <property name="driverName"><value>org.hsqldb.jdbcDriver</value></property> </bean> <bean id="targetConnectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> <property name="managedConnectionFactory"> <ref local="managedConnectionFactory"/> </property> </bean> <bean id="connectionFactory" class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter"> <property name="targetConnectionFactory"> <ref bean="targetConnectionFactory"/> </property> <property name="connectionSpec"> <bean class="com.sun.connector.cciblackbox.CciConnectionSpec"> <property name="user"><value>sa</value></property> <property name="password"><value/></property> </bean> </property> </bean> <bean id="component" class="MyDaoImpl"> <property name="connectionFactory"><ref local="connectionFactory"/></property> </bean>
In managed mode (that is, in a J2EE environment), the configuration could look as follows:
<bean id="targetConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"><value>eis/blackbox</value></property> </bean> <bean id="connectionFactory" class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter"> <property name="targetConnectionFactory"> <ref bean="targetConnectionFactory"/> </property> <property name="connectionSpec"> <bean class="com.sun.connector.cciblackbox.CciConnectionSpec"> <property name="user"><value>sa</value></property> <property name="password"><value/></property> </bean> </property> </bean> <bean id="component" class="MyDaoImpl"> <property name="connectionFactory"><ref local="connectionFactory"/></property> </bean>
In this section, the usage of the MappingCommAreaOperation will be shown: accessing a CICS with ECI mode with the IBM CICS ECI connector.
Firstly, the CCI InteractionSpec needs to be initialized to specify which CICS program to access and how to interact with it.
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(); } } }
The abstract EciMappingOperation class can then be subclassed to specify mappings between custom objects and Records.
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 look as follows in non-managed mode:
<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory"> <property name="serverName"><value>TXSERIES</value></property> <property name="connectionURL"><value>local:</value></property> <property name="userName"><value>CICSUSER</value></property> <property name="password"><value>CICS</value></property> </bean> <bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean"> <property name="managedConnectionFactory"> <ref local="managedConnectionFactory"/> </property> </bean> <bean id="component" class="MyDaoImpl"> <property name="connectionFactory"><ref local="connectionFactory"/></property> </bean>
In managed mode (that is, in a J2EE environment), the configuration could look as follows:
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"><value>eis/cicseci</value></property> </bean> <bean id="component" class="MyDaoImpl"> <property name="connectionFactory"><ref local="connectionFactory"/></property> </bean>
JCA specifies several levels of transaction suppot for resource adapters. The kind of transactions that your resource adapter supports is specified in its ra.xml file. There are essentially three options: none (for example with CICS EPI connector), local transactions (for example with CICS ECI connector), global transactions (for example with IMS connector).
<connector> ... <resourceadapter> ... <!-- transaction-support>NoTransaction</transaction-support --> <!-- transaction-support>LocalTransaction</transaction-support --> <transaction-support>XATransaction</transaction-support> ... <resourceadapter> ... <connector>
For global transactions, you can use Spring's generic transaction infrastructure to demarcate transactions, with JtaTransactionManager as backend (delegating to the J2EE server's distributed transaction coordinator underneath).
For local transactions on a single CCI ConnectionFactory, Spring provides a specific transaction management strategy for CCI, analogous to the DataSourceTransactionManager for JDBC. The CCI API defines a local transaction object and corresponding local transaction demarcation methods. Spring's CciLocalTransactionManager executes such local CCI transactions, fully compliant with Spring's generic PlatformTransactionManager abstraction.
<bean id="eciConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>eis/cicseci</value> </property> </bean> <bean id="eciTransactionManager" class="org.springframework.jca.cci.connection.CciLocalTransactionManager"> <property name="connectionFactory"> <ref local="eciConnectionFactory" /> </property> </bean>
Both transaction strategies can be used with any of Spring's transaction demarcation facilities, be it declarative or programmatic. This is a consequence of Spring's generic PlatformTransactionManager abstraction, which decouples transaction demarcation from the actual execution strategy. Simply switch between JtaTransactionManager and CciLocalTransactionManager as needed, keeping your transaction demarcation as-is.
For more information on Spring's transaction facilities, see the transaction management chapter.