Sample Resource AdapterSection overview In this section we'll look at a simple implementation of a JCA resource adapter. You'll find the sample resource adapter in the file helloworldra.rar. The Java classes are in the helloworldra.jar file within this RAR file. Source for all of the classes is also provided in helloworldra.jar. As you can probably guess from its name, this resource adapter implements the ubiquitous "Hello World" functionality. As such, it doesn't actually connect to an enterprise system, and its functionality is limited to returning just the one String with the much-loved message. Although the resource adapter does not implement the transaction or security contracts, it does implement the CCI; it uses an Interaction, an InteractionSpec, a RecordFactory, and IndexedRecords. You'll find the most important and interesting parts of the sample resource adapter extracted and explained in the discussion that follows. In addition to classes and interfaces, we'll discuss the source code for the deployment descriptor. From this simple implementation, you will be able to get some feel for the classes you will need to write and the relationships between them. In addition to studying the following two sections, you should spend some time reviewing the complete source code, which you will find in Resources on page 28 . HelloWorldConnectionFactoryImpl class The HelloWorldConnectionFactoryImpl class implements the CCI ConnectionFactory interface and provides the connection factory used by application components to create connections to the EIS. Following is the code to create a HelloWorldConnectionFactoryImpl instance, as well as the client methods that return connections, the record factory, and metadata. Creating a connection factory instance The HelloWorldConnectionFactoryImpl class's constructor requires that the ConnectionManager and ManagedConnectionFactory be passed in for use in the getConnection() method, as shown below:
...
public HelloWorldConnectionFactoryImpl( ManagedConnectionFactory mcf, ConnectionManager cm) { super(); this.mcf = mcf; this.cm = cm; } ... Getting a connection Neither ConnectionRequestInfo nor ConnectionSpec are supported by the HelloWorldConnectionFactoryImpl class, so both getConnection() methods do the same thing: they call the allocateConnection() method of the ConnectionManager, passing in the ManagedConnectionFactory and null for the ConnectionRequestInfo, as shown below:
...
public Connection getConnection() throws ResourceException { return (Connection) cm.allocateConnection(mcf, null); } public Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException return getConnection(); } ... Record factory and metadata methods The getRecordFactory() and getMetaData() methods simply return the implementation classes for the respective interfaces, as shown below:
...
public RecordFactory getRecordFactory() throws ResourceException { return new HelloWorldRecordFactoryImpl(); } public ResourceAdapterMetaData getMetaData() throws ResourceException { return new HelloWorldResourceAdapterMetaDataImpl(); } ... HelloWorldConnectionImpl class The HelloWorldConnectionImpl class implements the CCI Connection interface, and provides the connection handle for application components to access the EIS. Following are the methods to create, invalidate, and close a connection, the methods to provide clients with an Interaction and with metadata, and the code for signalling that the class does not support local transactions and result sets. Creating a connection The HelloWorldConnectionImpl class's constructor requires that the ManagedConnection be passed in for use in the close() method. A flag is set during instantiation to indicate that this is a valid connection, that is, not closed. In other methods, where appropriate, this flag is checked to determine if the requested action can be carried out.
...
public HelloWorldConnectionImpl(ManagedConnection mc) { super(); this.mc = mc; valid = true; } ... Invalidating a connection The invalidate() method sets the ManagedConnection reference to null and sets the flag indicating that the connection is no longer valid.
...
void invalidate() { mc = null; valid = false; } ... Closing a connection The close() method delegates its invocation to the ManagedConnection, as required.
...
public void close() throws ResourceException { if (valid) { ((HelloWorldManagedConnectionImpl) mc).close(); } } ... Interaction and metadata methods The createInteraction() and getMetaData() methods simply return the implementation classes for the respective interfaces.
...
public Interaction createInteraction() throws ResourceException { if (valid) { return new HelloWorldInteractionImpl(this); } else { throw new ResourceException(CLOSED_ERROR); } } public ConnectionMetaData getMetaData() throws ResourceException { if (valid) { return new HelloWorldConnectionMetaDataImpl(); } else { throw new ResourceException(CLOSED_ERROR); } } ... Throwing a NotSupportedException Because neither local transactions nor results sets are supported by this resource adapter, getLocalTransaction() and getResultSetInfo() throw
...
public LocalTransaction getLocalTransaction() throws ResourceException { throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED); } public ResultSetInfo getResultSetInfo() throws ResourceException { throw new NotSupportedException(RESULT_SETS_NOT_SUPPORTED); } ... HelloWorldManagedConnectionFactoryImpl class The HelloWorldManagedConnectionFactoryImpl class implements the ManagedConnectionFactory interface. In the code below, you'll see how a connection factory is created, how a managed connection is created, and how the class responds to a request to match managed connections. Creating a connection factory and a managed connection The createConnectionFactory() and createManagedConnection() methods simply return the implementation classes for the respective interfaces. Because the security contract is not implemented by this resource adapter, the createManagedConnection() method does not use the Subject or ConnectionRequestInfo parameters when creating the ManagedConnnection.
...
public Object createConnectionFactory(ConnectionManager cm) throws ResourceException { return new HelloWorldConnectionFactoryImpl(this, cm); } public ManagedConnection createManagedConnection( Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException { return new HelloWorldManagedConnectionImpl(); } ... Matching managed connections The simplicity of this resource adapter implementation makes each ManagedConnection indistinguishable from another. So, the matchManagedConnections() method simply returns the first ManagedConnection in the input Set, as shown below:
...
public ManagedConnection matchManagedConnections( Set connectionSet, Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException { ManagedConnection match = null; Iterator iterator = connectionSet.iterator(); if (iterator.hasNext()) { match = (ManagedConnection) iterator.next(); } return match; } ... HelloWorldManagedConnectionImpl class The HelloWorldManagedConnectionImpl class implements the ManagedConnection interface. In the code below, you'll see how the getConnection(), close(), cleanup(), and destroy() methods work together to create, close, and clean up a connection, as well as the methods that signal that this resource adapter does not support transactions. Creating a connection The getConnection() method first invalidates an existing connection handle, if one exists, then creates a new connection handle, storing it in an instance variable. It then returns a reference to that instance variable. This maintains a one-to-one relationship between the connection handle and the ManagedConnection. A resource adapter may have multiple connection handles associated with a ManagedConnection, but is not required to do so.
...
public Object getConnection( Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException { if (connection != null) { connection.invalidate(); } connection = new HelloWorldConnectionImpl(this); return connection; } ... Closing a connection The close() method notifies its ConnectionEventListeners that the close has occurred, then invalidates the connection handle.
...
public void close() { Enumeration list = listeners.elements(); ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED); while (list.hasMoreElements()) { ((ConnectionEventListener) list.nextElement()).connectionClosed(event); } connection.invalidate(); } ... Cleaning up and destroying a connection The cleanup() and destroy() methods both invalidate the connection handle. The destroy() method also sets the ManagedConnection's instance variables to null, in preparation for being removed from the application server's connection pool.
...
public void cleanup() throws ResourceException { connection.invalidate(); } public void destroy() throws ResourceException { connection.invalidate(); connection = null; listeners = null; } ... Throwing a NotSupportedException Because transactions are not supported by this resource adapter, the getXAResource() and getLocalTransaction() methods both throw NotSupportedException.
...
public XAResource getXAResource() throws ResourceException { throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR); } public LocalTransaction getLocalTransaction() throws ResourceException { throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR); } ... HelloWorldInteractionImpl class The HelloWorldInteractionImpl class implements the CCI Interaction interface. On this panel, you'll see how some of the more important elements of this class work together to create, execute, and close an Interaction. Creating an Interaction Because JCA requires that an Interaction maintain its association with the connection handle used to create it, the constructor requires that a Connection be passed in. A flag is set during instantiation to indicate that this is a valid Interaction, that is, not closed. In other methods, where appropriate, this flag is checked to determine if the requested action can be carried out.
...
public HelloWorldInteractionImpl(Connection connection) { super(); this.connection = connection; valid = true; } ... Executing an Interaction The HelloWorldInteractionImpl class only supports the execute() variant that takes an InteractionSpec, an input record, and an output record (the other execute() throws NotSupportedException). The method ensures that:
If all conditions are correct, the "Hello World!" message is placed in the output record. If a condition is not correct, then an exception is thrown.
...
public boolean execute(InteractionSpec ispec, Record input, Record output) throws ResourceException { if (valid) { if (((HelloWorldInteractionSpecImpl) ispec) .getFunctionName() .equals(HelloWorldInteractionSpec.SAY_HELLO_FUNCTION)) { if (input.getRecordName().equals(HelloWorldIndexedRecord.INPUT)) { if (output.getRecordName().equals(HelloWorldIndexedRecord.OUTPUT)) { ((HelloWorldIndexedRecord) output).clear(); ((HelloWorldIndexedRecord) output).add( OUTPUT_RECORD_FIELD_01); } else { throw new ResourceException(INVALID_OUTPUT_ERROR); } } else { throw new ResourceException(INVALID_INPUT_ERROR); } } else { throw new ResourceException(INVALID_FUNCTION_ERROR); } } else { throw new ResourceException(CLOSED_ERROR); } return true; } ... Closing an Interaction The close() method clears the connection handle and marks the Interaction as invalid.
...
public void close() throws ResourceException { connection = null; valid = false; } ... HelloWorldInteractionSpec class To hide as much implementation detail as possible from application components using this resource adapter, the HelloWorldInteractionSpec interface extends InteractionSpec. The InteractionSpec interface lets us provide all the information application components must have. In the next panel, we'll look at the implementation class for this interface.
...
public interface HelloWorldInteractionSpec extends InteractionSpec { public static final String SAY_HELLO_FUNCTION = "sayHello"; public String getFunctionName(); public void setFunctionName(String functionName); } HelloWorldInteractionSpecImpl class The HelloWorldInteractionSpecImpl class implements the HelloWorldInteractionSpec interface. It has one property, FunctionName, with a getter and setter to access the property. The JCA specification says that the properties in implementations of InteractionSpec must be bound or constrained. This implementation provides the property as bound.
...
public String getFunctionName() { return functionName; } public void setFunctionName(String functionName) { String oldFunctionName = functionName; this.functionName = functionName; firePropertyChange("FunctionName", oldFunctionName, functionName); } ... HelloWorldIndexedRecord class Like HelloWorldInteractionSpec, the HelloWorldIndexedRecord interface is used to hide implementation details from application components.
...
public interface HelloWorldIndexedRecord extends IndexedRecord { public static final String INPUT = "input"; public static final String OUTPUT = "output"; public static final int MESSAGE_FIELD = 0; } HelloWorldIndexedRecordImpl class The HelloWorldIndexedRecordImpl class implements the HelloWorldIndexedRecord interface. It has two properties, Name and ShortDescription, with getters and setters to access the properties. Because this class must also implement the List interface, it maintains an ArrayList as an instance variable, and implements all List methods by calling the corresponding method on the ArrayList.
...
public class HelloWorldIndexedRecordImpl implements HelloWorldIndexedRecord { private ArrayList list = new ArrayList(); private String name; private String description; ... HelloWorldRecordFactoryImpl class The HelloWorldRecordFactoryImpl class implements the RecordFactory interface. It does not support creating MappedRecords. The createIndexedRecord() method ensures that the requested record name is valid, then creates the record and returns it. If the record name is not valid, an exception is thrown.
...
public IndexedRecord createIndexedRecord(String recordName) throws ResourceException { HelloWorldIndexedRecordImpl record = null; if ((recordName.equals(HelloWorldIndexedRecord.INPUT)) || (recordName.equals(HelloWorldIndexedRecord.OUTPUT))) { record = new HelloWorldIndexedRecordImpl(); record.setRecordName(recordName); } if (record == null) { throw new ResourceException(INVALID_RECORD_NAME); } else { return record; } } ... The deployment descriptor The deployment descriptor provides the fully qualified names for the following JCA components:
< !DOCTYPE connector PUBLIC '-//Sun Microsystems, Inc.//
DTD Connector 1.0//EN' 'http://java.sun.com/dtd/connector_1_0.dtd'> <connector> <display-name>Hello World Sample</display-name> <vendor-name>Willy Farrell</vendor-name> <spec-version>1.0</spec-version> <eis-type>Hello World</eis-type> <version>1.0</version> <resourceadapter> <managedconnectionfactory-class> com.ibm.ssya.helloworldra.HelloWorldManagedConnectionFactoryImpl </managedconnectionfactory-class> <connectionfactory-interface> javax.resource.cci.ConnectionFactory </connectionfactory-interface> <connectionfactory-impl-class> com.ibm.ssya.helloworldra.HelloWorldConnectionFactoryImpl </connectionfactory-impl-class> <connection-interface> javax.resource.cci.Connection </connection-interface> <connection-impl-class> com.ibm.ssya.helloworldra.HelloWorldConnectionImpl </connection-impl-class> <transaction-support> NoTransaction </transaction-support> <reauthentication-support> false </reauthentication-support> </resourceadapter> </connector> |