Narayana implements JTA specification in Java. It's flexible and easy to be integrated to any system
which desires transaction capabilities. As proof of the Narayana extensibility check our quickstarts like
Spring Boot one
or Camel one.
But this blogpost is different integration effort. It talks in details about Narayana integration with Apache Tomcat server.
If you do not care about details then just jump directly to the Narayana quickstarts in this area and use the code there for yourself.
If you want more detailed understanding read further.
All the discussed abilities are considered as the state of Narayana 5.8.1.Final or later.
Narayana, database resources and JDBC interface
All the proclaimed Narayana capabilities to integrate with other systems come
from requirements for the system to conform with the JTA specification.
JTA expects manageable resources which follows XA specification in particular.
For case of the database resources the underlaying API is defined by JDBC specification.
JDBC assembled resources manageable by transaction manager under package javax.sql
It defines interfaces used for managing
XA capabilities. The probably most noticable is XADataSource
which serves as factory for XAConnection.
From there we can otain XAResource.
The XAResource
is interface that the transaction manager works with. The instance of it
participates in the two phase commit.
The workflow is to get or create the XADataSource
, obtains XAConnection
and as next the XAResource
which is enlisted to the global transaction (managed by a transaction manager).
Now we can call queries or statements through the XAConnection
. When all the business work is finished the global transaction is commanded to commit which is propagated to call the commit on each enlisted XAResource
s.
It's important to mention that developer is not expected to do all this
(getting xa resources, enlisting them to transaction manager…)
All this handling is responsibility of the "container" which could be WildFly,
Spring or Apache Tomcat in our case.
Normally the integration which ensures the database XAResource
is enlisted
to the transaction is provided by some pooling library. By the term pooling library
we means code that manages a connection pool with capability
enlisting database resource to the transaction.
We can say at the high level that integration parts are
- the Apache Tomcat container
- Narayana library
- jdbc pooling library
In this article we will talk about Narayana JDBC transactional driver, Apache Commons DBCP and IronJacamar.
Narayana configuration with Tomcat
After the brief overview of integration requirements, let's elaborate on
common settings needed for any integration approach you choose.
Be aware that each library needs a little bit different configuration and especially IronJacamar is specific.
JDBC pooling libraries integration
Narayana provides integration code in maven module
tomcat-jta
.
That contains the glue code which integrates Narayana to the world of the Tomcat.
If you write an application you will need the following:
- providing Narayana itself to the application classpath
- providing Narayana
tomcat-jta
module to the application classpath - configure
WEB-INF/web.xml
withNarayanaJtaServletContextListener
which ensures the intialization of Narayana transaction manager - add
META-INF/context.xml
which setup Tomcat to start using implementation of JTA interfaces provided by Narayana - configure database resources to be XA aware and cooperate with Narayana by setting
them up in the
META-INF/context.xml
NOTE: if you expect to use the IronJacamar this requirements differs a bit!
If we take a look at the structure of the jar to be deployed we would get the picture possibly similar to this one:
├── META-INF │ └── context.xml └── WEB-INF ├── classes │ ├── application… │ └── jbossts-properties.xml ├── lib │ ├── arjuna-5.8.1.Final.jar │ ├── jboss-logging-3.2.1.Final.jar │ ├── jboss-transaction-spi-7.6.0.Final.jar │ ├── jta-5.8.1.Final.jar │ ├── postgresql-9.0-801.jdbc4.jar │ └── tomcat-jta-5.8.1.Final.jar └── web.xml
From this summary let's overview the configuration files one by one to see what's needed to be defined there.
Configuration files to be setup for the integration
WEB-INF/web.xml
<web-app>
<listener>
<listener-class>org.jboss.narayana.tomcat.jta.NarayanaJtaServletContextListener</listener-class>
</listener>
</web-app>
The web.xml
needs to define the
NarayanaJtaServletContextListener
to be loaded during context initialization to initialize the Narayana itself.
Narayana needs to get running, for example, reaper thread that ensures transaction timeouts checking or thread of recovery manager.
WEB-INF/clases/jbossts-properties.xml
This file is not compulsory.
The purpose is to configure the Narayana itself.
If you don't use your own configuration file then the default is in charge.
See more at blogpost
Narayana periodic recovery of XA transactions
or consider settings done by the default descriptor
jbossts-properties.xml at narayana-jts-idlj.
META-INF/context.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Context antiJarLocking="true" antiResourceLocking="true">
<!-- Narayana resources -->
<Transaction factory="org.jboss.narayana.tomcat.jta.UserTransactionFactory"/>
<Resource factory="org.jboss.narayana.tomcat.jta.TransactionManagerFactory"
name="TransactionManager" type="javax.transaction.TransactionManager"/>
<Resource factory="org.jboss.narayana.tomcat.jta.TransactionSynchronizationRegistryFactory"
name="TransactionSynchronizationRegistry" type="javax.transaction.TransactionSynchronizationRegistry"/>
<Resource auth="Container" databaseName="test" description="Data Source"
factory="org.postgresql.xa.PGXADataSourceFactory" loginTimeout="0"
name="myDataSource" password="test" portNumber="5432" serverName="localhost"
type="org.postgresql.xa.PGXADataSource" user="test" username="test"
uniqueName="myDataSource" url="jdbc:postgresql://localhost:5432/test"/>
<Resource auth="Container" description="Transactional Data Source"
factory="org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory"
initialSize="10" jmxEnabled="true" logAbandoned="true" maxAge="30000"
maxIdle="16" maxTotal="4" maxWaitMillis="10000" minIdle="8"
name="transactionalDataSource" password="test" removeAbandoned="true"
removeAbandonedTimeout="60" testOnBorrow="true" transactionManager="TransactionManager"
type="javax.sql.XADataSource" uniqueName="transactionalDataSource"
username="test" validationQuery="select 1" xaDataSource="myDataSource"/>
</Context>
I divide explanation this file into two parts. First are the generic settings -
those needed for transaction manager integration (top part of the context.xml
).
The second part is on resource declaration that defines linking to the JDBC pooling library.
Transaction manager integration settings
We define implementation classes for the JTA api here.
The implementation is provided by Narayana transaction manager.
Those are lines of UserTransactionFactory
and
resources of TransactionManager
and TransactionSynchronizationRegistry
in the context.xml
file.
JDBC pooling library settings
We aim to define database resources that can be used in the application.
That's how you get the connection
typically with code DataSource ds = InitialContext.doLookup("java:comp/env/transactionalDataSource")
,
and eventually execute a sql statement.
We define a PostgreSQL datasource with information how to create a new XA connection
(we provide the host and port, credentials etc.) in the example.
The second resource is definition of jdbc pooling library to utilize the PostgreSQL one
and to provide the XA capabilities. It roughtly means putting the PostgreSQL connection
to the managed pool and enlisting the work under an active transaction.
Thus we have got two resources defined here. One is non-managed
(the PosgreSQL one) and the second manages the first one to provide
the ease work with the resources. For the developer is the most important to know
he needs to use the managed one in his application, namely the transactionalDataSource
from our example.
A bit about datasource configuration of Apache Tomcat context.xml
Let's take a side step at this place.
Before we will talk in details about supported pooling libraries
let's check a little bit more about the configuration of the Resource
from perspective of XA connection in the context.xml
.
Looking at the Resource
definition there are highlighted parts which are
interesting for us
<Resource auth="Container" databaseName="test" description="Data Source" factory="org.postgresql.xa.PGXADataSourceFactory" loginTimeout="0" name="myDataSource" password="test" portNumber="5432" serverName="localhost" type="org.postgresql.xa.PGXADataSource" uniqueName="myDataSource" url="jdbc:postgresql://localhost:5432/test" user="test" username="test"/>
name
- defines the name the resource is bound at the container and we can use the jndi lookup to find it by that name in application
factory
- defines what type we will get as the final created
Object
. The factory which we declares here is class which implements interface ObjectFactory and from the provided properties it construct an object.
If we would not define anyfactory
element in the definition then the Tomcat class ResourceFactory is used (see default factory constants). TheResourceFactory
will pass the call to the BasicDataSourceFactory of the dbcp2 library. Here we can see the importantce of thetype
xml parameter which defines what is the object type we want to obtain and the factory normally checks if it's able to provide such (by string equals check usually).
The next step is generation of the object itself where the factory takes each of the properties and tries to applied them.
In our case we use the PGXADataSourceFactory which utilizes some of the properties to create theXADataSource
. serverName
,portNumber
,databaseName
,user
,password
- are properties used by the
object factory
class to get connection from the database
Knowing the name of the properties for the particularObjectFactory
is possibly the most important when you need to configure your datasource. Here you need to checksetters
of the factory implementation.
In case of thePGXADataSourceFactory
we need to go through the inheritance hierarchy to find the properties are saved at BaseDataSource. For our case for the relevant properties areuser name
andpassword
. From theBaseDataSource
we can see the setter for the user name is setUser thus the property name we look for isuser
.
After this side step let's take a look at the setup of the Resource
s
in respect of the used pooling library.
Apache Commons DBCP2 library
Quickstart: https://github.com/jbosstm/quickstart/tree/master/dbcp2-and-tomcat
The best integration comes probably with Apache Common DBCP2 as the library itself is part of the Tomcat distribution (the Tomcat code base uses fork of the project). The XA integration is provided in Apache Tomcat version 9.0.7 and later. There is added dbcp2 package managed which knows how to enlist a resource to XA transaction.
The integration is similar to what we discussed in case of the JDBC transactional driver.
You need to have configured two resources in context.xml
. One is
the database datasource (see above) and other
is wrapper providing XA capabilities.
<Resource name="transactionalDataSource" uniqueName="transactionalDataSource"
auth="Container" type="javax.sql.XADataSource"
transactionManager="TransactionManager" xaDataSource="h2DataSource"
factory="org.jboss.narayana.tomcat.jta.TransactionalDataSourceFactory"/>
The integration is here done over the use of the specific factory
which directly depends on classes from Apache Tomcat org.apache.tomcat.dbcp.dbcp2
package.
The factory ensures the resource being enlisted to the recovery manager as well.
The nice feature is that you can use all the DBCP2 configuration parameters for pooling
as you would used when BasicDataSource
is configured. See the configuration options
and the their meaning at the Apache Commons documentation.
Summary:
- Already packed in the
Apache Tomcat
distribution from version 9.0.7 - Configure two resources in
context.xml
. One is the database datasource, the second is wrapper providing XA capabilities with use of thedbcp2
pooling capabilities integrated withTransactionalDataSourceFactory
.
NOTE: if you consider checking the pool status over JMX calls then DBCP2
comes with
|
Narayana jdbc transactional driver
Quickstart: https://github.com/jbosstm/quickstart/tree/master/transactionaldriver/transactionaldriver-and-tomcat
With this we will get back to other two recent articles about
jdbc transactional driver
and recovery of the transactional driver.
The big advantage of jdbc transactional driver is its tight integration with Narayana.
It's the dependecy of the Narayana tomcat-jta
module which contains all the integration code needed for Narayana working in Tomcat. So if you take the tomcat-jta-5.8.1.Final
you have packed the Narayna integration code and jdbc driver in out-of-the-box working bundle.
Configuration actions
Here we will define two resources in the context.xml
file.
The first one is the database one.
<Resource name="h2DataSource" uniqueName="h2Datasource" auth="Container"
type="org.h2.jdbcx.JdbcDataSource" username="sa" user="sa" password="sa"
url="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1" description="H2 Data Source"
loginTimeout="0" factory="org.h2.jdbcx.JdbcDataSourceFactory"/>
The database one defines data needed for preparation of datasource and creation of the connection. The datasource is not XA aware. We need to add one more layer on top which is transactional JDBC driver here. It wraps the datasource connection within XA capabilities.
<Resource name="transactionalDataSource" uniqueName="transactionalDataSource"
auth="Container" type="javax.sql.DataSource" username="sa" password="sa"
driverClassName="com.arjuna.ats.jdbc.TransactionalDriver"
url="jdbc:arjuna:java:comp/env/h2DataSource" description="Transactional Driver Datasource"
connectionProperties="POOL_CONNECTIONS=false"/>
As we do not define the element factory
then the default one is used
which is org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
.
Unfortunately, this is fine up to the time you need to process some more sophisticated pooling strategies. In this aspect the transactional driver does not play well with the default factory and some further integration work would be needed.
This configuration is nice for having transactionalDataSource
available for the
transactional work. Unfortunately, it's not all that you need to do. You miss here configuration of recovery. You need to tell the recovery manager what is the resource to care of.
You can setup this in jbossts-properties.xml
or maybe easier way to add it
to environment variables of the starting Tomcat,
for example by adding the setup under script $CATALINA_HOME/bin/setenv.sh
You define it with property com.arjuna.ats.jta.recovery.XAResourceRecovery.
-Dcom.arjuna.ats.jta.recovery.XAResourceRecovery1=com.arjuna.ats.internal.jdbc.recovery.BasicXARecovery;abs://$(pwd)/src/main/resources/h2recoveryproperties.xml
You can define whatever number of the resources you need the recovery is aware of.
It's done by adding more numbers
at the end of the property name (we use 1
in the example above).
The value of the property is the class implementing com.arjuna.ats.jta.recovery.XAResourceRecovery
.
All the properties provided to the particular implementation is concatenated after the ;
character.
In our example it's path to the xml descriptor h2recoveryproperties.xml
.
When transactional driver is used then you need to declareBasicXARecovery
as recovery
implementation class and this class needs
connection properties
to be declared in the xml descriptor.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="DB_1_DatabaseUser">sa</entry>
<entry key="DB_1_DatabasePassword">sa</entry>
<entry key="DB_1_DatabaseDynamicClass"></entry>
<entry key="DB_1_DatabaseURL">java:comp/env/h2DataSource</entry>
</properties>
Note: there is option not defining the two resources under context.xml
and use the env property for recovery enlistment. All the configuration
properties are then involved in one properties
file and
transactional driver dynamic class is used.
If interested the working example is at
ochaloup/quickstart-jbosstm/tree/transactional-driver-and-tomcat-dynamic-class.
Summary:
- Already packed in the
tomcat-jta
artifact - Configure two resources in
context.xml
. One is database datasource, the second is transactional datasource wrapped by transactional driver. - Need to configure recovery with env variable setup
com.arjuna.ats.jta.recovery.XAResourceRecovery
while providing xml descriptor with connection parameters
IronJacamar
Quickstart: https://github.com/jbosstm/quickstart/tree/master/jca-and-tomcat
The settings of IronJacamar integration differs pretty much from what we've seen so far. The IronJacamar implements whole JCA specification and it's pretty different beast (not just a jdbc pooling library).
The whole handling and integration is passed to IronJacamar itself.
You don't use tomcat-jta
module at all.
You need to configure all aspects in the IronJacamar xml descriptors
.
Aspects like datasource definition, transaction configuration, pooling definition, up to the jndi binding.
The standalone IronJacamar is needed to be started with command org.jboss.jca.embedded.EmbeddedFactory.create().startup()
where you defines the descriptors to be used.
You can configure it in web.xml
as
ServletContextListener
.
What are descriptors to be defined:
- jdbc-xa.rar
which is resource adapter provided by IronJacamar itself. It needs to be part of the deployment. It's capable to process
ds
files. - ds.xml which defines connecion properties and jndi name binding
- transaction.xml
which configures transaction manager instead of use of the
jbossts-properties.xml
.
Summary: IronJacamar is started as embedded system and process all the handling on its own. Developer needs to provide xml descriptor to set up.
Summary
This article provides the details about configuration of the Narayana when used in Apache Tomcat container. We've seen the three possible libraries to get the integration
working - the Narayana JDBC transactional driver, Apache DBCP2 library
and IronJacamar JCA implementation.
On top of it, the article contains many details about Narayana and Tomcat resource configuration.
If you hesitate what alternative is the best fit for your project then this table can help you
JDBC integration library | When to use |
---|---|
Apache DBCP2 | It's the recommended option when you want to obtain Narayana transaction handling in the Apache Tomcat Integration is done in the Narayana resource factory which ensures easily setting up the datasource and recovery in the one step. |
Narayana transactional jdbc driver | Is good fit when you want to have all parts integrated and covered by Narayana project. It provides lightweight JDBC pooling layer that could be nice for small projects. Integration requires a little bit more hand working. |
IronJacamar | To be used when you need whole JCA functionality running in Apache Tomcat. The benefit of this solution is the battle tested integration of Narayana and IronJacamar as they are delivered as one pack in the WildFly application server. |
1 comment:
Post a Comment