Chapter 2. Getting started with transactions on Karaf (OSGi)
This section describes a Camel application that uses transactions to access an Artemis JMS broker. The information is organized as follows:
2.1. Prerequisites
Implementation of this Camel application has the following prerequisites:
An external AMQ 7 JMS message broker must be running.
The following sample code runs a standalone (non-Docker) version of
amq-broker-7.1.0-bin.zip
. Execution creates and runs anamq7
instance:$ pwd /data/servers/amq-broker-7.1.0 $ bin/artemis create --user admin --password admin --require-login amq7 Creating ActiveMQ Artemis instance at: /data/servers/amq-broker-7.1.0/amq7 Auto tuning journal ... done! Your system can make 27.78 writes per millisecond, your journal-buffer-timeout will be 36000 You can now start the broker by executing: "/data/servers/amq-broker-7.1.0/amq7/bin/artemis" run Or you can run the broker in the background using: "/data/servers/amq-broker-7.1.0/amq7/bin/artemis-service" start $ amq7/bin/artemis run __ __ ____ ____ _ /\ | \/ |/ __ \ | _ \ | | / \ | \ / | | | | | |_) |_ __ ___ | | _____ _ __ / /\ \ | |\/| | | | | | _ <| '__/ _ \| |/ / _ \ '__| / ____ \| | | | |__| | | |_) | | | (_) | < __/ | /_/ \_\_| |_|\___\_\ |____/|_| \___/|_|\_\___|_| Red Hat JBoss AMQ 7.1.0.GA 018-05-02 16:37:19,294 INFO [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server ...
Client libraries are required. Artemis libraries are available in Maven Central or a Red Hat repository. For example, you can use:
-
mvn:org.apache.activemq/artemis-core-client/2.4.0.amq-710008-redhat-1
-
mvn:org.apache.activemq/artemis-jms-client/2.4.0.amq-710008-redhat-1
Alternatively, Artemis/AMQ 7 client libraries can be installed as Karaf features, for example:
-
karaf@root()> feature:install artemis-jms-client artemis-core-client
-
Some supporting features that provide Karaf shell commands or dedicated Artemis support are required:
karaf@root()> feature:install jms pax-jms-artemis pax-jms-config
Required Camel features are:
karaf@root()> feature:install camel-jms camel-blueprint
2.2. Building the camel-jms project
You can download the quickstarts
from the Fuse Software Downloads page.
Extract the contents of the zip file to a local folder, for example a new folder named quickstarts
.
You can then build and install the /camel/camel-jms
example as an OSGi bundle. This bundle contains a Blueprint XML definition of a Camel route that sends messages to an AMQ 7 JMS queue.
In the following example, $FUSE_HOME
is the location of the unzipped Fuse distribution. To build this project:
Invoke Maven to build the project:
$ cd quickstarts $ mvn clean install -f camel/camel-jms/
Create a JMS connection factory configuration so that the
javax.jms.ConnectionFactory
service is published in the OSGi runtime. To do this, copyquickstarts/camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg
into the$FUSE_HOME/etc
directory. This configuration will be processed to create a working connection factory. For example:$ cp camel/camel-jms/src/main/resources/etc/org.ops4j.connectionfactory-amq7.cfg ../etc/
Verify the published connection factory:
karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- felix.fileinstall.filename = file:$FUSE_HOME/etc/org.ops4j.connectionfactory-amq7.cfg name = artemis osgi.jndi.service.name = artemis password = admin pax.jms.managed = true service.bundleid = 251 service.factoryPid = org.ops4j.connectionfactory service.id = 436 service.pid = org.ops4j.connectionfactory.d6207fcc-3fe6-4dc1-a0d8-0e76ba3b89bf service.scope = singleton type = artemis url = tcp://localhost:61616 user = admin Provided by : OPS4J Pax JMS Config (251) karaf@root()> jms:info -u admin -p admin artemis Property │ Value ─────────┼────────────────────────── product │ ActiveMQ version │ 2.4.0.amq-711002-redhat-1 karaf@root()> jms:queues -u admin -p admin artemis JMS Queues ──────────────────────────────────── df2501d1-aa52-4439-b9e4-c0840c568df1 DLQ ExpiryQueue
Install the bundle:
karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/7.0.0.redhat-SNAPSHOT Bundle ID: 256
Confirm that it is working:
karaf@root()> camel:context-list Context Status Total # Failed # Inflight # Uptime ------- ------ ------- -------- ---------- ------ jms-example-context Started 0 0 0 2 minutes karaf@root()> camel:route-list Context Route Status Total # Failed # Inflight # Uptime ------- ----- ------ ------- -------- ---------- ------ jms-example-context file-to-jms-route Started 0 0 0 2 minutes jms-example-context jms-cbr-route Started 0 0 0 2 minutes
-
As soon as the Camel routes have started, you can see a directory,
work/jms/input
, in your Fuse installation. Copy the files you find in this quickstart’ssrc/main/data
directory to the newly createdwork/jms/input
directory. Wait a few moments and you will find the same files organized by country under the
work/jms/output
directory:-
order1.xml
,order2.xml
andorder4.xml
inwork/jms/output/others
-
order3.xml
andorder5.xml
inwork/jms/output/us
-
order6.xml
inwork/jms/output/fr
-
See the logs to check out the business logging:
2018-05-02 17:20:47,952 | INFO | ile://work/jms/input | file-to-jms-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Receiving order order1.xml 2018-05-02 17:20:48,052 | INFO | umer[incomingOrders] | jms-cbr-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Sending order order1.xml to another country 2018-05-02 17:20:48,053 | INFO | umer[incomingOrders] | jms-cbr-route | 58 - org.apache.camel.camel-core - 2.21.0.fuse-000077 | Done processing order1.xml
See that the queue was dynamically created:
karaf@root()> jms:queues -u admin -p admin artemis JMS Queues ──────────────────────────────────── DLQ 17767323-937f-4bad-a403-07cd63311f4e ExpiryQueue incomingOrders
Check Camel route statistics:
karaf@root()> camel:route-info jms-example-context file-to-jms-route Camel Route file-to-jms-route Camel Context: jms-example-context State: Started State: Started Statistics Exchanges Total: 1 Exchanges Completed: 1 Exchanges Failed: 0 Exchanges Inflight: 0 Min Processing Time: 67 ms Max Processing Time: 67 ms Mean Processing Time: 67 ms Total Processing Time: 67 ms Last Processing Time: 67 ms Delta Processing Time: 67 ms Start Statistics Date: 2018-05-02 17:14:17 Reset Statistics Date: 2018-05-02 17:14:17 First Exchange Date: 2018-05-02 17:20:48 Last Exchange Date: 2018-05-02 17:20:48
2.3. Explanation of the camel-jms project
Camel routes are using the following endpoint URIs:
<route id="file-to-jms-route"> ... <to uri="jms:queue:incomingOrders?transacted=true" /> </route> <route id="jms-cbr-route"> <from uri="jms:queue:incomingOrders?transacted=true" /> ... </route>
The jms
component is configured by using this snippet:
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory"> <reference interface="javax.jms.ConnectionFactory" /> </property> <property name="transactionManager" ref="transactionManager"/> </bean>
While the transactionManager
reference is:
<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
As you can see, both the JMS connection factory and the Spring interface of PlatformTransactionManager
are only references. There is no need to define them in Blueprint XML. These services are exposed by Fuse itself.
You have already seen that javax.jms.ConnectionFactory
was created by using etc/org.ops4j.connectionfactory-amq7.cfg
.
The transaction manager is:
karaf@root()> service:list org.springframework.transaction.PlatformTransactionManager [org.springframework.transaction.PlatformTransactionManager] ------------------------------------------------------------ service.bundleid = 21 service.id = 527 service.scope = singleton Provided by : Red Hat Fuse :: Fuse Modules :: Transaction (21) Used by: Red Hat Fuse :: Quickstarts :: camel-jms (256)
Check for other interfaces under which the actual transaction manager is registered:
karaf@root()> headers 21 Red Hat Fuse :: Fuse Modules :: Transaction (21) ------------------------------------------------ ... Bundle-Name = Red Hat Fuse :: Fuse Modules :: Transaction Bundle-SymbolicName = fuse-pax-transx-tm-narayana Bundle-Vendor = Red Hat ... karaf@root()> bundle:services -p 21 Red Hat Fuse :: Fuse Modules :: Transaction (21) provides: ---------------------------------------------------------- objectClass = [org.osgi.service.cm.ManagedService] service.bundleid = 21 service.id = 519 service.pid = org.ops4j.pax.transx.tm.narayana service.scope = singleton ---- objectClass = [javax.transaction.TransactionManager] provider = narayana service.bundleid = 21 service.id = 520 service.scope = singleton ---- objectClass = [javax.transaction.TransactionSynchronizationRegistry] provider = narayana service.bundleid = 21 service.id = 523 service.scope = singleton ---- objectClass = [javax.transaction.UserTransaction] provider = narayana service.bundleid = 21 service.id = 524 service.scope = singleton ---- objectClass = [org.jboss.narayana.osgi.jta.ObjStoreBrowserService] provider = narayana service.bundleid = 21 service.id = 525 service.scope = singleton ---- objectClass = [org.ops4j.pax.transx.tm.TransactionManager] provider = narayana service.bundleid = 21 service.id = 526 service.scope = singleton ---- objectClass = [org.springframework.transaction.PlatformTransactionManager] service.bundleid = 21 service.id = 527 service.scope = singleton
The transaction manager is available from these interfaces:
-
javax.transaction.TransactionManager
-
javax.transaction.TransactionSynchronizationRegistry
-
javax.transaction.UserTransaction
-
org.jboss.narayana.osgi.jta.ObjStoreBrowserService
-
org.ops4j.pax.transx.tm.TransactionManager
-
org.springframework.transaction.PlatformTransactionManager
You can use any of them in any context that you need. For example camel-jms
requires that the org.apache.camel.component.jms.JmsConfiguration.transactionManager
field be initialized. This is why the example uses:
<reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
instead of, for example:
<reference id="transactionManager" interface="javax.transaction.TransactionManager" />