Posts Tagged JNDI
Bitronix Transaction Manager has been helpful for our integration tests, which can then run outside the app server and have a JTA transaction manager and enough of a JNDI provider to get by.
We have been using version 1.3, since that’s the version that first had the JNDI server. But we had to specify disableJmx=true in the Configuration object so that the java: prefix could work.
This limitation has been removed as of BTM 1.3.1. I’m BTM-enabling another module — maybe now’s the time to update to the current version of BTM…
In our system we have proved out looking up the JMS connection factory from JNDI, but up to this point we have declared the Queue bean using an <amq:queue> element in the application bean files. This ties our application code to the ActiveMQ broker, which we don’t want. We want to move toward looking up the connection factory and the queue from JNDI. That way the application bean file only needs to know the JNDI name, and we can configure the queue in the connection factory datasource configuration file on the app server (e.g., activemq-jms-ds.xml), probably as an mbean element in there:
<mbean code="org.jboss.resource.deployment.AdminObject" name="activemq.queue:name=TheMBeanNameYouSeeInTheJMXConsole"> <attribute name="JNDIName">this/is/the/jndi/name/the/bean/files/will/use</attribute> <depends optional-attribute-name="RARName">jboss.jca:service=RARDeployment,name='activemq-rar-5.1.0.rar'</depends> <attribute name="Type">javax.jms.Queue</attribute> <attribute name="Properties">PhysicalName=theQueueName.YouWillSee.In.ActiveMQ</attribute> </mbean>
But Who Will Care for the Babies?
This will be fine for a production environment — but what about our integration tests? Right now we use Bitronix Transaction Manager 1.3’s JNDI server facilities to provide JNDI lookup of the JDBC datasource for our integration tests. I thought we’d also use BTM to look up the JMS connection factory… but BTM does not support JNDI-looking-up queues as well. Ludovic says:
If you really need a complete JNDI server, you can use Tomcat’s which is quite easy to reuse. This is what I recommended using before BTM 1.3, there is an example here: http://docs.codehaus.org/display/BTM/Hibernate
That’s probably what we’ll need to look into when the time comes…
I’m preparing to commit a change that will switch our code over from datasource beans defined within the .war project to datasources that are looked up via JNDI from the app server. I have to make this change carefully, placing the datasource configuration file on the app servers and setting up the properties files beforehand so that my commit doesn’t break the build.
As part of my testing, I made the changes to my local JBoss. Everything seemed to work. Then I removed the no-longer-used properties that had been needed when we defined the bean locally. Everything still worked. Then, to make sure I wasn’t getting false positives, I removed the JNDI-name property that should be relied on by the new code…
Everything still worked.
The deployment failed when I deleted the properties file altogether, but it was satisfied even with a completely empty file. What was going on?
What was going on
I had forgotten about some logic in our code that is responsible for loading the bean files — we don’t load the persistence beans file if the project doesn’t need them. And currently all the persistence stuff is mocked out, so it doesn’t need them. Since the beans file is not loaded, the project doesn’t care about those properties in the properties file.
Mystery solved. Thanks to Detective Keith for his help!
(Usually it’s good to drop one’s pretensions, but since all we have handy is a prefix we’ll just drop that instead…)
We are (for the moment, anyway) going to need to change our JBoss JDBC datasources so that the datasource resides in the global JNDI namespace, to be looked up under jndi-name
Blah rather than
java:Blah. The reason we need to make this change is that Bitronix doesn’t currently support the java: prefix.
I seem to remember seeing a tag that you could put in your datasource configuration file that would tell JBoss to make the datasource available from the global JNDI namespace instead of from the JVM-specific
Ah yes, the JBoss 4 Application Server Guide and the DTD both document the use-java-context element. Set that to false and it goes in the global JNDI namespace. Using the java: namespace would be preferred; The JBoss 4 Application Server Guide also says,
DataSource wrappers are not usable outside of the server VM, so they are normally bound under the java:/, which isn’t shared outside the local VM.
That’s what we’ll need to set in our production datasources so that Bitronix Transaction Manager can exactly replicate the JNDI functionality at integration test time.
Why is having a JNDI server for our integration testing environment so helpful? Well, just for our minimal XA integration test example, there were (at one point) fourteen Spring beans:
If we had to use a non-JNDI strategy for integration testing outside the app server, seven of these beans would have to be swapped out for test-only doubles — but we want our configuration for integration testing to match as closely as possible our production configuration so that we’re testing the same thing we’re deploying!
In a previous episode, I replaced a JTA transaction manager bean resembling this:
with a bean resembling this:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="BitronixTransactionManager" /> <property name="userTransaction" ref="BitronixTransactionManager" /> </bean>
That’s kind of ok, but having to change out that transaction manager bean makes the configuration more complicated.
As it turns out though, my testing shows that we can keep the
version and it finds the BTM UserTransaction and TransactionManager fine.
In part 3, we tried our first JMS message send; but even after we wrapped the message send in a transaction using AOP, BTM still reported that “resource ‘activemq/QueueConnectionFactory’ cannot be used outside XA transaction scope.” Today we’ll work to resolve that issue.
Both Sends and Receives Need Transactions
Ludovic’s helpful comment on yesterday’s post confirms that we need to make sure both sends and receives are in transactions. As of yesterday, we were only wrapping the message sends, but not receives, in a transaction — at least that was what we were trying to do! As it turns out, the transaction-wrappage of the sends was unsuccessful, while on the other hand we were accidentally successfully wrapping the receives! Here’s how it happened:
Wrapping Sends: If It Ain’t A Bean…
As you may recall from yesterday, the pointcut I had defined for applying transaction advice to message sends was as follows:
<aop:pointcut id="messageReceiveOperation" expression="execution(void com.example.dmn.JndiBitronixTest.transactedSendMessage(..))" />
But as Keith points out, since my JndiBitronixTest class is not defined as a bean in my Spring beans files, the transaction advice will not be applied to the transactedSendMessage method execution. Oops! I didn’t realize that Spring AOP advice applied only to beans! Fixing that is easy enough: the transactedSendMessage method calls one layer down into the MessageSender class, which is defined as a bean. We can just start the transaction there instead:
<aop:pointcut id="messageSendOperation" expression="execution(void com.example.dmn.MessageSender.sendMessage(..))" />
Wrapping Receives: Forgotten Advice
I was looking in the main beans file to verify that MessageSender was defined as a bean when I saw a surprising thing: I already had transactional advice on the message receives, including the following pointcut:
<aop:pointcut id="messageReceiveOperation" expression="execution(void com.example.dmn.MessageProcessor.processIt(..))" />
This advice came along when I brought this code over from my original XA example manual test that runs only on an app server. I consolidated this advice with the send advice into the spring-beans-bitronix.xml file to yield this advice:
<aop:pointcut id="messageSendOperation" expression="execution(void com.example.dmn.MessageSender.sendMessage(..))" /> <aop:pointcut id="messageReceiveOperation" expression="execution(void com.example.dmn.MessageProcessor.processIt(..))" />
Happy Path Seems to Work
Now our integration test can send a JMS message and the listener picks it up from the queue and writes it to the database, without exceptions or stack traces. Furthermore, when I bump the log level to DEBUG for Bitronix classes, in my log4j.properties file:
Then I see encouraging looking XA-type messages like the following:
DEBUG bitronix.tm.BitronixTransaction - committing, 2 enlisted resource(s) DEBUG bitronix.tm.BitronixTransaction - changing transaction status to PREPARING DEBUG bitronix.tm.journal.TransactionLogAppender - between 19089 and 19193, writing a Bitronix TransactionLogRecord with status=PREPARING, recordLength=96, headerLength=28, time=1219154728262, sequenceNumber=11, crc32=-1825120209, gtrid=737072696E672D62746D0000011BDB48D3FF00000000, uniqueNames=XAOracleDS,activemq/QueueConnectionFactory DEBUG bitronix.tm.journal.TransactionLogAppender - disk journal appender now at position 19193 DEBUG bitronix.tm.twopc.AbstractPhaseEngine - executing phase on 2 resource(s) enlisted in 1 position(s) in natural position order ... DEBUG bitronix.tm.BitronixTransaction - changing transaction status to PREPARED ... DEBUG bitronix.tm.twopc.Preparer - successfully prepared 2 resource(s) DEBUG bitronix.tm.BitronixTransaction - 2 interested resource(s) DEBUG bitronix.tm.BitronixTransaction - changing transaction status to COMMITTING (forced) ... DEBUG bitronix.tm.BitronixTransaction - changing transaction status to COMMITTED
There’s Also a One-Phase Commit In There (and that’s good)
Before I found that two-phase commit sequence in the log, I found another sequence farther up
DEBUG bitronix.tm.BitronixTransaction - committing, 1 enlisted resource(s) DEBUG bitronix.tm.BitronixTransaction - changing transaction status to PREPARING DEBUG bitronix.tm.journal.TransactionLogAppender - between 18721 and 18813, writing a Bitronix TransactionLogRecord with status=PREPARING, recordLength=84, headerLength=28, time=1219154728200, sequenceNumber=7, crc32=-667556454, gtrid=737072696E672D62746D0000011BDB48D4AA00000003, uniqueNames=activemq/QueueConnectionFactory DEBUG bitronix.tm.journal.TransactionLogAppender - disk journal appender now at position 18813 DEBUG bitronix.tm.twopc.Preparer - 1 resource enlisted, no prepare needed (1PC) DEBUG bitronix.tm.BitronixTransaction - changing transaction status to PREPARED DEBUG bitronix.tm.journal.TransactionLogAppender - between 18813 and 18905, writing a Bitronix TransactionLogRecord with status=PREPARED, recordLength=84, headerLength=28, time=1219154728200, sequenceNumber=8, crc32=-2001767057, gtrid=737072696E672D62746D0000011BDB48D4AA00000003, uniqueNames=activemq/QueueConnectionFactory DEBUG bitronix.tm.journal.TransactionLogAppender - disk journal appender now at position 18905 DEBUG bitronix.tm.BitronixTransaction - 1 interested resource(s) DEBUG bitronix.tm.BitronixTransaction - changing transaction status to COMMITTING DEBUG bitronix.tm.journal.TransactionLogAppender - between 18905 and 18997, writing a Bitronix TransactionLogRecord with status=COMMITTING, recordLength=84, headerLength=28, time=1219154728200, sequenceNumber=9, crc32=236131807, gtrid=737072696E672D62746D0000011BDB48D4AA00000003, uniqueNames=activemq/QueueConnectionFactory ... DEBUG bitronix.tm.BitronixTransaction - changing transaction status to COMMITTED
I worried about line 8 where it said that only one resource was enlisted and so only a one-phase commit (1PC) was needed — “What happened to the database resource?” I wondered. But then I remembered that this must be the message send (I tend to forget that that’s being transacted too), and the send does not involve the database resource.
I’d still like to have my integration test query the database to verify that the record really made it in there.