Oracle® TimesTen In-Memory Database Java Developer's Guide Release 11.2.1 Part Number E13068-02 |
|
|
View PDF |
You can use the TimesTen JMS/XLA API (JMS/XLA) to monitor TimesTen for changes to specified tables in a local data store and receive real-time notification of these changes. One of the purposes of JMS/XLA is to provide a high-performance, asynchronous alternative to triggers.
You can also use JMS/XLA to build a custom data replication solution, if the TimesTen replication solutions described in Oracle TimesTen In-Memory Database TimesTen to TimesTen Replication Guide do not meet your needs.
JMS/XLA implements Sun Microsystems Java Message Service (JMS) interfaces to make the functionality of the TimesTen Transaction Log API (XLA) available to Java applications. JMS information and resources are available from Sun Microsystems at the following location:
http://java.sun.com/products/jms/docs.html
In addition, the standard JMS API documentation from Sun Microsystems is installed with the Oracle TimesTen In-Memory Database at the following location:
install_dir
/3rdparty/jms1.1/doc/api/index.html
For information about tuning TimesTen JMS/XLA applications for improved performance, see "Tuning JMS/XLA applications".
This chapter includes the following topics:
Java applications can use the JMS/XLA API to receive event notifications from TimesTen. JMS/XLA uses the JMS publish-subscribe interface to provide access to XLA updates.
You subscribe to updates by establishing a JMS Session
that provides a connection to XLA and creating a durable subscriber (TopicSubscriber
). You can receive and process messages synchronously through the subscriber, or you can implement a listener (MessageListener
) to process the updates asynchronously.
JMS/XLA is designed for applications that want to monitor a local data store. TimesTen and the application receiving the notifications must reside on the same system.
Note:
The JMS/XLA API supports persistent-mode XLA. In this mode, XLA obtains update records directly from the transaction log buffer or transaction log files, so the records are available until they are read. Persistent-mode XLA also allows multiple readers to access transaction log updates simultaneously.This section includes the following topics:
As applications modify a TimesTen data store, TimesTen generates transaction log records that describe the changes made to the data and other events such as transaction commits.
New transaction log records are always written to the end of the transaction log buffer as they are generated. Transaction log records are periodically flushed in batches from the log buffer in memory to transaction log files on disk.
Applications can use XLA to monitor the transaction log for changes to the TimesTen data store. XLA reads through the transaction log, filters the log records, and delivers XLA applications with a list of transaction records that contain the changes to the tables and columns of interest.
XLA sorts the records into discrete transactions. If multiple applications are updating the data store simultaneously, transaction log records from the different applications will be interleaved in the transaction log.
XLA transparently extracts all transaction log records associated with a particular transaction and delivers them in a contiguous list to the application.
Only the records for committed transactions are returned. They are returned in the order in which their final commit record appears in the transaction log. XLA filters out records associated with changes to the data store that have not yet committed.
If a change is made but then rolled back, XLA does not deliver the records for the aborted transaction to the application.
Most of these basic XLA concepts are demonstrated in Example 3-1 and summarized in the bulleted list that follows the example.
Consider the example transaction log illustrated in Figure 3-1.
Figure 3-1 Records extracted from the transaction log
Example 3-1 Reading transaction log records
In this example, the transaction log contains the following records:
An XLA application that is set up to detect changes to Tables W, Y, and Z would see the following:
This example demonstrates the following:
Transaction records for application B and application C all appear together.
Though the records for Application C begin to appear in the transaction log before those for Application B, the commit for Application B (BT3) appears in the transaction log before the commit for Application C (CT3). As a result, the records for Application B are returned to the XLA application ahead of those for Application C.
The Application B update to Table X (BT1) is not presented because XLA is not set up to detect changes to Table X.
Application A's updates to Table Z (AT1 and AT2) are never presented because it did not commit and was rolled back (AT3).
You can use XLA to track changes to both tables and materialized views. A materialized view provides a single source from which you can track changes to selected rows and columns in multiple detail tables. Without a materialized view, the XLA application would have to monitor and filter the update records from all of the detail tables, including records reflecting updates to rows and columns of no interest to the application.
In general, there are no operational differences between the XLA mechanisms used to track changes to a table or a materialized view. However, for asynchronous materialized views, be aware that the order of XLA notifications for an asynchronous view is not necessarily the same as it would be for the associated detail tables, or the same as it would be for a synchronous view. For example, if there are two inserts to a detail table, they may be done in the opposite order in the asynchronous materialized view. Furthermore, updates may be treated as a delete followed by an insert, and multiple operations (such as multiple inserts or multiple deletes) may be combined. Applications that depend on ordering should not use asynchronous materialized views.
An XLA bookmark marks the read position of an XLA subscriber application in the transaction log. Bookmarks facilitate durable subscriptions, enabling an application to disconnect from a topic and then reconnect to continue receiving updates where it left off.
When you create a message consumer for XLA, you always use a durable TopicSubscriber
. The subscription identifier you specify when you create the subscriber is used as the XLA bookmark name. When you use the ttXlaSubscribe
and ttXlaUnsubscribe
built-in procedures through JDBC to start and stop XLA publishing for a table, you explicitly specify the name of the bookmark to be used.
Bookmarks are reset to the last read position whenever an acknowledgment is received. For more information about how update messages are acknowledged, see the "XLA acknowledgment modes".
You can remove a durable subscription by calling unsubscribe()
on the JMS Session
object. This deletes the corresponding XLA bookmark and forces a new subscription to be created when you reconnect. For more information see "Deleting bookmarks".
A bookmark subscription cannot be altered when it is in use. To alter a subscription, you must close the message consumer, alter the subscription using ttXlaSubscribe
and ttXlaUnsubscribe
, and open the message consumer.
Note:
You can also use thettXlaBookmarkCreate
TimesTen built-in function to create bookmarks. See "ttXlaBookmarkCreate" in Oracle TimesTen In-Memory Database Reference for information about that function.If you are using an active standby pair replication scheme, you have the option of using replicated bookmarks, according to the replicatedBookmark
attribute of the <topic>
element in the jmsxla.xml
file as discussed in "JMS/XLA configuration file and topics". For a replicated bookmark, operations on the bookmark are replicated to the standby database as appropriate, assuming there is suitable write privilege for the standby. This allows more efficient recovery of your bookmark positions in the event of failover.
You can only read and acknowledge a replicated bookmark in the active database. Each time you acknowledge a replicated bookmark, the acknowledge operation is asynchronously replicated to the standby database.
Note:
Alternatively, if you usettXlaBookmarkCreate
to create a bookmark, that function has a bit you can set to specify a replicated bookmark.Be aware of the following usage notes:
The position of the bookmark in the standby database will be very close to that of the bookmark in the active database; however, because the replication of acknowledge operations is asynchronous, you may see a small window of duplicate updates in the event of a failover, depending on how often acknowledge operations are performed.
If replicated bookmarks already exist at the time you enable the active standby pair scheme, the bookmarks will automatically be added to the replication scheme.
It is permissible to drop the active standby pair scheme while replicated bookmarks exist. The bookmarks will cease to be replicated at that point.
You cannot delete replicated bookmarks as long as the replication agent is running.
To connect to XLA, you establish a connection to a JMS Topic
that corresponds to a particular data store. The JMS/XLA configuration file provides the mapping between topic names and data stores.
You can specify a replicated bookmark by setting replicatedBookmark="yes"
in the <topic>
element when you specify the topic. The default setting is "no"
. Also see "XLA bookmarks".
By default, JMS/XLA looks for a configuration file called jmsxla.xml
in the current working directory. If you want to use another name or location for the file, you must specify it as part of the environment variable in the InitialContext
class and add the location to CLASSPATH.
Example 3-2 Specifying the JMS/XLA configuration file
The following code specifies the configuration file as part of the environment variable in the InitialContext
class, use code similar to Example 3-2.
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.timesten.dataserver.jmsxla.SimpleInitialContextFactory"); env.put(XlaConstants.CONFIG_FILE_NAME, "/newlocation.xml"); InitialContext ic = new InitialContext(env);
The JMS/XLA API uses the class loader to locate the JMS/XLA configuration file if XlaConstants.CONFIG_FILE_NAME
is set. In Example 3-2, the JMS/XLA API searches for the newlocation.xml
file in the top directory in both the location specified in the CLASSPATH environment variable and in the JAR files specified in the CLASSPATH environment variable.
The JMS/XLA configuration file can also be located in subdirectories, as follows:
env.put(XlaConstants.CONFIG_FILE_NAME, "/com/mycompany/myapplication/deepinside.xml");
In this case, the JMS/XLA API searches for the deepinside.xml
file in the com/mycompany/myapplication
subdirectory in both the location specified in the CLASSPATH environment variable and in the JAR files specified in the CLASSPATH environment variable.
The JMS/XLA API uses the first configuration file that it finds.
Example 3-3 Defining a topic in the configuration file
A topic definition in the configuration file consists of a name, a JDBC connection string, and a prefetch value that specifies how many updates to retrieve at a time.
For example, the configuration file shown here maps the DemoDataStore
topic to the TestDB
DSN:
<xlaconfig> <topics> <topic name="DemoDataStore" connectionString="jdbc:timesten:direct:DSN=TestDB" xlaPrefetch="100" /> </topics> </xlaconfig>
Example 3-4 Defining a topic to use replicated bookmarks
A topic definition can also specify whether a replicated bookmark should be used. The following repeats the preceding example, but with a replicated bookmark.
<xlaconfig> <topics> <topic name="DemoDataStore" connectionString="jdbc:timesten:direct:DSN=TestDB" xlaPrefetch="100" replicatedBookmark="yes" /> </topics> </xlaconfig>
Applications receive XLA updates as JMS MapMessage
objects. The MapMessage
contains a set of typed name/value pairs that correspond to the fields in an XLA update header.
You can access the message fields using the MapMessage
getter methods. The getMapNames()
method returns an Enumeration
object that contains the names of all of the fields in the message. You can retrieve individual fields from the message by name. All reserved field names begin with two underscores, for example __TYPE
.
All update messages have a __TYPE
field that indicates what type of update the message contains. The types are specified as integer values. As a convenience, you can use the constants defined in com.timesten.dataserver.jmsxla.XlaConstants
to compare against the integer types. The supported types are described in Table 3-1.
Table 3-1 XLA update types
Update type | Description |
---|---|
INSERT |
A row has been added. |
UPDATE |
A row has been modified. |
DELETE |
A row has been removed. |
COMMIT_ONLY |
A transaction has been committed. |
CREATE_TABLE |
A table has been created. |
DROP_TABLE |
A table has been dropped. |
CREATE_INDEX |
An index has been created. |
DROP_INDEX |
An index has been dropped. |
ADD_COLUMNS |
New columns have been added to the table. |
DROP_COLUMNS |
Columns have been removed from the table. |
CREATE_VIEW |
A materialized view has been created. |
DROP_VIEW |
A materialized view has been dropped. |
CREATE_SEQ |
A SEQUENCE has been created. |
DROP_SEQ |
A SEQUENCE has been dropped. |
TRUNCATE |
The table has been truncated and all rows in the table have been deleted. |
For more information about the contents of an XLA update message, see "JMS/XLA MapMessage contents".
The XLA acknowledgment mechanism is designed to ensure that an application has not only received a message, but has successfully processed it. Acknowledging an update permanently resets the application's XLA bookmark to the last record that was read. This prevents previously returned records from being reread, ensuring that an application does not receive previously acknowledged records if the bookmark is reused when an application reconnects to XLA.
JMS/XLA can automatically acknowledge XLA update messages, or applications can choose to acknowledge messages explicitly. You specify how updates are to be acknowledged when you create the Session
object.
JMS/XLA supports three acknowledgment modes:
AUTO_ACKNOWLEDGE: In this mode, updates are automatically acknowledged as you receive them. Each message is delivered only once. Duplicate messages will not be sent and in the event of an application failure, messages might be lost. In AUTO_ACKNOWLEDGE mode, messages are always delivered and acknowledged individually, so JMS/XLA does not prefetch multiple records. The xlaprefetch
attribute in the topic is ignored.
DUPS_OK_ACKNOWLEDGE: In this mode, updates are automatically acknowledged, but duplicate messages might be delivered in the event of an application failure. In DUPS_OK_ACKNOWLEDGE mode, JMS/XLA prefetches records according to the xlaprefetch
attribute specified for the topic and sends an acknowledgment when the last record in a prefetched block is read. If the application fails before reading all of the prefetched records, all of the records in the block are presented to the application it restarts.
CLIENT_ACKNOWLEDGE: In this mode, applications are responsible for acknowledging receipt of update messages by calling acknowledge()
on the MapMessage
. In CLIENT_ACKNOWLEDGE mode, JMS/XLA prefetches records according to the xlaprefetch
attribute specified for the topic.
Prefetching multiple update records at a time is more efficient than obtaining each update record from XLA individually. Because updates are not prefetched when you use AUTO_ACKNOWLEDGE mode, it can be slower than the other modes. If possible, you should design the application to tolerate duplicate updates so you can use DUPS_OK_ACKNOWLEDGE, or explicitly acknowledge updates. Explicitly acknowledging updates usually yields the best performance, as long as you can avoid acknowledging each message individually.
To explicitly acknowledge an XLA update, you call acknowledge()
on the update message. Acknowledging a message implicitly acknowledges all previous messages. Typically, you receive and process multiple update messages between acknowledgments. If you are using the CLIENT_ACKNOWLEDGE mode and intend to reuse a durable subscription in the future, you should call acknowledge()
to reset the bookmark to the last-read position before exiting.
"Considering TimesTen features for access control" provides a brief overview of how TimesTen access control affects operations in the database. Access control includes impact on XLA, as follows:
Any XLA functionality requires the system privilege XLA. This includes connecting to TimesTen as an XLA reader and executing the TimesTen XLA built-in functions ttXlaBookmarkCreate
, ttXlaBookmarkDelete
, ttXlaSubsribe
, and ttXlaUnsubscribe
, all of which are documented in "Built-In Procedures" in Oracle TimesTen In-Memory Database Reference.
A user with the XLA privilege has capabilities equivalent to the SELECT ANY TABLE and SELECT ANY SEQUENCE system privileges.
The JMS/XLA API uses orai18n.jar
, part of the Oracle Globalization Development Kit (GDK) for translating from the database character set specified by the DatabaseCharacterSet
attribute to UTF-16 encoding. JMS/XLA API supports a specific version of the GDK with each TimesTen release. If JMS/XLA finds other versions of the GDK already loaded in the JVM, it displays a severe warning and continues processing. You can find out the GDK version supported by JMS/XLA by entering the following commands:
$ cd install_dir/lib
$ java -cp ./orai18n.jar oracle.i18n.util.GDKOracleMetaData -version
To connect to XLA so you can receive updates, use the JMS ConnectionFactory
to create a Connection
. Then use the Connection
to establish a Session
. When you are ready to start processing updates, you call start
on the Connection
to enable message dispatching. This is shown in Example 3-5.
Example 3-5 Connecting to XLA
ConnectionFactory connectionFactory; Context messaging = new InitialContext(); connectionFactory = (ConnectionFactory) messaging.lookup("ConnectionFactory"); Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); connection.start();
Before you can start receiving updates, you must specify to XLA which tables you want to monitor for changes.
To subscribe to changes and turn on XLA publishing for a table, call the ttXlaSubscribe
built-in procedure through JDBC.
When you use ttXlaSubscribe
to enable XLA publishing for a table, you must specify two parameters—the name of the table and the name of the bookmark that will be used to track the table:
ttXlaSubscribe(user.table, mybookmark)
For example, call ttXlaSubscribe
by the JDBC CallableStatement
interface:
Connection con; CallableStatement cStmt; ... cStmt = con.prepareCall("{call ttXlaSubscribe(user.table, mybookmark)}"); cStmt.execute();
Use ttXlaUnsubscribe
to unsubscribe from the table during shutdown. For more information, see "Unsubscribing from a table".
The application can verify table subscriptions by checking the SYS.XLASUBSCRIPTIONS system table.
For more information about using TimesTen built-in procedures in a Java application, see "Calling TimesTen built-in procedures".
You can receive XLA updates either synchronously or asynchronously.
To receive and process update for a topic synchronously, perform the following tasks:
Create a durable TopicSubscriber
instance to subscribe to a topic.
Call receive()
or receiveNoWait()
on your subscriber to get the next available update.
Process the returned MapMessage
instance.
To receive and process updates for a topic asynchronously, perform the following tasks.
Create a MessageListener
instance to process the updates.
Create a durable TopicSubscriber
instance to subscribe to a topic.
Register the MessageListener
with the TopicSubscriber
.
Start the connection.
Note:
You must register theMessageListener
before you start the connection. Otherwise, you can miss messages. If the connection is already started, stop the connection, register the MessageListener
, then start the connection.Wait for messages to arrive. You can call the Object
method wait()
to wait for messages if your application does not have to do anything else in its main thread.
When an update is published, the MessageListener
method onMessage()
is called and the message is passed in as a MapMessage
instance.
The application can verify table subscriptions by checking the SYS.XLASUBSCRIPTIONS system table.
Example 3-6 uses a listener to process updates asynchronously.
Example 3-6 Using a listener to process updates asynchronously
MyListener myListener = new MyListener(outStream); outStream.println("Creating consumer for topic " + topic); Topic xlaTopic = session.createTopic(topic); TopicSubscriber subscriber = session.createDurableSubscriber(xlaTopic, "mybookmark"); subscriber.setMessageListener(myListener);
Note that mybookmark
must already exist. You can use JDBC and the ttXlaBookmarkCreate
built-in procedure to create a bookmark. Also, the TopicSubscriber
must be a durable subscriber. XLA connections are designed to be durable. XLA bookmarks make it possible to disconnect from a topic and then reconnect to start receiving updates where you left off. The String
you pass in as the subscription identifier when you create a durable subscriber is used as the XLA bookmark name.
You can call unsubscribe()
on the JMS TopicSession
to delete the XLA bookmark used by the subscriber when the application shuts down. This causes a new bookmark to be created when the application is restarted.
When you receive an update, you can use the MapMessage
getter methods to extract information from the message and then perform whatever processing your application requires. The TimesTen XlaConstants
class defines constants for the update types and special message fields to make it easier to process XLA update messages.
The first step is typically to determine what type of update the message contains. You can use the MapMessage
method getInt()
to get the contents of the __TYPE
field, and compare the value against the numeric constants defined in the XlaConstants
class.
In Example 3-7, the method onMessage()
extracts the update type from the MapMessage
object and displays the action that the update signifies.
Example 3-7 Determining the update type
public void onMessage(Message message) { MapMessage mapMessage = (MapMessage) message; String messageType = null; if (message == null) { errStream.println("MyListener: update message is null"); return; } try { // Get the update type(insert, update, delete, etc.). int type = mapMessage.getInt(XlaConstants.TYPE_FIELD); if (type == XlaConstants.INSERT) { System.out.println("A row was inserted."); } else if (type == XlaConstants.UPDATE) { System.out.println("A row was updated."); } else if (type == XlaConstants.DELETE) { System.out.println("A row was deleted."); } else { return; }
When you know what type of message you have received, you can process the message according to the application's needs. To get a list of all of the fields in a message, you can call the MapMessage
method getMapNames()
. You can retrieve individual fields from the message by name.
Example 3-8 extracts the column values from insert, update, and delete messages using the column names.
Example 3-8 Extracting column values
if (type == XlaConstants.INSERT || type == XlaConstants.UPDATE || type == XlaConstants.DELETE) { // Get the column values from the message. int cust_num = mapMessage.getInt("cust_num"); String region = mapMessage.getString("region"); String name = mapMessage.getString("name"); String address = mapMessage.getString("address"); System.out.println("New Column Values:"); System.out.println("cust_num=" + cust_num); System.out.println("region=" + region); System.out.println("name=" + name); System.out.println("address=" + address); }
For detailed information about the contents of XLA update messages, see "JMS/XLA MapMessage contents". For information about how TimesTen column types map to JMS data types and the getter methods used to retrieve the column values, see "Data type support".
When the XLA application has finished reading from the transaction log, it should gracefully exit by closing the XLA connection, deleting any unneeded bookmarks, and unsubscribing from any tables to which you explicitly subscribed.
To close the connection to XLA, call close()
on the Connection
object.
After a connection has been closed, any attempt to use it, its sessions, or its subscribers will throw an IllegalStateException
. You can continue to use messages received through the connection, but you cannot call a received message's acknowledge()
method after the connection is closed.
Deleting XLA bookmarks during shutdown is optional. Deleting a bookmark enables the disk space associated with any unread update records in the transaction log to be freed.
If you do not delete the bookmark, it can be reused by a durable subscriber. As long as the bookmark is available when a durable subscriber reconnects, the subscriber will receive all unacknowledged updates published since the previous connection was terminated. Keep in mind that as long as a bookmark exists with no application reading from it, the transaction log will continue to grow and the amount of disk space consumed by your database will increase.
To delete a bookmark, you can simply call unsubscribe
on the JMS Session, which invokes the ttXlaBookmarkDelete
built-in procedure to remove the XLA bookmark.
Note:
You cannot delete replicated bookmarks while the replication agent is running.To turn off XLA publishing for a table, use the ttXlaUnsubscribe
built-in procedure. If you use ttXlaSubscribe
to enable XLA publishing for a table, you should use ttXlaUnsubscribe
to unsubscribe from the table when shutting down your application.
When you unsubscribe from a table, specify the name of the table and the name of the bookmark used to track the table:
ttXlaUnsubscribe(user.table, mybookmark)
The following example calls ttXlaUnSubscribe
through a CallableStatement
object.
Example 3-9 Unsubscribing from a table
Connection con; CallableStatement cStmt; ... cStmt = con.prepareCall("{call ttXlaUnSubscribe(user.table, mybookmark)}"); cStmt.execute();
For more information about using TimesTen built-in procedures in a Java application, see "Calling TimesTen built-in procedures".
If the TimesTen replication solutions described in Oracle TimesTen In-Memory Database TimesTen to TimesTen Replication Guide do not meet your needs, you can use JMS/XLA to replicate updates from a source data store to a target data store.
The source data store generates JMS/XLA messages. To apply the messages to a target data store, you must extract the XLA descriptor from them. Use the MapMessage
interface to extract the update descriptor:
MapMessage message; /** *...other code */ try { byte[]updateMessage= mapMessage.getBytes(XlaConstants.UPDATE_DESCRIPTOR_FIELD); } catch (JMSException jex){ /** *...other code */ }
The target data store may reside on a different system from the source data store. The update descriptor is returned as a byte array and can be serialized for network transmission.
You must create a target data store object that represents the target data store so you can apply the objects from the source data store. You can create a target data store object called myTargetDataStore
as an instance of the TargetDataStoreImpl
class. For example:
TargetDataStore myTargetDataStore= new TargetDataStoreImpl("DSN=sampleDSN");
Apply messages to myTargetDataStore
by using the TargetDataStore
method apply()
. For example:
myTargetDataStore.apply(updateDescriptor);
By default, TimesTen checks for conflicts on the target data store before applying the update. If the target data store has information that is later than the update, TargetDataStore
throws an exception. If you do not want TimesTen to check for conflicts, use the TargetDataStore
method setUpdateConflictCheckFlag()
to change the behavior.
By default, TimesTen commits the update to the data store based on commit flags and transaction boundaries contained in the update descriptor. If you want the application to perform manual commits instead, use the setAutoCommitFlag()
method to change the autocommit flag. To perform a manual commit on myTargetDataStore
, use the following command:
myTargetDataStore.commit();
You can perform a rollback if errors occur during the application of the update. Use the following command for myTargetDataStore
:
myTargetDataStore.rollback();
Close myTargetDataStore
by using the following command:
myTargetDataStore.close;
See "JMS/XLA replication API" for more information about the TargetDataStore
interface.
Invoking TargetDataStore
can yield transient and permanent errors.
TargetDataStore
methods return a nonzero value when transient errors occur. The application can retry the operation and is responsible for monitoring update descriptors that must be reapplied. For more information about transient XLA errors, see "Handling XLA errors" in Oracle TimesTen In-Memory Database C Developer's Guide.
TargetDataStore
methods return a JMSException
object for permanent errors. If the application receives a permanent error, it should verify that the data store is valid. If the data store is invalid, the target data store object should be closed and a new one should be created. Other types of permanent errors may require manual intervention.
The following example shows how to recover errors from a TargetDataStore
object.
Example 3-10 Recovering errors
TargetDataStore theTargetDataStore; byte[] updateDescriptor; int rc; // Other code try { ... if ( (rc = theTargetDataStore.apply(updateDescriptor) ) == 0 ) { // apply successful } else { // Transient error. Retry later. } } catch (JMSException jex) { if (theTargetDataStore.isDataStoreValid() ) { // Data store is valid; permanent error that may need Administrator intervention. } else { try { theTargetDataStore.close(); } catch (JMSException closeEx) { // Close errors are not usual. This may need Administrator intervention. } }