Anda di halaman 1dari 47

Atomikos TransactionsJTA Guide

Author: Dr. Guy Pardon (guy@atomikos.com).


Edition 1.0, September 2002.
Copyright © 2002, Atomikos. All rights reserved.
http://www.atomikos.com

Contents
1. Preface
2. System Requirements
3. JTA Overview
4. Configuring TransactionsJTA
5. Programming with TransactionsJTA
6. Appendix A: Answers
7. Appendix B: Complete Examples
8. Appendix C: Getting More out of TransactionsJTA
9. Appendix D: Troubleshooting
10. References

1. Preface
This user guide explains how to use Sun's Java Transaction API (JTA) version 1.0.1 and the Atomikos TransactionsJTA
embedded transaction manager. It is not meant as a general discussion of JTA. However, an overview of JTA is included,
and wherever appropriate there are questions at the end of each chapter that allow you to test your understanding. You are
encouraged to actively try to answer these questions, since they will allow you to get more out if this manual. For more
information on JTA, you are referred to the Sun site (http://java.sun.com), where detailed JTA specifications can be
downloaded for free. Although examples based on JDBC and JMS are used to illustrate the concepts, we consider those
two technologies to fall outside the scope of this manual. Again, the Sun website has more information for the interested
reader. If you don't like to read manuals, then you can take a shortcut and go to the examples in the back. These illustrate
the concepts that are explained in the text with source code of example programs.

2. System Requirements
This guide has been written for Atomikos TransactionsJTA release 1.10 or higher. In order to use the AtomikosJTA system
we recommend that you install and run a Java VM of at least version 1.3. Memory requirements are likely to depend more
on your code than on our software because of the compact nature of the kernel; most modern systems should have more
than enough memory. The libraries that come with TransactionsJTA include most of what you need in order to compile
transactional applications: the API definitions for javax.transaction.*, javax.sql.* and javax.jms.* are included so that
you do not need to get those separately.

NOT included in the TransactionsJTA release are vendor-specific JDBC or JMS implementation libraries. For instance, if
you want to use our transaction service to manage transactions that access your Oracle database, then you need to make
sure that you have the Oracle JDBC classes installed in your classpath, in addition to the TransactionsJTA distribution
classes. Likewise, if you want to use IBM MQSeries for JMS then you need to make sure that the MQSeries libraries are in
your classpath.

3. JTA Overview
Unless explicitly mentioned, the discussion in this chapter is limited to pure JTA: the content of this chapter should apply
for any JTA implementation, not just Atomikos'. Atomikos-specific information is provided in the later chapters of this
manual.

This chapter is a generic JTA overview: it quickly reviews the most important things about JTA that you need to know in
order to use TransactionsJTA. The organization is as follows:

Transactions
What is JTA?
Two-Phase Commit
JTA Components
JTA Interactions
Questions

Transactions
A transaction is a logical unit of work which effects can either be made permanent in their entirety (committed) or
cancelled in their entirety (rolled back). Before a transaction is committed or rolled back, it is active. Active transactions'
effects are typically invisible to other, concurrent transactions. Consequently, only committed transactions' effects are
visible (can you see why?).!

Example

Imagine that you want to publish a customer-related order message through the Java Message Service (JMS) and at the
same time mark the customer's order data in the database as being processed. The message should not be sent unless the
database can be updated and vice versa.!

The concept of transactions requires system-level software support to make these properties hold. A piece of software that
takes care of this is called a transaction manager or transaction service.

Nested Transactions

The nested transaction model is a variant of the normal ('flat') transactional model.
Nested transactions differ in that a subtransaction can be created within
an existing transaction (which becomes the parent transaction). The subtransaction is again a transaction that can be
committed or rolled back. The major differences with normal transactions are in visibility and termination:

Visibility: an active (sub)transaction's effects are visible to its subtransactions (if any). This means that there is
sharing of updates from parent transaction to subtransaction.
Termination: a rolled-back subtransaction does not affect its parent transaction. On the other hand, a committed
subtransaction's effects become part of the parent transaction, and become permanent only after the top-level
transaction (the one without a parent) commits.

What is JTA?
JTA is short for Sun Microsystems' Java Transaction API and is Sun's (low-level) API for creating transactions in Java
and making your data access operations part of those transactions.

The JTA defines how your application can request transactional functionality on the Java platform. JTA is not a product
in itself, but rather a set of Java interfaces. A vendor-specific JTA implementation referred to as a transaction manager
or transaction service (such as TransactionsJTA) is needed to actually use the functionality defined in these interfaces. In
other words, you can program JTA transactions in your application, but you need the implementation classes of a JTA-
compliant transaction manager vendor in order to run your application.

JTA is a standard part of the Java Enterprise (J2EE) platform and every Enterprise JavaBeans (EJB) application server
should also include a JTA implementation. The JTA is said to be low-level because EJB programmers typically don't
access the JTA API directly or explicitly. Rather, the EJB application server makes the appropriate calls behind the scenes.

So given that an EJB server will also give you JTA functionality, why should you consider using Atomikos'
TransactionsJTA? Here are just a few reasons:

EJB servers are often very expensive, and an overkill for many solutions that only need a fraction of the J2EE APIs.
TransactionsJTA provides transactions at a fraction of the cost of a full EJB server.
TransactionsJTA offers more features than defined in the JTA specification.!
TransactionsJTA has better and more functionality than most competitors offer.
TransactionsJTA was designed for very high performance (there is no extra overhead for JTA transactions; local
transactions and JTA transactions can be expected to be equally fast).
With TransactionsJTA you can bring JTA functionality on the J2SE (Java 2 Standard Edition) platform.

Two-Phase Commit
In the previous section we have referred to a JTA implementation as a transaction manager or transaction service. A
transaction is a logical unit of work that either happens completely (in all databases or queues that were accessed by it) or
not at all. The transaction manager is the software module that is responsible for ensuring this property. It does this by
executing a two-phase commit termination protocol that addresses all of the resources that a transaction has used. This
two-phase commit happens behind the scenes of your application: you typically don't notice it.

Let us briefly describe two-phase commit with the previous example still in mind. Two-phase commit works in two
phases: a voting phase and a decision phase.

In the voting (or prepare) phase, the transaction manager will ask both the JMS message queue and the database
whether they can agree with a successful termination or not. Each may return a negative reply, for instance if there
was a time-out which caused the database's work to be rolled back. If one of them replies positively, then it should
make sure it can always make the work permanent (this implies that it can no longer cancel due to an internal time-
out).
After the transaction manager has received all of the replies (also called 'votes') it will make a global decision on the
outcome of the transaction. This decision will depend on the collected replies:
If both replies were positive (meaning that both the JMS and the database can make the work permanent), then
the transaction manager will instruct each to commit.!
If at least one reply is negative (or missing) then a rollback decision is sent to the remaining resource. This
means that the remaining resource cancels (rolls back) the work done for the transaction.

There are two things to notice:

Each resource must have the capability to understand two-phase commit: it needs to reply to a prepare request from
the transaction manager, and be able to rollback (cancel) the work if the transaction manager decides so.
If a resource votes positively during prepare and is then cut off from the transaction manager (for instance, if the
transaction manager crashes) then it does not know what to do. It can not cancel on its own due to the two-phase
commit protocol rules, so it needs to remember the transaction indefinitely. In addition, this restricts concurrent
access by other transactions. In that case, the resource is said to be in-doubt.

This explains why JTA can not be used to make anything transactional: you can only have transactional properties for
applications that access the proper type of resources (those that understand two-phase commit). The fact that a resource can
remain in-doubt and restrict concurrent access is something that has bothered many vendors. To alleviate this restriction, a
practical variant of the two-phase commit protocol includes so-called heuristic decisions: a resource that remains in-doubt
for too long may decide to unilaterally rollback (or commit) the transaction, leading to possible violation of the all-or-
nothing property. We will see more on this later in this chapter.

JTA Components
Here we will review the main components (interfaces) of the JTA specification and briefly discuss their roles. We will not
repeat the definitions for these interfaces; those can be found in the JTA specifications on Sun's site. The packages relevant
to this chapter are javax.transaction and javax.transaction.xa.

TransactionManager
Transaction
Xid
XAResource
Synchronization
UserTransaction
Exceptions

TransactionManager

The transaction manager is where you can create new transactions and set properties (such as timeout) for future
transactions. It also allows your application to retrieve the current transaction, after you have created one. An interesting
point is that this is thread-safe: if you have multiple threads running concurrently, then each thread can create its own
transaction and will be able to retrieve only that transaction which it created. The transaction manager will behave as a
'private' manager for each thread of your application.

The following methods are provided:

begin: this method creates a transaction for the application. When it returns, you will be able to retrieve the
transaction object through getTransaction within the current thread. Atomikos' TransactionsJTA supports nested
transactions, meaning that a transaction can be created within another one. This means that for TransactionsJTA,
calling this method twice in the same thread (without commit/rollback in between) will create a nested transaction,
whose final commit will coincide with the commit of the first transaction you created.
commit: this method will try to commit the last transaction that was created for the current thread. Afterwards, the
transaction can no longer be retrieved by getTransaction. For AtomikosJTA, if the last transaction was a
subtransaction then this will trigger the commit of the subtransaction. According to the semantics of nested
transactions, the subtransaction's updates will not be visible or permanent before the top-level transaction to which it
belongs is committed. The commit of a subtransaction will restore the thread association for its parent transaction.
This means that calling getTransaction will again return the parent transaction.
rollback: this method will trigger rollback of the last transaction that was created for the current thread. For
AtomikosJTA, nested semantics apply: if the current transaction is a subtransaction, then the rollback will not affect
the parent transaction: work done within the parent is not automatically lost by rolling back the subtransaction. As
with commit, this method changes the transaction-association for the thread. For a top-level transaction, this leaves
the current thread without a transaction. For a subtransaction, this method restores the thread association for the
parent transaction.
getTransaction: this method returns the transaction object for the calling thread, or null if there is no active
transaction. The transaction object is needed in order to add work to it: all the work that needs to be part of this
transaction must be explicitly added to it (more on that below).
setTransactionTimeout: this is to set the timeout of future transactions. A timeout indicates the time a transaction is
allowed to be active before it is automatically rolled back by the transaction manager.
getStatus: allows you to retrieve the status of the current transaction.
setRollbackOnly: see Transaction.
suspend: this method is useful if an active transaction exists, but you need to start a new transaction that is
independent. By suspending the current transaction, you dissociate it from the current thread and are free to begin a
new one, whose commit or rollback will not affect the current transaction. If you want to have another thread
continue the current transaction then this method can be used (in combination with resume) to 'pass on' the
transaction to another thread.
resume: this method (re-)associates the calling thread with an existing transaction (typically one that was suspended
first). If you continue a transaction in a different thread, then that thread should call this method with the transaction
as an argument. If you have done some intermediate work in a different transaction, then this method can be called
to resume the original transaction.

Transaction

The transaction interface allows manipulation of an active transaction. The most important role of this interface is to add
work to the scope of the transaction, thereby making the outcome of the work depend on the outcome of the transaction.
The functionality of the transaction interface is discussed below.

enlistResource: this method adds work to the transaction. The required argument is of type XAResource, which is
an interface for resources that understand two-phase commit. By enlisting an XAResource, the work that it represents
will undergo the same outcome as the transaction. If different resources are enlisted, then their outcome will be
consistent with the transaction's outcome, meaning that either all will commit or all with rollback.
delistResource: this method indicates that the application stops using the XAResource for this transaction. The
XAResource is essentially a connection to the underlying data source, and this method notifies the transaction
manager that the connection becomes available for two-phase commit processing.
There are two special cases: if a flag value of TMSUSPEND is given as a parameter, then the method call merely
indicates that the application is temporarily done and intends to come back to this work. This merely serves for
internal optimizations inside the data source. You should call this method if the transaction is being suspended.
Coming back to such a suspended work's context is done by calling enlistResource again, with the same XAResource.
The second special case is when TMFAIL is supplied as argument. This can be done to indicate that a failure has
happened and that the application is uncertain about the work that was done. In this case, commit should not be
allowed, because there is uncertainty about the contents of the transaction. For instance, if a SQLException occurs
during a SQL update, then the application can not know if the update was done or not. In that case, it should delist
the resource with the TMFAIL flag, because committing the transaction would lead to unknown effects on the data;
this could lead to corrupt data.
getStatus: this method returns the status of the transaction.
commit: same as TransactionManager.commit(). This method should not be called randomly: first, every
XAResource that was enlisted should also be properly delisted. Otherwise, XA-level protocol errors can occur.
rollback: same as TransactionManager.rollback(). As with commit, this method should not be called randomly: first,
every resource that was enlisted should also be delisted. Otherwise, XA-level protocol errors can occur.
setRollbackOnly: mark the transaction so that it can not commit. This method is provided to allow application code
to prevent the transaction from committing, without the requirement to call rollback directly. There are good reasons
for this: the rollback should happen after proper delisting of all resources and therefore is not something that happens
randomly. This method, however, can be called at any time when the transaction is active.
registerSynchronization: this method adds a callback for third-party notifications about two-phase commit outcome.
Xid

This interface is important for the communication between the transaction manager and the system behind the
XAResource. The XAResource is essentially a connection to that system, and many different transactions can use the same
connection. Therefore each time the transaction manager wants to begin or end a transaction, it needs to use an identifier
that the system behind understands and that identifies the work of the transaction in question. To this end, one JTA
transaction can have one or even multiple Xid instances associated to it. It is not necessary to completely understand this
mechanism in order to use TransactionsJTA, so it will not be discussed in more detail here.

XAResource

The XAResource is the transaction manager's connection to the data source. For each application-level connection, an
XAResource is needed to make the application's work through that connection part of a JTA transaction. The details of the
XAResource are not important for TransactionJTA, so we will not discuss them any further.

Synchronization

This interface is a means to register an application-level callback; it allows the application to be notified upon two-phase
commit events. You can use this functionality by implementing this interface in your application.

Note: synchronizations are not persistent; after a crash, any recovered transactions' synchronizations will be lost.

beforeCompletion: this method is called before the transaction will start its commit. A typical usage of this method
is to write pending updates to the database.
afterCompletion: this method is called after the commitment was done, and indicates whether it was successful or
not.

UserTransaction

This interface is a simple and restricted version of the JTA functionality. It is the typical application-level transaction
service handle in EJB. You can use this interface to expose only a subset of JTA functionality to the application code.

Exceptions

There are some specific exceptions in JTA that are worth mentioning: those that concern the heuristic terminations. Since
they are not really made clear in the JTA specification, we will mention something about them here. Whenever a heuristic
error happens the transaction manager should keep a log entry for the transaction involved, so that a human administrator
can resolve any conflicts. Part of Atomikos' patent applications concern precisely the kind of information that is available
in the logs in these cases.

HeuristicCommitException: if all resources have been in-doubt for too long, they may have committed the
transaction although all replied positively during the prepare of two-phase commit. If the transaction manager later
re-establishes contact and instructs the resources to rollback then this exception will be thrown to the application. It
indicates an anomaly in the transaction's outcome, where all resources involved have chosen to commit
heuristically, because all were left in-doubt. If you get this exception, it means that the entire transaction has been
committed, although rollback was desired.
HeuristicRollbackException: all resources have decided to rollback although the final decision of the transaction
manager was to commit. This is similar to the previous case; this time it means that the entire transaction has in fact
been rolled back whereas the desired outcome was commit.
HeuristicMixedException: this is the most complex error, where some of the resources may have committed and
others have rolled back. It hints that the transaction's effects are only partial; this is a clear violation of transactional
semantics. Remember, more information should be in the logs.

JTA Interactions
This section hightlights some typical JTA interactions for JDBC data sources. For other resources such as JMS queues,
most things are the same except for the way the XAResources are to be retrieved.

Active Transaction
Transaction Commit
Transaction Rollback
Transaction Termination with Errors

Active Transaction
The typical interactions for an active JTA transaction are shown below. Note that the only thing you have to provide is the
Application, and the Connection Manager if you don't use the Atomikos connection pools.

Please note a very important point when using connection pools: the connection manager will only be able to delist a
resource when it is informed about the application-level close operation on the JDBC connection. This means that you
should always properly close the connections from the pool; this should be done in the finally-part of a try{...}finally{...}
block. Opening the connection belongs in the try-part.

Transaction Commit

The typical commit scenario is shown below.


Transaction Rollback

A possible rollback scenario is shown below: the application requests commit, but one of the XAResources has timed out
and rolled back before it is asked to prepare. The result is rollback, and an application-level exception (since commit was
requested).

Transaction Termination with Errors

A possible heuristic scenario is shown below: the application requests commit, but one of the XAResources becomes
unreachable after it is asked to prepare. The result is heuristic rollback, and by the time the transaction manager re-
establishes contact commit fails with a heuristic error. An application-level heuristic mixed exception is thrown (since the
other two XAResources did commit, parts have been committed and other parts have not).!
Questions
Question 1

Consider the two-phase commit protocol and the example in the text: an update in a database and a message being
published in JMS, as part of one transaction. Can't you solve the problem of reaching the same outcome for both parts by
controlling the order in which you execute each one? For instance, why bother controlling the outcome of a database
update if your application knows that it succeeded already?!

Question 2

Imagine the following scenario: an application is using a JTA implementation to manage transactions that access two JDBC
databases, say, DBa and DBb. The application has updated both of them and is in the course of committing the transaction.
As part of that commit processing, the transaction manager has received positive replies from both databases when it asked
them to prepare and hence it decides to commit. However, it can only notify DBa of this decision: before DBb can be told
about commit, a system crash happens and causes DBb to go down. The rest of the system is not affected because DBb is
running on its own private machine. The transaction manager repeatedly retries to connect to DBb, but after a while it
gives up and throws an exception to the application. What is the exact type of this exception?

Question 3

Which of these methods can be called at any time when a transaction is active: setRollbackOnly or rollback? Why?

Question 4

An application has started a transaction and is in the middle of updating a JDBC database when a SQLException happens.
Should the transaction be committed or rolled back?

Question 5

An application is listening on incoming remote method invocation (RMI) request. An incoming requests is executed in
some Java thread according to the virtual machine's internal rules. This thread could be the same one as for a previous
request. The application-level logic for an invocation involves creating a JTA transaction and doing some JDBC work as
well as publishing a JMS message. If you are using a JTA implementation that supports nested transactions, why should
you always make sure that the transaction is terminated (by commit or rollback) before the invocation returns?

4. Configuring TransactionsJTA
Whereas the previous chapter was generic JTA information, this chapter is specific to TransactionsJTA. It concerns the
setup (configuration) of TransactionsJTA in your application.

TransactionsJTA is an embedded transaction service, meaning that it runs inside the same VM as your application. This
optimizes speed and availability of your application.

The configuration of TransactionsJTA is done in two parts:

The configuration file (static information).


The runtime setup (dynamic information).

The configuration file is independent of which type of resources you want to use. It contains general transaction-related
information such as where logfiles are to be kept and what default timeout values are.

The runtime setup is needed to indicate which resources you want to use in your transactions. This is important since it also
indicates which resources are to be recovered during transaction service startup. Because runtime setup is different for JMS
or JDBC data sources, we discuss each separately. We will also see how to add other types of XA-capable resources.

For each type of JDBC and JMS setup there is a complete application with source code in the appendix.

The rest of this chapter has been organized as follows:

The Configuration File


Runtime Configuration
Configuring for JDBC Transactions
JDBC Integration at the XA-Level
JDBC Integration with Atomikos Connection Pooling
Configuring for JMS Transactions
JMS Integration at the XA-Level
JMS Integration with Atomikos JMS/JTA Adapters
Configuring for Other XA Resource Types
Questions

The Configuration File


The configuration file contains the parameters for initialization and operation of the transaction service. If this file can not
be found then default values will be used. To instruct TransactionsJTA to use a custom configuration file, there are several
possibilities:

Name the file standalone.prp and put it in the directory where you start your application (assuming you are using a
command-line approach to launch).
Give your file any name and location you like, and specify this as a system property at startup: java -
Dcom.atomikos.icatch.standalone.properties=path_to_your_file ... Note that setting this system property
overrides any standalone.prp configuration data that you might have according to the first approach.
To avoid using a configuration file, you can also use system properties for each of the parameter settings. You can
indicate that this is the case by supplying the following system property at startup: java -
Dcom.atomikos.icatch.standalone.no_file ... In that case, your custom settings need to be supplied as system
properties with the same name as their configuration file equivalents.

The configuration file can contain the parameters shown in the example below. Note that the format should be a valid Java
property-file format.

!#THIS FILE ILLUSTRATES THE DIFFERENT SETTINGS FOR THE TRANSACTION MANAGER
!#UNCOMMENT THE ASSIGNMENTS TO CHANGE THEIR VALUES;
!#VALUES SHOWN NOW ARE THE DEFAULTS

!#set name of file where messages are output


!#console_file_name = tm.out

!#set the number of log writes between checkpoints


!#checkpoint_interval=500

!#set output directory where console file and other files are to be put
!#make sure this directory exists!
!#output_dir = ./

!#set directory of log files; make sure this directory exists!


!#log_base_dir = ./

!#set base name of log file


!#this name will be used as the first part of
!#the system-generated log file name
!#log_base_name = tmlog

!#set the max number of active local transactions


!#max_actives = 50

!#set the max timeout (in milliseconds) for local transactions


!#max_timeout = 60000

Runtime Configuration
For any application based on TransactionsJTA, we can distinguish the following main steps:

1. Get an instance of com.atomikos.icatch.TSInitInfo.


2. Register one or more com.atomikos.datasource.RecoverableResource instances with the TSInitInfo.
3. Optionally register a com.atomikos.icatch.admin.LogAdministrator with the TSInitInfo.
4. Initialize (startup) the transaction service.
5. Let your application do its work.
6. Shutdown the transaction service.

The following code fragment illustrates this and summarizes the information in this chapter:

!TSInitInfo info = StandAloneConfiguration.createTSInitInfo();


!//register one or more resources, as explained in this chapter
!UserTransactionService uts =
!!StandAloneConfiguration.getUserTransactionService();
!uts.init ( info );

!//the transaction service is now running.

!//let your application do the work, as explained in next chapter

!//shutdown the transaction service


!uts.shutdown ( false );

This chapter is about steps 1, 2, 4 and 6. Step 3 is discussed in the Appendix. Step 5 is the topic of the next chapter, when
we talk about programming with TransactionsJTA. The main runtime configuration of TransactionsJTA is done in steps 1
and 2: there you indicate what resources (data sources) should fall under the custody of the transaction service. This is most
important for recovery.

Getting a TSInitInfo Instance

An instance of TSInitInfo can be obtained through the


com.atomikos.icatch.standalone.StandAloneConfiguration.createTSInitInfo() method. This will return an instance for
TransactionsJTA. The TSInitInfo is an interface for intialization information that can be specified at startup.

Registering a RecoverableResource

The interface com.atomikos.datasource.RecoverableResource allows about any data source you can imagine to take part
in transactions. For instance, it is possible to make a transactional file system that implements this interface.

It is important to note that each resource should be given a unique name that identifies the combination (transaction
manager, resource). So if you use the same database or queue in different transactional applications then you need to
make sure that every application uses a unique and different name.

The JTA specification works with the XA protocol for making things transactional, so we have provided some XA-specific
implementations of RecoverableResource for your convenience. The two most useful ones are one for JDBC and one for
JMS transactions. Each of them is discussed in the rest of this chapter.

Initialize the Transaction Service

First, you need to get an instance of com.atomikos.icatch.UserTransactionService through


com.atomikos.icatch.standalone.StandAloneConfiguration.getUserTransactionService(). This interface is the main
application-level handle to the transaction service. Then you can call the init method with the TSInitInfo as argument.

Shutdown the Transaction Service

Before your application exits, you should give the transaction service a chance to perform proper shutdown (closing the
logs, detecting active transactions, ...). You can do this by calling the UserTransactionService's shutdown method. This
method expects one boolean argument indicating whether to force shutdown or not. If true, then shutdown proceeds even if
there are active transactions. Forcing shutdown can sometimes lead to increased risk for heuristics. Note: shutdown(false)
will block until any remaining active transactions are finished.

Configuring for JDBC Transactions


The configuration needed to incorporate JDBC database accesses in your transactions depends on whether you want to
work at the XA-level or not. Each option is discussed separately.

JDBC Integration at the XA-Level

This type of integration is of interest to application-server vendors who are likely to have their own connection pooling
support. If you are not going to use Atomikos' connection pooling then setup is done by registering an instance
of com.atomikos.jdbc.JdbcTransactionalResource with the TSInitInfo instance.

This class implements RecoverableResource and has two constructors:

JdbcTransactionalResource (String serverName , javax.sql.XADataSource ds)


The constructor you would normally use. The first argument is a unique name that identifies the combination
(resource,transaction manager). The second argument is the datasource to get XAConnections from.
JdbcTransactionalResource (String serverName , javax.sql.XADataSource ds, com.atomikos.datasource.xa.XidFactory
xidFactory)
This constructor is added for occasional datasources that will not accept arbitrary Xid instances (as, for instance,
Oracle8x). In that case, the TransactionsJTA internal Xid format will not work and the supplied factory merely
handles the creation of an Xid specific for the datasource, based on the JTA unique transaction identifier (a String)
and the unique resource name. For Oracle, a predefined XidFactory is included: class
com.atomikos.datasource.xa.OraXidFactory.

As soon as you have created an instance of JdbcTransactionalResource, you can register it with TSInitInfo. Later, after
startup, you will be able to run transactions that include data accesses to the underlying database (as explained in the next
chapter). The following code fragment illustrates JDBC setup for XA integration.

!...
!//database-specific setup of an XADataSource;
!//see your favorite database docs for details
!//on where and how to create the data source instance
!XADataSource xads = ...

!//create a JdbcTransactionalResourc for the data source


!JdbcTransactionalResource res =
!!new JdbcTransactionalResource ( "MyResourceName" , xads );
!//register the resource with the TSInitInfo
!info.registerResource ( res );
!...

JDBC Integration with Atomikos Connection Pooling

Use this approach if you don't want to write XA-level integration code. If you are using Atomikos' Connection Pooling
then you don't need to explicitly create a JdbcTransactionalResource. Rather, you create
an instance of Atomikos' com.atomikos.jdbc.JtaDataSourceImp to get connections from. As part of that process, a
JdbcTransactionalResource will be created for you. The class JtaDataSourceImp implements the javax.sql.DataSource
interface, so it is JDBC-compatible. The creation of a JtaDataSourceImp is a three-step process:

The first step in creating a JtaDataSourceImp is to create an instance of com.atomikos.jdbc.XAConnectionFactory. This


factory object will be needed to create pooled connections from. The following code fragment illustrates this.

!...
!//database-specific setup of an XADataSource;
!//see your favorite database docs for details
!//on where and how to create the data source instance
!XADataSource xads = ...

!//create an Atomikos connection factory for the data source


!//and supply the unique name for the JdbcTransactionalResource to be created
!//as well as the user and passwd to get connections for.
!XAConnectionFactory factory =
!!new XAConnectionFactory ( "MyResourceName" , "MyUserName" , "MyPassword" , xads );
!...

Note that an additional constructor is available that allows you to supply a custom XidFactory; this is needed only if the
XADataSource will not accept arbitrary Xid formats.
At creation time, this connection factory has automatically created a JdbcTransactionalResource for you. You can (and
should) register this resource in the TSInitInfo instance:

!...
!TransactionalResource res = factory.getTransactionalResource();
!info.registerResource ( res );
!...

The final step is to create the JtaDataSourceImp instance.

!...
!//how large is the connection pool?
!int poolsize = 5;
!//how many seconds should connections be allowed inactive?
!int ctimeout = 30;
!//how many seconds before a statement times out?
!int stimeout = 30;
!com.atomikos.jdbc.JtaDataSourceImp jtads =
!!new JtaDataSourceImp ( factory , poolsize , ctimeout , stimeout );
!info.registerResource ( res );
!...

You can now use the JtaDataSourceImp in your application to access pooled connections. The picture below summarizes
all of the JDBC pooling setup.

Configuring for JMS Transactions


JMS (queuing) applications can be combined in two ways with TransactionsJTA: either by doing all of the XA-related
operations in the application, or by letting Atomikos' JMS adapter framework take care of that. The first case is more
complicated to program than the second. Each of them will be discussed in turn.

NOTE: for JMS, currently only queuing operations are readily supported by TransactionsJTA. If you do need to
incorporate the publish-subscribe model in your transactions then see the following section, Configuring for Other
XAResource Types.
JMS Integration at the XA-Level

This is the more complicated way of combining TransactionsJTA with your JMS application. The complexity appears at
the programming level, not at the configuration level.

In order to make a JMS (queuing) system transactional, you need to create the proper RecoverableResource to register with
the transaction service. For JMS queues, the appropriate class
to use is com.atomikos.datasource.xa.jms.JmsTransactionalResource. The two constructors you can use are the
following:

JmsTransactionalResource(String name, javax.jms.XAQueueConnectionFactory qFactory)


The first argument represents the unique name for the resource, the second argument is the connection factory to the
JMS in question.
JmsTransactionalResource(String name, javax.jms.XAQueueConnectionFactory qFactory,
com.atomikos.datasource.xa.XidFactory xFactory)
The first two arguments have the same meaning as in the other constructor. The last argument serves to supply a
custom Xid factory when the JMS will not accept arbitrary Xid instances. This should rarely happen.

Most of the time, the first constructor will be the one you need. Once created, the resource can be registered with the
transaction service.!The following code fragment illustrates this.

!...
!//get an XAConnectionFactory for the queuing system, cf. your JMS provider's documentation
!XAConnectionFactory factory = ...

!//create a JmsTransactionalResource
!JmsTransactionalResource resource =
!!new JmsTransactionalResource ( "MyUniqueName" , factory );
!//add the resource to the info object
!info.registerResource ( resource );
!...

JMS Integration with Atomikos JMS/JTA Adapters

Use this approach if you don't want to use XA-level integration. For your convenience, a JMS adapter framework is
included with TransactionsJTA. This framework hides the extra complexities of XA-related operations from your
application. Externally, it offers the same set of interfaces as any JMS (1.0.2b) queuing system with the difference that
everything works in JTA transactions.

Instead of directly creating a JmsTransactionalResource (as in the previous case), you now create a
com.atomikos.datasource.xa.jms.JtaQueueConnectionFactory. This is an implementation of
javax.jms.QueueConnectionFactory that performs XA-related tasks behind the scenes. From this specialized factory, you
can then retrieve the JmsTransactionalResource instance (which is created for you during initialization).

The constructors available have the same arguments as for JmsTransactionalResource:

JtaQueueConnectionFactory (String name, javax.jms.XAQueueConnectionFactory qFactory)


The first argument represents the unique name for the resource, the second argument is the XAConnection factory to
the JMS in question.
JtaQueueConnectionFactory (String name, javax.jms.XAQueueConnectionFactory qFactory,
com.atomikos.datasource.xa.XidFactory xidFactory)
The first two arguments have the same meaning as in the other constructor. The last argument serves to supply a
custom Xid factory when the JMS will not accept arbitrary Xid instances. This should not happen very often.

Usually you will only need the first constructor. Once created, this JtaQueueConnectionFactory also has the resource you
need to add to the transaction service. The following code fragment illustrates this.

!...
!//get an XAConnectionFactory for the queueing system, cf. your JMS provider's documentation
!XAConnectionFactory factory = ...
!//create the JtaQueueConnectionFactory
!JtaQueueConnectionFactory jtaConnFactory =
!!new JtaQueueConnectionFactory ( "MyResourceName" , factory );
!//get the corresponding JmsTransactionalResource
!JmsTransactionalResource resource =
!!jtaConnFactory.getTransactionalResource();

!//add the resource to the info object.


!info.registerResource ( resource );
!...

The entire JMS setup scenario is illustrated in the following picture.

Configuring for Other XA Resource Types

If you need JTA transactions that access systems other than JDBC or JMS queues then you may need to implement your
own adapter class, by extending our com.atomikos.datasource.xa.XATransactionalResource class.

As the previous diagram shows, the XATransactionalResource is an abstract class with generic functionality already in
place. The diagram only shows the methods that are relevant for you:

A constructor with a String argument


This is the most commonly used constructor, with the unique resource name as the argument value.
A constructor with String and XidFactory arguments
This is the constructor for special cases where a vendor-specific Xid format is required.
Method refreshXAConnection
This method must be overridden for your case. It is called at regular intervals to renew the XAResource that is used
by the XATransactionalResource internally. Different back-end systems typically require different procedures in
order to get an XAResource instance.
Method close
This method may be overridden for your case. For instance, a typical example is that you need to clean up any
remaining connections that you use to implement the refresh method. In order for the XAResource returned by
refreshConnection to remain valid, the connection that was used to obtain it must often remain open. The close
method is a good place to clean up such pending open connections. It is called as part of the transaction service
shutdown procedure.

Refer to the appendix for an example of how to implement a typical adapter.

Questions
Question 1

Suppose you are doing an Enterprise Application Integration (EAI) project that connects a JDBC database, a JMS queue
and a JCA (Java Connector API) adapter to a legacy system. Which of the following does not have out-of-the-box support
in TransactionsJTA?

1. The JDBC database access.


2. The JMS queue access.
3. The JCA adapter to the legacy system.

Question 2

If you don't have a configuration file and don't set system properties related to the configuration, where will the transaction
service write log files?

1. In the current directory.


2. In the directory where you start your application.
3. In a new directory, called tmlog.

Question 3

In the configuration file, there is a parameter called checkpoint_interval. If you increase this parameter's value, then what
happens?

1. The average log file size will be larger.


2. The average log file size will be smaller.

Question 4

Are the following statements true or false? Suppose there is a back-end JDBC database that is not registered with the
transaction service by means of a corresponding resource. In that case,

1. The transaction service will not perform recovery for that JDBC database.
2. The JTA transactions you create can not include accesses to that JDBC database.

5. Programming with TransactionsJTA


The previous chapter showed how to setup the TransactionsJTA for your application. This chapter explains how the actual
transactional programming is done. The typical way of programming with TransactionsJTA is illustrated by the following
code fragment. Important points are included in comments.

!...
!import com.atomikos.icatch.UserTransactionService;
!import com.atomikos.icatch.standalone.StandAloneConfiguration;
!import javax.transaction.*;
!...
!//get the main application-level handle to the transaction service
!UserTransactionService uts =
!!StandAloneConfiguration.getUserTransactionService();
!//retrieve a handle to the JTA transaction manager
!TransactionManager tm = uts.getTransactionManager();
!//rollback flag, true as soon as error happens
!boolean rollback = false;!

!try {
!!rollback = false;

!!//start a new transaction


!!tm.begin();

!!//application logic of transaction


!}
!catch ( Exception e ) {
!!//rollback for safety
!!rollback = true;
!!throw e;
!}
!finally {
!!//ALWAYS make sure that the transaction is terminated!
!!if ( rollback )
!!!tm.rollback();
!!else
!!!tm.commit();
!}

The rest of this chapter discusses programming in more detail, according to the following outline.

Beginning a Transaction
JDBC Transactions
XA-Level Integration
Integration with Atomikos Connection Pools
JMS Transactions
XA-Level Integration
Integration with Atomikos JMS/JTA Adapters
Transactions for Other XAResource Types
Terminating a Transaction
Questions

Complete example programs for JDBC and JMS programming with TransactionsJTA are provided in the appendix.

Beginning a Transaction
Beginning a transaction is done by calling the TransactionManager.begin() method. The previous chapter explained how to
initialize the transaction service by calling its init() method with the initialization object. From that moment on, a JTA
Transaction Manager is available inside your application's virtual machine and can be retrieved through the
UserTransactionService interface.

JDBC Transactions
How to include JDBC access in a JTA transaction depends on the options you made during configuration.

XA-Level Integration

If you chose for XA-level integration then more things will need to be done by your application (either inline with the
code as shown here, or behind the scenes in custom connection pools). The following code fragment elaborates on how to
provice XA-level transaction integration to a JDBC database. It is based on the code fragment at the beginning of this
chapter.

!...
!//we assume you have gotten an instance of XADataSource already; see the previous chapter
!XADataSource xads = ... //vendor-specific
!//get an XAConnection from the XA datasource
!XAConnection xaconn = xads.getXAConnection();

!//rollback flag, true as soon as error happens


!boolean rollback = false;
!try {
!!rollback = false;

!!//start a new transaction for the current thread


!!tm.begin();

!!//retrieve the transaction


!!Transaction tx = tm.getTransaction();
!!//get the XAResource instance for the connection
!!XAResource xares = xaconn.getXAResource();

!!//enlist the resource with the transaction


!!tx.enlistResource ( xares );

!!//access the database


!!Connection conn = xaconn.getConnection();

!!//perform regular JDBC access through the connection


!!...

!!//close the connection


!!conn.close();

!!//delist the resource


!!tx.delistResource ( xres , XAResource.TMSUCCESS );

!}
!catch ( Exception e ) {
!!//rollback for safety
!!rollback = true;
!!throw e;
!}
!finally {
!!//ALWAYS make sure that the transaction is terminated!
!!if ( rollback )
!!!tm.rollback();
!!else
!!!tm.commit();
!}

Note that this is a simplified example showing only the most essential operations. In practice, you should make sure that
delistResource is called even in case of exceptions during JDBC access (with the TMFAIL flag in that case). Application-
server vendors with their own connection pools typically do this behind the scenes.

Integration with Atomikos Connection Pools

If you don't want to implement your own xaconnection management then it is probably better to use the Atomikos pooling
utilities. This section assumes that you have setup and configured TransactionsJTA to use the supplied pooling facility. In
other words, you have created a JtaDataSourceImp and registered the corresponding JdbcTransactionalResource.

In this case, transactional JDBC (version 2) is simplified to the following:

!...
!//we assume you have created a JtaDataSourceImp at startup, as described in chapter 4
!JtaDataSourceImp jtads = new JtaDataSourceImp ( ... );
!...
!//rollback flag, true as soon as error happens
!boolean rollback = false;!

!//a JDBC connection will be needed


!Connection conn = null;

!try {
!!rollback = false;

!!//start a new transaction


!!tm.begin();

!!//application logic of transaction:


!!//get a connection
!!conn = jtads.getConnection();

!!//do regular JDBC SQL


!!...

!}
!catch ( Exception e ) {
!!//rollback for safety
!!rollback = true;
!!throw e;
!}
!finally {
!!//ALWAYS close the connection to return it to the pool!
!!if ( conn != null )
!!!conn.close();

!!//ALWAYS make sure that the transaction is terminated!


!!//This should happen AFTER the connection was closed.
!!if ( rollback )
!!!tm.rollback();
!!else
!!!tm.commit();
!}

In this case programming is according to the normal javax.sql.DataSource way. As can be seen, this is very simple. The
important thing is to always close a connection; this will return it to the pool and also do the delistResource behind the
scenes. After that, commit or rollback can be done.

NOTE: the getConnection() operation will only work inside a JTA transaction. Also note that the JtaDataSourceImp class
implements the javax.sql.DataSource interface.

JMS Transactions
For JMS, we will again make the distinction between XA-level and the simpler integration through adapters.

XA-Level Integration

Here, it is assumed that you have done the corresponding configuration tasks for JMS with XA-level integration. For this
mode, you need to work with XA classes and perform the enlistResource and delistResource operations yourself, either
inline or behind the scenes through utility classes that you create. The following illustrates this.

!...
!//we assume you have created an XAQueueConnectionFactory as described in chapter 4.
!XAQueueConnectionFactory factory = ...
!//create an XAQueueConnection
!XAQueueConnection xaconn = factory.createXAQueueConnection();

!//create an XAQueueSession
!XAQueueSession xasession = xaconn.createXAQueueSession();

!//get the QueueSession


!QueueSession session = xasession.getQueueSession();

!//create or find a queue to use


!Queue queue = ...
!//rollback flag, true as soon as error happens
!boolean rollback = false;!

!Tranaction tx = null;

!try {
!!rollback = false;

!!//start a new transaction


!!tm.begin();

!!//create a sender for the session


!!QueueSender sender = session.createSender ( queue );

!!//create a message
!!Message message = session.createTextMessage();

!!//fill in the message content


!!...

!!//BEFORE doing a send, make sure you enlist


!!tx = tm.getTransaction();
!!XAResource xares = xasession.getXAResource();
!!tx.enlistResource ( xares );
!!//do the JMS send with the corresponding parameters
!!sender.send ( message , DeliveryMode.PERSISTENT , Message.DEFAULT_PRIORITY , 0 );
!!//delist
!!tx.delistResource ( xares , XAResource.TMSUCCESS );

!}
!catch ( Exception e ) {
!!//rollback for safety
!!rollback = true;
!!throw e;

!}
!finally {
!!//ALWAYS make sure that the transaction is terminated!
!!if ( rollback )
!!!tm.rollback();
!!else
!!!tm.commit();
!}

Integration with Atomikos JMS/JTA Adapters

The simpler way of integrating JMS (1.0.2b) with your JTA transactions is through the Atomikos JMS/JTA adapters. Here
we assume that you have done setup and configuration as required for this paradigm. This means that you have created a
JtaQueueConnectionFactory. The typical application-level tasks are shown below.

!...
!//we assume you have created a JtaQueueConnectionFactory during setup
!JtaQueueConnectionFactory factory = ...

!//create a regular QueueConnection


!QueueConnection conn = factory.createQueueConnection();

!//create a QueueSession; arguments are IGNORED for JTA!


!QueueSession session = conn.createQueueSession ( true , 0 );

!//get a reference to the queue


!Queue queue = ...

!//rollback flag, true as soon as error happens


!boolean rollback = false;!

!try {
!!rollback = false;

!!//start a new transaction


!!tm.begin();

!!//create a queue sender


!!QueueSender sender = session.createSender ( queue );

!!//create and fill in message


!!Message message = session.createTextMessage();
!!...

!!//send the message


!!sender.send ( message , DeliveryMode.PERSISTENT , Message.DEFAULT_PRIORITY , 0 );
!}
!catch ( Exception e ) {
!!//rollback for safety
!!rollback = true;
!!throw e;
!}
!finally {
!!//ALWAYS make sure that the transaction is terminated!
!!if ( rollback )
!!!tm.rollback();
!!else
!!!tm.commit();
!}

This looks exactly like regular JMS queue manipulation, with the exception that everything is transactional.

Atomikos JMS adapters require a transaction for a send or receive; trying to send or receive outside a transaction will fail.
Also, there are no clear transactional semantics for the MessageListener interface (the JMS specifications are not clear with
respect to this issue).

Transactions for Other XAResource Types


For other XAResource types, setup needs to be done according to the rules specific for those systems. In addition, the
programming will be more complex and look similar to the JDBC and JMS schemes outlined. This means that before and
after an operation is done on the resource, the appropriate enlistResource and delistResource calls should be made.

Terminating a Transaction
The termination of a transaction is done by calling either the rollback or commit method. Termination is extremely
important, to avoid confusion with later transactions started in the same thread (see our JTA discussion on nested
transactions).

Questions
Question 1

For each of the following, indicate whether the application code is portable across multiple vendors' JTA.

1. JDBC-XA
2. JDBC-DataSource
3. JMS-XA
4. JMS-Adapters

6. Appendix A: Answers
Chapter 3: Answers
Question 1

You can not control the joint outcome of a database update and a JMS message publication by merely ordering the
executions. For instance, if you do a successful database update first and then try to send a JMS message, then what do you
do if the JMS part fails? You could argue that the JDBC and the JMS allow you to explicitly control commit and rollback,
but that does not change the real problem: in what order should the commits of the database and the message send be done?
If you commit the database first, there is no way to go back if the later JMS commit fails. A similar argument shows that
the JMS can not be committed first either. Controlling the order of executions or the order of commits is not a solution;
that is why two-phase commit is used.

Question 2

The exception is of type HeuristicMixedException. The transaction manager is cut off from the in-doubt database, so it
does not know whether the database will make a heuristic decision or not, and if it does then it could be either commit or
rollback. A violation of the requirement that all resources have the same outcome is possible.

Question 3
The method setRollbackOnly can be called almost anytime. The method rollback should only be called after all resources
that were enlisted have also been delisted accordingly. Otherwise, XA-level errors may occur during rollback.

Question 4

If a SQLException happens in a database update, there is a possibility that the database state for the active transaction is
corrupt or does not correspond to what is expected from the point of view of the application. The transaction should be
rolled back to make sure that the database is restored to the previous and correct state.

Question 5

The JTA transaction manager associates the thread to the transaction you create. This means that if you call
TransactionManager.begin() at the beginning of the method invocation then the executing thread has a JTA transaction. If
you don't terminate this transaction then it will become a pending active transaction, subject to JTA rollback after timeout
(and this will happen). Before the timeout, other requests may be executed in the same thread. If that happens then the
second request's TransactionManager.begin() will create a subtransaction of the still pending transaction. So even if the
second invocation calls commit, this will be a commit of a subtransaction, whose effects will not become permanent until
the parent transaction commits. But the latter will not happen because the parent is a pending active transaction that will be
rolled back if it times out. This is illustrated in the picture below.

Chapter 4: Answers
Question 1

The JCA does not have out-of-the-box support: you need to subclass
com.atomikos.datasource.xa.XATransactionalResource in order to use the legacy system in your JTA transactions.

Question 2

The logs will be written in the directory where you start your application. See the sample configuration file listing, which
shows the default values. These are the values used if no other information is available.

Question 3

The average log file size will be larger. The checkpoint interval is the number of writes to the sequential log file before
this file is cleaned up, so if this number is higher then the average log file size will increase as well. The cleanup
encompasses purging the log by deleting information about terminated transactions.
Question 4

Both statements are true: no recovery will happen on behalf of the transaction service, and no JTA-transactional accesses
will be possible. You need to register the resource in order to do that.

Chapter 5: Answers
Question 1

If we disregard the configuration code (by separating it from the application code) then the following can be said about
portability:

1. JDBC-XA is portable; no Atomikos-specific classes or techniques are used.


2. JDBC-DataSource is portable: this is all standardized.
3. JMS-XA is portable for the same reasons.
4. JMS-Adapters is not portable; we know of no clear agreement on how to integrate JMS with JTA at the time of
writing.

The configuration code is not portable because there is no specification on how to configure and setup JTA.

7. Appendix B: Complete Examples


Example 1: XA-Level JDBC Integration
The first example demonstrates a bank account application that manages transactions in a Cloudscape database by XA-level
integration with TransactionsJTA.

package examples.jdbc.xa;

import javax.transaction.*;
import javax.transaction.xa.*;
import javax.sql.*;
import java.sql.*;

import com.atomikos.icatch.*;
import com.atomikos.icatch.standalone.*;
import com.atomikos.datasource.*;
import com.atomikos.datasource.xa.*;
import com.atomikos.jdbc.*;

import COM.cloudscape.core.DataSourceFactory;

/**
*Copyright © 2002, Guy Pardon. All rights reserved.
*
*A simple program that uses XA-level integration with
*TransactionsJTA. Although only one database is
*accessed, it shows all important steps for programming
*with TransactionsJTA.
*
*Usage: java XaAccount account_number operation [amount]

*where:
*account number is an integer between 0 and 99

*and operation is one of (balance, owner, withdraw, deposit).

*In case of withdraw and deposit, an extra (integer) amount is expected.


*/

public class XaAccount


{
//the user name in the database; change if needed.
//the current settings are empty strings, for
//cloudscape embedded database without
//authentication
private static String user = "";
//the password for the user; change if needed.
//the current settings are empty strings,
//for cloudscape embedded database
private static String passwd = "";

//the unique resource name for the DB; change if needed


private static String resourceName = "ExamplesJdbcXaDatabase";

//the data source, set by getXADataSource


private static XADataSource xads = null;

/**
*Initialize the TM, and setup DB tables if needed.
*/

private static void setup()


throws Exception
{

//STEP 1: Cloudscape-specific setup of XADataSource


xads = DataSourceFactory.getXADataSource();
COM.cloudscape.core.AbstractDataSource ads =
( COM.cloudscape.core.AbstractDataSource ) xads;
ads.setDatabaseName ( resourceName );
ads.setCreateDatabase ( "create" );

//STEP 2: create the resource to register with the


//transaction service.
JdbcTransactionalResource resource =
new JdbcTransactionalResource (
resourceName , xads );

//STEP 3: get the TSInitInfo and register the resource


TSInitInfo info = StandAloneConfiguration.createTSInitInfo();
info.registerResource ( resource );

//STEP 4: get the UserTransactionService and initialize.


UserTransactionService uts =
StandAloneConfiguration.getUserTransactionService();
uts.init ( info );

//From here on, the transaction service is running...

//Below is application-specific setup


TransactionManager tm = uts.getTransactionManager();
tm.setTransactionTimeout ( 60 );

boolean error = false;


XAConnection xaconn = null;
try {
xaconn = getConnection();
Statement s = xaconn.getConnection().createStatement();
try {
s.executeQuery ( "select * from Accounts" );
}
catch ( SQLException ex ) {
//table not there = > create it
System.err.println ( "Creating Accounts table..." );
s.executeUpdate ( "create table Accounts ( " +
" account VARCHAR ( 20 ), owner VARCHAR(300), balance DECIMAL (19,0) )" );
for ( int i = 0; i < 100 ; i++ ) {
s.executeUpdate ( "insert into Accounts values ( " +
"'account"+i +"' , 'owner"+i +"', 10000 )" );
}
}
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( xaconn , error );

//That concludes setup

/**
*Shutdown the TM and the datasource.
*/

private static void shutdown()


throws Exception
{
StandAloneConfiguration.getUserTransactionService().
shutdown ( true );

//Cloudscape-specific shutdown procudure


//for embedded database
COM.cloudscape.core.AbstractDataSource ds =
( COM.cloudscape.core.AbstractDataSource ) getXADataSource();
ds.setShutdownDatabase ( "shutdown" );
try {
getXADataSource().getXAConnection().getConnection();
}
catch ( Exception e ) {
//exception seems to be inevitable for clean shutdown
//of cloudscape embedded DB...
}

//
//THE CODE BELOW SHOULD NOT BE CHANGED;
//IT SHOULD WORK ON ANY JDBC COMPLIANT SYSTEM
//

/**
*Gets the xa datasource instance.
*
*@return XADataSource The data source.
*/

private static XADataSource getXADataSource()


throws Exception
{
return xads;
}

/**
*Utility method to start a transaction and
*get a connection.
*This method does all XA related tasks
*and should be called within a transaction.
*@return XAConnection The xa connection.
*/

private static XAConnection getConnection()


throws Exception
{
XADataSource xads = getXADataSource();
XAConnection xaconn = null;
//retrieve the TM
TransactionManager tm = getTransactionManager();

//First, create a transaction


tm.begin();
xaconn = xads.getXAConnection ( user , passwd );
XAResource xares = xaconn.getXAResource();
//get the current tx
Transaction tx = tm.getTransaction();
//enlist
tx.enlistResource ( xares );
return xaconn;

/**
*Utility method to close the connection and
*terminate the transaction.
*This method does all XA related tasks
*and should be called within a transaction.
*When it returns, the transaction will be terminated.
*@param xaconn The xa connection.
*@param error Indicates if an error has
*occurred or not. If true, the transaction will be rolled back.
*If false, the transaction will be committed.
*/

private static void closeConnection ( XAConnection xaconn , boolean error )


throws Exception
{
int flag = XAResource.TMSUCCESS;
XAResource xares = xaconn.getXAResource();
//retrieve the TM
TransactionManager tm = getTransactionManager();

//get the current tx


Transaction tx = tm.getTransaction();
//closeConnection
if ( error )
flag = XAResource.TMFAIL;
tx.delistResource ( xares , flag );
//close the JDBC user connection
xaconn.getConnection().close();

if ( error )
tm.rollback();
else
tm.commit();

//close XAConnection AFTER commit, or commit will fail!


xaconn.close();
}

private static TransactionManager getTransactionManager()


{
return StandAloneConfiguration.getUserTransactionService().
getTransactionManager();
}

private static long getBalance ( int account )


throws Exception
{
long res = -1;
boolean error = false;
TransactionManager tm = getTransactionManager();
XAConnection xaconn = null;

try {
xaconn = getConnection();
Statement s = xaconn.getConnection().createStatement();
String query = "select balance from Accounts where account='"
+"account"+account+"'";
ResultSet rs = s.executeQuery ( query );
if ( rs == null || !rs.next() )
throw new Exception ( "Account not found: " + account );
res = rs.getLong ( 1 );
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( xaconn , error );
}
return res;
}

private static String getOwner ( int account )


throws Exception
{
String res = null;
boolean error = false;
TransactionManager tm = getTransactionManager();
XAConnection xaconn = null;

try {
xaconn = getConnection();
Statement s = xaconn.getConnection().createStatement();
String query = "select owner from Accounts where account='account"
+ account+"'";
ResultSet rs = s.executeQuery ( query );
if ( rs == null || !rs.next() )
throw new Exception ( "Account not found: " +account );
res = rs.getString ( 1 );
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( xaconn , error );
}
return res;
}

private static void withdraw ( int account , int amount )


throws Exception
{
boolean error = false;
TransactionManager tm = getTransactionManager();
XAConnection xaconn = null;

try {
xaconn = getConnection();
Statement s = xaconn.getConnection().createStatement();

String sql = "update Accounts set balance = balance - "


+ amount + " where account ='account"+account+"'";
s.executeUpdate ( sql );
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( xaconn , error );

}
public static void main ( String[] args )
{
try {
//test if DB data has to be created
setup();

if ( args.length < 2 || args.length > 3 ) {


System.err.println (
"Arguments required: account_number operation [amount]" );
System.exit ( 1 );
}

//get account number


int accno = new Integer ( args[0] ).intValue();
if ( accno < 0 || accno > 99 ) {
System.err.println (
"Account number should be between 0 and 99." );
System.exit ( 1 );
}

//get operation
String op = args[1];

if ( op.equals ( "balance" ) ) {
long bal = getBalance ( accno );
System.out.println ( "Balance of account " + accno + " is: " + bal );
}
else if ( op.equals ( "owner" ) ) {
String owner = getOwner ( accno );
System.out.println ( "Owner of account " + accno + " is: " + owner );
}
else {
//get amount
if ( args.length< 3 ) {
System.err.println ( "Missing argument: amount." );
System.exit ( 1 );
}
int amount = new Integer ( args[2] ).intValue();
if ( op.equals ( "withdraw" ) )
withdraw ( accno , amount );
else withdraw ( accno , amount * (-1) );
}

shutdown();

}
catch ( Exception e ) {
e.printStackTrace();
}

Example 2: DataSource JDBC Integration


The second example manipulates a bank account database (in a Cloudscape embedded RDBMS) with TransactionsJTA and
Atomikos connection pools.

package examples.jdbc.pooled;

import javax.transaction.*;
import javax.transaction.xa.*;
import javax.sql.*;
import java.sql.*;

import com.atomikos.icatch.*;
import com.atomikos.icatch.standalone.*;
import com.atomikos.datasource.*;
import com.atomikos.datasource.xa.*;
import com.atomikos.jdbc.*;

import COM.cloudscape.core.DataSourceFactory;

/**
*Copyright © 2002, Guy Pardon. All rights reserved.
*
*A simple program that uses JDBC-level integration with
*TransactionsJTA. Although only one database is
*accessed, it shows all important steps for programming
*with TransactionsJTA.
*
*Usage: java PooledAccount account_number operation [amount]

*where:
*account number is an integer between 0 and 99

*and operation is one of (balance, owner, withdraw, deposit).

*In case of withdraw and deposit, an extra (integer) amount is expected.


*/

public class PooledAccount


{
//the user name in the database; change if needed.
//the current settings are empty strings, for
//cloudscape embedded database without
//authentication
private static String user = "";

//the password for the user; change if needed.


//the current settings are empty strings,
//for cloudscape embedded database
private static String passwd = "";

//the unique resource name for the DB; change if needed


private static String resourceName = "ExamplesJdbcPooledDatabase";

//the xa data source, needed in shutdown


private static XADataSource xads = null;

//the data source, set by getDataSource


private static DataSource ds = null;
/**
*Initialize the TM, and setup DB tables if needed.
*/

private static void setup()


throws Exception
{
//STEP 1: Cloudscape-specific setup of XADataSource
xads = DataSourceFactory.getXADataSource();
COM.cloudscape.core.AbstractDataSource ads =
( COM.cloudscape.core.AbstractDataSource ) xads;
ads.setDatabaseName ( resourceName );
ads.setCreateDatabase ( "create" );

//STEP 2: setup the DataSource


XAConnectionFactory factory =
new XAConnectionFactory ( resourceName , "" , "" , xads );
ds = new JtaDataSourceImp ( factory , 1 , 30 , 30 );

//STEP 3: get the TSInitInfo and register the resource


TSInitInfo info = StandAloneConfiguration.createTSInitInfo();
info.registerResource ( factory.getTransactionalResource() );

//STEP 4: get the UserTransactionService and initialize


UserTransactionService uts =
StandAloneConfiguration.getUserTransactionService();
uts.init ( info );

//From here on, the transaction service is running...


//Below is application-specific setup

TransactionManager tm = uts.getTransactionManager();
tm.setTransactionTimeout ( 60 );

boolean error = false;


Connection conn = null;
try {
conn = getConnection();
Statement s = conn.createStatement();
try {
s.executeQuery ( "select * from Accounts" );
}
catch ( SQLException ex ) {
//table not there = > create it
System.err.println ( "Creating Accounts table..." );
s.executeUpdate ( "create table Accounts ( " +
" account VARCHAR ( 20 ), owner VARCHAR(300), balance DECIMAL (19,0) )" );
for ( int i = 0; i< 100 ; i++ ) {
s.executeUpdate ( "insert into Accounts values ( " +
"'account"+i +"' , 'owner"+i +"', 10000 )" );
}
}
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( conn , error );

//That concludes setup

/**
*Shutdown the TM and the datasource.
*/

private static void shutdown()


throws Exception
{
StandAloneConfiguration.getUserTransactionService().
shutdown ( true );

//clean up the connection pool


( ( JtaDataSourceImp ) getDataSource() ).close();

//Cloudscape-specific shutdown procedure


//for embedded database
COM.cloudscape.core.AbstractDataSource ds =
( COM.cloudscape.core.AbstractDataSource ) xads;
ds.setShutdownDatabase ( "shutdown" );
try {
xads.getXAConnection().getConnection();
}
catch ( Exception e ) {
//exception seems to be inevitable for clean shutdown
//of cloudscape embedded DB...
}

//
//THE CODE BELOW SHOULD NOT BE CHANGED;
//IT SHOULD WORK ON ANY JDBC COMPLIANT SYSTEM
//
/**
*Gets the xa datasource instance.
*
*@return XADataSource The xa data source.
*/

private static XADataSource getXADataSource()


throws Exception
{
return xads;
}

private static DataSource getDataSource()


{
return ds;
}

/**
*Utility method to start a transaction and
*get a connection.
*This method should be called within a transaction.
*@return Connection The connection.
*/

private static Connection getConnection()


throws Exception
{
DataSource ds = getDataSource();
Connection conn = null;

//First, create a transaction


TransactionManager tm = getTransactionManager();
tm.begin();

conn = ds.getConnection();

return conn;

/**
*Utility method to close the connection and
*terminate the transaction.
*This method does all XA related tasks
*and should be called within a transaction.
*When it returns, the transaction will be terminated.
*@param conn The connection.
*@param error Indicates if an error has
*occurred or not. If true, the transaction will be rolled back.
*If false, the transaction will be committed.
*/

private static void closeConnection ( Connection conn , boolean error )


throws Exception
{
conn.close();

//terminate the transaction


TransactionManager tm = getTransactionManager();
if ( error )
tm.rollback();
else
tm.commit();

private static TransactionManager getTransactionManager()


{
return StandAloneConfiguration.getUserTransactionService().
getTransactionManager();
}

private static long getBalance ( int account )


throws Exception
{
long res = -1;
boolean error = false;
TransactionManager tm = getTransactionManager();
Connection conn = null;

try {
conn = getConnection();
Statement s = conn.createStatement();
String query = "select balance from Accounts where account='"
+"account"+account+"'";
ResultSet rs = s.executeQuery ( query );
if ( rs == null || !rs.next() )
throw new Exception ( "Account not found: " + account );
res = rs.getLong ( 1 );
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( conn , error );
}
return res;
}

private static String getOwner ( int account )


throws Exception
{
String res = null;
boolean error = false;
TransactionManager tm = getTransactionManager();
Connection conn = null;

try {
conn = getConnection();
Statement s = conn.createStatement();
String query = "select owner from Accounts where account='account"
+ account+"'";
ResultSet rs = s.executeQuery ( query );
if ( rs == null || !rs.next() )
throw new Exception ( "Account not found: " +account );
res = rs.getString ( 1 );
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( conn , error );
}
return res;
}

private static void withdraw ( int account , int amount )


throws Exception
{
boolean error = false;
TransactionManager tm = getTransactionManager();
Connection conn = null;

try {
conn = getConnection();
Statement s = conn.createStatement();
String sql = "update Accounts set balance = balance - "
+ amount + " where account ='account"+account+"'";
s.executeUpdate ( sql );
s.close();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
closeConnection ( conn , error );

public static void main ( String[] args )


{
try {
//test if DB data has to be created
setup();

if ( args.length < 2 || args.length > 3 ) {


System.err.println (
"Arguments required: account_number operation [amount]" );
System.exit ( 1 );
}

//get account number


int accno = new Integer ( args[0] ).intValue();
if ( accno < 0 || accno > 99 ) {
System.err.println (
"Account number should be between 0 and 99." );
System.exit ( 1 );
}

//get operation
String op = args[1];

if ( op.equals ( "balance" ) ) {
long bal = getBalance ( accno );
System.out.println ( "Balance of account " + accno + " is: " + bal );
}
else if ( op.equals ( "owner" ) ) {
String owner = getOwner ( accno );
System.out.println ( "Owner of account " + accno + " is: " + owner );
}
else {
//get amount
if ( args.length < 3 ) {
System.err.println ( "Missing argument: amount." );
System.exit ( 1 );
}
int amount = new Integer ( args[2] ).intValue();
if ( op.equals ( "withdraw" ) )
withdraw ( accno , amount );
else withdraw ( accno , amount * (-1) );
}

shutdown();

}
catch ( Exception e ) {
e.printStackTrace();
}

}
Example 3: XA-Level JMS Integration
The third example application is a JMS-based program with XA-Level integration with TransactionsJTA. It assumes a
SONICMQ installation is present on your system.

package examples.jms.xa;

import javax.transaction.*;
import javax.transaction.xa.*;
import javax.jms.*;

import com.atomikos.icatch.*;
import com.atomikos.icatch.standalone.*;
import com.atomikos.datasource.*;
import com.atomikos.datasource.xa.*;
import com.atomikos.datasource.xa.jms.*;

import progress.message.jclient.QueueConnectionFactory;

/**
*Copyright © 2002, Guy Pardon. All rights reserved.
*
*An example of a queue application using XA integration with
*TransactionsJTA. Although only one queue is accessed, all the
*essential operations are shown.
*
*/

public class XaQueue


{
//the broker name of the SONIC MQ broker
private static String BROKER_NAME = "localhost:2506";

//user to connect to queue mgr with; change if needed.


private static String USER = "Administrator";

//password to connect to queue mgr with; change if needed.


private static String PASSWORD = "Administrator";

//the name of the queue to use; change if needed.


private static String QUEUE_NAME = "SampleQ1";

//the queue connection in use.


private static XAQueueConnection xaqconn;

//the queue session


private static XAQueueSession xasession;

//the queue to use


private static Queue queue;

private static void setup()


throws Exception
{
//STEP 1: get the vendor-specific XAQueueConnectionFactory
XAQueueConnectionFactory qf =
new progress.message.jclient.xa.
XAQueueConnectionFactory ( BROKER_NAME );

//STEP 2: create the resource to register with


//the transaction service
JmsTransactionalResource res =
new JmsTransactionalResource ( "ExampleJmsXaResource" , qf );
//set the resource in weak compare mode; otherwise the
//integration with SONICMQ will not work!
res.useWeakCompare ( true );

//STEP 3: get the TSInitInfo and register the resource


TSInitInfo info =
StandAloneConfiguration.createTSInitInfo();
info.registerResource ( res );
//STEP 4: get the UserTransactionService and initialize.
UserTransactionService uts =
StandAloneConfiguration.getUserTransactionService();
uts.init ( info );

//what is left is some application-specific setup


xaqconn = qf.createXAQueueConnection ( USER , PASSWORD );
xaqconn.start();
xasession = xaqconn.createXAQueueSession();
queue = xasession.getQueueSession().createQueue ( QUEUE_NAME );
uts.getTransactionManager().setTransactionTimeout ( 59 );
}

private static void shutdown()


throws Exception
{
xasession.close();
xaqconn.close();
StandAloneConfiguration.getUserTransactionService().
shutdown ( true );
}

private static Queue getQueue()


throws Exception
{
return queue;
}

/**
*Get the transaction manager.
*@return TransactionManager The tm.
*/

private static TransactionManager getTransactionManager()


{
return StandAloneConfiguration.getUserTransactionService().
getTransactionManager();
}

/**
*Utility method to process XA operations
*before doing send or receive. Also starts a transaction.
*@return XAQueueSession The xa queue session.
*
*/

private static XAQueueSession getXAQueueSession()


throws Exception
{
TransactionManager tm = getTransactionManager();
tm.begin();
Transaction tx = tm.getTransaction();
tx.enlistResource ( xasession.getXAResource() );
return xasession;
}

/**
*Utility method to process XA operations at end of send/receive.
*This method also terminates the transaction.
*@param session The XA session.
*@param error True iff rollback required.
*/

private static void giveXAQueueSession (


XAQueueSession session , boolean error )
throws Exception
{
int flag = XAResource.TMSUCCESS;
if ( error )
flag = XAResource.TMFAIL;
TransactionManager tm = getTransactionManager();
Transaction tx = tm.getTransaction();
tx.delistResource ( xasession.getXAResource() , flag );
if ( error )
tm.rollback();
else
tm.commit();
}

/**
*Receive from queue.
*@return String The text of the message found.
*/

private static String receive()


throws Exception
{
boolean error = false;
String ret = null;
XAQueueSession xasess = null;
try {
xasess = getXAQueueSession();
QueueSession session = xasess.getQueueSession();

QueueReceiver receiver = session.createReceiver ( getQueue() );

TextMessage msg = ( TextMessage ) receiver.receive ( 1000 );


if ( msg != null )
ret = msg.getText();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
giveXAQueueSession ( xasess , error );
}
return ret;
}

/**
*Send to the queue.
*@param text The text of the message to be sent.
*/

private static void send ( String text )


throws Exception
{
boolean error = false;
XAQueueSession xasess = null;
try {
xasess = getXAQueueSession();
QueueSession session = xasess.getQueueSession();
QueueSender sender = session.createSender ( getQueue() );
TextMessage msg = session.createTextMessage();
msg.setText ( text );
sender.send (
msg , DeliveryMode.PERSISTENT,
Message.DEFAULT_PRIORITY , 0 );

}
catch ( Exception e ) {
e.printStackTrace();
error = true;
throw e;
}
finally {
giveXAQueueSession ( xasess , error );
}
}

public static void main ( String[] args )


{
try {
//initialize transaction service and queue
setup();

//check arguments
if ( args.length == 0 ) {
System.err.println ( "Arguments required: operation [message]" );
System.err.println ( "where operation is send or receive" );
System.err.println ( "and message is the text in case of send" );
System.exit ( 1 );
}

String operation = args[0];


if ( operation.equals ( "send" ) ) {
String text = args[1];
send ( text );

}
else if ( operation.equals ( "receive" ) ) {
String text = receive();
System.out.println ( "Received text: " + text );
}
else {
System.out.println ( "Unknown operation: " + operation );
}

//perform clean shutdown


shutdown();
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}

Example 4: Adapter-Level JMS Integration


The following example shows a simple application that uses TransactionsJTA to manage transactions that access a JMS
queue in SONICMQ. It uses the adapter approach for JMS integration.

package examples.jms.adapter;

import javax.transaction.*;
import javax.transaction.xa.*;
import javax.jms.*;

import com.atomikos.icatch.*;
import com.atomikos.icatch.standalone.*;
import com.atomikos.datasource.*;
import com.atomikos.datasource.xa.*;
import com.atomikos.datasource.xa.jms.*;

import progress.message.jclient.QueueConnectionFactory;

/**
*Copyright © 2002, Guy Pardon. All rights reserved.
*
*An example of a queue application using adapter integration with
*TransactionsJTA. Although only one queue is accessed, all the
*essential operations are shown.
*
*/

public class AdapterQueue


{
//the broker name of the SonicMQ broker
private static String BROKER_NAME = "localhost:2506";

//user to connect to queue mgr with; change if needed.


private static String USER = "Administrator";
//password to connect to queue mgr with; change if needed.
private static String PASSWORD = "Administrator";

//the name of the queue to use; change if needed.


private static String QUEUE_NAME = "SampleQ1";

//the queue connection in use.


private static QueueConnection qconn;

//the queue session


private static QueueSession session;

//the queue to use


private static Queue queue;

/**
*The adapter integration setup.
*/

private static void setup()


throws Exception
{
//STEP 1: get the vendor-specific XAQueueConnectionFactory
XAQueueConnectionFactory qf =
new progress.message.jclient.xa.
XAQueueConnectionFactory ( BROKER_NAME );

//STEP 2: construct an Atomikos adapter class


JtaQueueConnectionFactory jtaqf =
new JtaQueueConnectionFactory (
"ExampleJmsAdapterResource" , qf );

//STEP 3: get the resource to register with the transaction service


JmsTransactionalResource res =
jtaqf.getTransactionalResource();
//set the resource in weak compare mode; otherwise the
//integration with SONICMQ will not work!
res.useWeakCompare ( true );

//STEP 4: get the TSInitInfo and register the resource


TSInitInfo info =
StandAloneConfiguration.createTSInitInfo();
info.registerResource ( res );

//STEP 5: get the UserTransactionService and initialize (start) it.


UserTransactionService uts =
StandAloneConfiguration.getUserTransactionService();
uts.init ( info );

//do some application-specific setup


uts.getTransactionManager().setTransactionTimeout ( 59 );
qconn = jtaqf.createQueueConnection ( USER , PASSWORD );
qconn.start();
session = qconn.createQueueSession ( true , 0 );
queue = session.createQueue ( QUEUE_NAME );
}

/**
*Perform shutdown.
*/

private static void shutdown()


throws Exception
{
session.close();
qconn.close();
StandAloneConfiguration.getUserTransactionService().
shutdown ( true );
}

private static Queue getQueue()


throws Exception
{
return queue;
}

/**
*Get the transaction manager.
*@return TransactionManager The tm.
*/

private static TransactionManager getTransactionManager()


{
return StandAloneConfiguration.getUserTransactionService().
getTransactionManager();
}

/**
*Utility method that get the session and starts a transaction.
*@return QueueSession The queue session.
*
*/

private static QueueSession getQueueSession()


throws Exception
{
TransactionManager tm = getTransactionManager();
tm.begin();

return session;
}

/**
*Utility method to process operations at end of send/receive.
*Terminates the transaction.
*@param session The session.
*@param error True iff rollback required.
*/

private static void giveQueueSession (


QueueSession session , boolean error )
throws Exception
{
TransactionManager tm = getTransactionManager();
if ( error )
tm.rollback();
else
tm.commit();
}

/**
*Receive from queue.
*@return String The text of the message found.
*/

private static String receive()


throws Exception
{
boolean error = false;
String ret = null;
QueueSession session = null;
try {
session = getQueueSession();
QueueReceiver receiver = session.createReceiver ( getQueue() );

TextMessage msg = ( TextMessage ) receiver.receive ( 1000 );


if ( msg != null )
ret = msg.getText();
}
catch ( Exception e ) {
error = true;
throw e;
}
finally {
giveQueueSession ( session , error );
}
return ret;
}

/**
*Send to the queue.
*@param text The text of the message to be sent.
*/

private static void send ( String text )


throws Exception
{
boolean error = false;
QueueSession xasess = null;
try {
session = getQueueSession();
QueueSender sender = session.createSender ( getQueue() );
TextMessage msg = session.createTextMessage();
msg.setText ( text );
sender.send (
msg , DeliveryMode.PERSISTENT,
Message.DEFAULT_PRIORITY , 0 );
}
catch ( Exception e ) {
e.printStackTrace();
error = true;
throw e;
}
finally {
giveQueueSession ( session , error );
}
}

public static void main ( String[] args )


{
try {
//initialize transaction service and queue
setup();
//check arguments
if ( args.length == 0 ) {
System.err.println ( "Arguments required: < operation > [< message > ]" );
System.err.println ( "where < operation > is send or receive" );
System.err.println ( "and < message > is the text in case of send" );
System.exit ( 1 );
}
String operation = args[0];
if ( operation.equals ( "send" ) ) {
String text = args[1];
send ( text );
}
else if ( operation.equals ( "receive" ) ) {
String text = receive();
System.out.println ( "Received text: " + text );
}
else {
System.out.println ( "Unknown operation: " + operation );
}
//perform clean shutdown
shutdown();
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}

An Example of a Custom Resource


This section illustrates how to support new types of XAResource instances (i.e. those that do not correspond to JDBC
databases or JMS queues). The example source code is that of the JmsTransactionalResource included in the
TransactionsJTA. Note that the actual resource-specific work is concentrated in the refreshXAConnection method. If you
have a specific XAResource type that is not readily supported by a TransactionalResource class in the TransactionsJTA
distribution then you can use this example to program your own implementation.

package com.atomikos.datasource.xa.jms;
import com.atomikos.datasource.*;
import com.atomikos.datasource.xa.*;
import javax.transaction.*;
import javax.transaction.xa.*;
import java.util.*;
import javax.jms.*;
/**
*A transactional resource implementation for JMS queues.
*
*Copyright © 2002, Guy Pardon, Atomikos. All rights reserved.
*/

public class JmsTransactionalResource


extends XATransactionalResource
{
//keep the connection factory for re-opening
//the connection when needed
private XAQueueConnectionFactory factory_;

//keep the last connection that was opened


private XAQueueConnection conn_;

/**
*Create a new instance.
*@param name The unique resource name.
*@param factory The xa connection factory to use.
*/

public JmsTransactionalResource (
String name ,
XAQueueConnectionFactory factory )
{
super ( name );
factory_ = factory;
conn_ = null;
}

/**
*Create a new instance, but one that
*requires a specific Xid format. This may be
*necessary for some JMS implementations that require
*their own format.
*@param name The unique resource name.
*@param qFactory The queue connection factory.
*@param xidFactory The factory for Xid instances.
*/

public JmsTransactionalResource (
String name ,
XAQueueConnectionFactory qFactory,
XidFactory xidFactory )
{
super ( name , xidFactory );
factory_ = qFactory;
conn_ = null;
}

/**
*Implements the functionality to get an XAResource
*handle. This method is called by the superclass
*whenever necessary. It is the most important method of
*this class.
*@return XAResource The XAResource instance.
*/

protected synchronized XAResource refreshXAConnection()


throws ResourceException
{
XAResource res = null;

//Close any open connection, since this connection


//is likely to have timed out (otherwise there would
//be no reason to refresh the XAResource in the
//first place).
//Note: on the first invocation (at creation time)
//there is no open connection yet, so conn_ is null.

if ( conn_ != null ) {
try {
conn_.close();
}
catch ( Exception err ) {
//happens if connection has timed out
//which is probably the case
}
}

try {
//JMS-specific way of obtaining an XAResource
//instance that connects to the back-end JMS system

conn_ = factory_.createXAQueueConnection();
XAQueueSession session = conn_.createXAQueueSession();

//note: the session does not have to be kept in an attribute,


//since JMS explicitly states that closing the connection
//also closes all sessions.

res = session.getXAResource();
}
catch ( JMSException jms ) {
Stack errors = new Stack();
errors.push ( jms );
throw new ResourceException (
"Error in getting XA resource" , errors );
}

return res;

/**
*Overrides default close to include closing any open
*connections to the Queue infrastructure.
*/

public void close() throws ResourceException


{
super.close();
try {
if ( conn_ != null )
conn_.close();
}
catch ( JMSException err ) {
throw new ResourceException ( err.getMessage() );
}
}

8. Appendix C: Getting More out of TransactionsJTA


There are some Atomikos-specific features that allow you to get more out of using TransactionsJTA. Most of them are
related to heuristic problem cases, and information that is available to resolve them. An explanation of these features
follows next.

The HeuristicMessage Interface


One of the patent-pending features of Atomikos is the ability to include application-level comments in the transaction logs.

Heuristic exceptions can be well-documented with their effects on the business-level: if you add an application-level
comment for each interaction with a data source or JMS queue, then the occurrence of a heuristic exception will allow the
corresponding comments to be retrieved from the logs. The result is that heuristic exceptions include their application-level
effects, thereby easing administration. The comment's interface type is discussed in this section; the later sections show
how to add them to the interactions (and to the logs).

The figure above shows the


basic interface for the kind of comments we mentioned. The interface is com.atomikos.diagnostics.HeuristicMessage and
the supplied implementation class is com.atomikos.diagnostics.StringHeuristicMessage. Instances of this implementation
class can be used to document interactions through JDBC or JMS. The string content will be saved in the logs along with
the transaction log information.

JDBC: The HeuristicDataSource Interface


For JDBC interactions, the heuristic messages can be added at the time
of getting a connection from a com.atomikos.jdbc.JtaDataSourceImp. That is possible because the JtaDataSourceImp
not only implements the interface javax.sql.DataSource, but also the Atomikos interface
com.atomikos.jdbc.HeuristicDataSource, shown below.

Instead of merely getting a connection from a JtaDataSourceImp (as done with regular DataSource instances), you can use
the getConnection method with one extra parameter that supplies a HeuristicMessage. This will add the message to the logs
in case of JDBC.
You have to use the JDBC with Atomikos connection pools for this approach to work.

JMS: The HeuristicQueueSender Interface


For JMS send operations, the addition of a heuristic message is possible because the QueueSender instances that you create
through the adapter classes are actually of type com.atomikos.datasource.xa.jms.HeuristicQueueSender.

By performing a cast to this interface upon calling QueueSession.createSender(queue) you can gain access to the extra
functionality.

You have to use the JMS with Atomikos' JMS adapters for this approach to work.

JMS: The HeuristicQueueReceiver Interface


For JMS receive operations, the addition of a heuristic message is possible because the QueueReceiver instances that you
create through the adapter classes are also of type com.atomikos.datasource.xa.jms.HeuristicMessageConsumer.

By performing a cast to this interface upon calling QueueSession.createReceiver(queue) you can gain access to the extra
functionality.

You have to use the JMS with Atomikos' JMS adapters for this approach to work.

Adding and Using a LogAdministrator


The interface com.atomikos.icatch.admin.LogAdministrator is the main interface for administration. It allows
inspection of log entries: what active transactions are in their two-phase commit and what are the heuristic application-level
comments for heuristic problems?

There is a built-in implementation of this interface: class com.atomikos.icatch.admin.LocalLogAdministrator. If you


use this class, then a GUI window will allow you to inspect the transaction logs. All you need to do is register an instance
with the TSInitInfo instance before you initialize the transaction service. The following code fragment illustrates this.

!..
!//create a log administrator with given title and (in our case)
!//indicate that it is not running as the main window
!//of the application
!LocalLogAdministrator admin =
!!new LocalLogAdministrator ( "Transaction Administrator" , false );
!info.registerLogAdministrator ( admin );
!uts.init ( info );
!...

Viewing Active Transactions

Through the menu item labeled Show Active Transactions... you can create a view window that shows the transactions
that are in their 2-phase-commit at that moment. There are a few important things to notice about this window:

Transactions created after this window was opened will not be shown. This is to prevent high throughput from
causing many screen updates and thereby cluttering the overview.
If any transactions appear, then their states will be refreshed periodically (about every 5-10 seconds).
For lower throughputs, this window will usually be empty. Only problematic transactions are likely to show up,
because the two-phase commits happen in a relatively short time span.

Running as the Main Window

If you are running the administrator as the main window of your application, then choosing Exit from the main menu will
also shutdown the transaction service for you.

If you are using an instance of LocalLogAdministrator that is not the main window (as indicated in the constructor
arguments) then your application will not exit unless you explicitly call the System.exit() method. This is normal
behaviour caused by the Swing event thread that will keep runnig even after you have called shutdown.

JTA UserTransaction
The TransactionsJTA release also comes with a UserTransaction
implementation: com.atomikos.icatch.jta.UserTransactionImp. This class has a public no-argument constructor and is
referenceable through JNDI.

9. Appendix D: Troubleshooting
This appendix contains some help on problems that you might encounter when using TransactionsJTA. The set of problems
here is fairly 'stable' in that they are not bugs but merely consequences of using TransactionsJTA in a way it is not intended
to. Usually there is an easy solution. If you have a problem not listed here then please contact support@atomikos.com.

Timeout of Active Transaction


Problem Description

During the execution of a transaction, a delistResource or another operation that accesses the transaction (such as a send/
receive in JMS or a getConnection/close in JDBC) fails with a message saying (among other things):
Wrong state for addParticipant: TERMINATED

Explanation

The work you were doing inside the transaction has taken too long, and the transaction has been rolled back in the
meantime.

Suggestion

Increase the transaction timeout by calling the TransactionManager's setTransactionTimeout method. Do this before you
start the transaction.

Epoch Overflow
Problem Description
The transaction service throws a RuntimeException with the following message:
Epoch overflow!

Explanation

Each transaction needs to have a unique identifier. The epoch is an essential counter that ensures this uniqueness; if it
overflows then it will re-use old values and endanger transaction integrity. The transaction service will not allow this and
therefore throws this exception.

Suggestion

You are probably using an evaluation version of TransactionsJTA, which is limited in the number of times it can be used.
If you want to continue using TransactionsJTA, then you need to purchase a license.

Log Already in Use


Problem Description

During initialization of the transaction service, a runtime exception happens with message:
Log already in use?

Explanation

There should only be one running transaction manager that uses the physical log. Each transaction manager should have its
own log file, at the location specified in the configuration file. Either you have two services that use the same log file, or
the transaction service did not shutdown properly last time it was used.!

Suggestion

Assert that you have no two different servers that use the same log file. Check if no other instance of the server is running.
If the problem is due to improper shutdown, then manually delete the file with extension .lck in the log directory (after
you have made sure that the transaction service is no longer running). You can then restart the service.

HeuristicMixed Exception in 1-Phase Commit/Rollback


Problem Description

One-phase commit/rollback takes a long time and finally an exception of type HeuristicMixedException is thrown.

Explanation

This happens if the transaction service can no longer contact the resource for commitment.

Suggestion

If you are doing XA-Level integration, then make sure that the XAConnection is closed only after the transaction has
committed or rolled back.

Many RollbackException Occurrences


Problem Description

Commit often fails with a RollbackException, on repeated occasions and for transactions that access more than one
resource.

Explanation

This can happen if the transaction service can not contact one of the resources for two-phase commit.

Suggestion
If you are doing XA-Level integration, then make sure that the XAConnections are closed only after the transaction has
committed or rolled back.

Already Enlisted Error


Problem Description

During enlistResource or in a getConnection/send/receive operation an error happens with a message saying something like:
Already enlisted.

Explanation

TransactionsJTA does not allow the same XAResource to be enlisted twice in one transaction without a delistResource in
between. This error will also happen if you try to open two connections to the same DataSource without closing one first.

Suggestion

When enlisting a second XAResource to the same underlying system, serialize the enlistResource/delistResource
operations: do not enlist a second time before delisting the first XAResource. For a DataSource, always close a connection
before opening a second one.

Initialization and WeakCompare


Problem Description

Initialization fails after you have registered multiple resources in TSInitInfo, some of which are in weak compare mode.

Explanation

To preserve correctness of recovery, only one resource per vendor can be added in weak compare mode.

Suggestion

Do not include more than one resource of the same vendor in weak compare mode.

Unknown Resource
Problem Description

During enlistResource, send, receive or getConnection operations there is an exception with a message saying Unknown
resource.

Explanation

The resource could not be found in the transaction service's configuration.

Suggestion

Make sure that the resource is included in the configuration. If so, then you might need to use the resource in weak
compare mode. See the JMS examples on how to do this.

Application Not Exiting


Problem Description

The VM does not exit when I use a LocalLogAdministrator.

Explanation

The VM does not exit because a Swing event thread is still running. This is normal behaviour.
Suggestion

Call System.exit() if you want a Swing application to exit. Since the Transaction Service's LocalLogAdministrator does
not know when your application needs to exit, it can not do this for you.

10. References
http://java.sun.com Sun's Java website with the JTA, JDBC and JMS specifications and extra information.
http://www.atomikos.com Atomikos' website; please check regularly for updates and support information.
Distributed Transaction Processing: The XA Specification (ISBN 1-872630-24-3).
Published by The Open Group (http://www.opengroup.org).

Anda mungkin juga menyukai