I was using the wrong kind of SessionFactoryBean

I was trying to change over from using a Hibernate mappings file to annotations, but my test kept blowing up while instantiating the Spring beans.

Then I realized that in my Spring bean file, my sessionFactory bean was declared as an org.springframework.orm.hibernate3.LocalSessionFactoryBean instead of an org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean.  The annotatedClasses property I was trying to set didn’t exist in the LocalSessionFactoryBean!

I had been focusing on how to specify the list of annotated classes (see this example in the Spring Javadoc) and hadn’t paid attention to what kind of SessionFactory bean I was working with.

Advertisements

Blowing up in test mode

This is the final post in a 9-part series on Envers, Spring, and JTA. The previous post is The final answer.


Setting the hibernate.transaction.manager_lookup_class and org.hibernate.transaction.JTATransactionFactory properties in the Hibernate config:

<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop>
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>

fixed my issue where the envers beforeCompletion synchronization wasn’t-getting-called-when-we’re-in-JTA-mode… but with this change, the integration test stopped working — it now couldn’t find the JTA transaction manager by JNDI lookup.

Ah, yes — we’d need a JTA transaction manager to actually be available at integration test time — looks like another job for Bitronix Transaction Manager.

1. How I Set Up BTM

So, I put a jndi.properties file in the root of the classpath under src/test/java/ …

java.naming.factory.initial = bitronix.tm.jndi.BitronixInitialContextFactory

…put a bitronix-default-config.properties file beside it…

bitronix.tm.serverId = hibernate-support-module-btm-server
bitronix.tm.journal.disk.logPart1Filename = target/btm1.log
bitronix.tm.journal.disk.logPart2Filename = target/btm2.log

…and added a bitronixTransactionManager bean to the beans file under src/test/resources…

<bean id="bitronixTransactionManager" factory-method="getTransactionManager" class="bitronix.tm.TransactionManagerServices" destroy-method="shutdown"/>

2. One More Piece

The transaction manager JNDI lookup still was failing though, with this error:

org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is org.hibernate.TransactionException: Naming service lookup for UserTransaction returned null [UserTransaction]

Caused by: org.hibernate.TransactionException: Naming service lookup for UserTransaction returned null [UserTransaction]

The BTM Hibernate documentation pointed out that I can set the

hibernate.transaction.manager_lookup_class to Hibernate’s BTMTransactionManagerLookup class.  (Huh!)

<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.BTMTransactionManagerLookup</prop>

When I do this, the integration test now works again.

3. Now the Deploy to JBoss Fails

I was pretty sure that deploying to JBoss with the transaction manager lookup setting pointing to a BTMTransactionManagerLookup wasn’t going to work, and sure enough, on deploy I get this:

org.hibernate.HibernateException: Could not obtain BTM transaction manager instance

Caused by: java.lang.ClassNotFoundException: bitronix.tm.TransactionManagerServices

Right, this error is because we aren’t including BTM in the production war project (and we don’t want to).

4. Test and Production Both Need To Work At Once

So now we can run our tests if we specify the BTMTransactionManagerLookup, and we can run in production if we specify the JBossTransactionManagerLookup.  We need to find a configuration that will work both in test and production modes.

Another nagging issue is that we don’t want to hardcode our production configuration to be specific to JBoss — ideally we’d like the application server to be auto-detected; if we need to we could make it manually configurable.  Hardcoded JBossTransactionManagerLookup won’t do.

5. Spring to the Rescue?

Thinking of auto-detecting the application server reminds me of what Spring’s JtaTransactionManager does — it tries various JNDI lookups for the JTA TransactionManager and UserTransaction until it finds ones that work, so you’re not hardcoding support for only one app server and there is no user configuration needed .  I wonder if Spring has a TransactionManagerLookup that does that sort of thing to avoid  — is Spring’s LocalTransactionManagerLookup what we need?

When I switch the transaction manager lookup line to that:

<prop key="hibernate.transaction.manager_lookup_class">org.springframework.orm.hibernate3.LocalTransactionManagerLookup</prop>

…I get this error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [com/ontsys/fw/hibernatesupport/startup/hibernate-support-beans.spring.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Could not instantiate TransactionManagerLookup ‘org.springframework.orm.hibernate3.LocalTransactionManagerLookup’

Come to think of it, the javadoc for LocalTransactionManagerLookup does say:

Implementation of Hibernate’s TransactionManagerLookup interface that returns a Spring-managed JTA TransactionManager, determined by LocalSessionFactoryBean’s “jtaTransactionManager” property.

(Emphasis mine.)  My Hibernate configuration configures an AnnotationSessionFactoryBean (which is a LocalSessionFactoryBean), and I didn’t set the jtaTransactionManager property there.

5.1. Setting the jtaTransactionManager Property

As a first attempt, I set

<property name="jtaTransactionManager" ref="transactionManager" />

where the transactionManager reference refers to my

<tx:jta-transaction-manager />

bean.  I get:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [com/ontsys/fw/hibernatesupport/startup/hibernate-support-beans.spring.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [org.springframework.transaction.jta.JtaTransactionManager] to required type [javax.transaction.TransactionManager] for property ‘jtaTransactionManager’; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [org.springframework.transaction.jta.JtaTransactionManager] to required type [javax.transaction.TransactionManager] for property ‘jtaTransactionManager’: no matching editors or conversion strategy found

So the Spring JtaTransactionManager isn’t a javax.transaction.TransactionManager.  What do you specify for the jtaTransactionManager property, then?

In a helpful post from 2004, Juergen Hoeller speaks to Spring’s support for, and the limits of, container-independent JTA configuration:

…Spring’s JtaTransactionManager itself works just nicely in a container-independent fashion – in typical usage scenarios where you could use a plain JTA javax.transaction.UserTransaction too…you just need container-specific configuration for more advanced usage, namely transaction suspension and Hibernate cache completion callbacks. The simple reason for this is that you need the JTA javax.transaction.TransactionManager to suspend/resume transactions and register transaction synchronizations; the UserTransaction interface does not support that functionality.

Unfortunately, J2EE does not specify how to access the JTA TransactionManager, just the common JNDI location of the JTA UserTransaction – so the lookup of the JTA TransactionManager has to be container-specific. This is the case with Hibernate (see TransactionManagerLookup), this is the case with Spring, this is the case with any such tool.

For Hibernate cache completion callbacks with JTA, you need to specify a corresponding TransactionManagerLookup in the Hibernate properties. As an alternative, Spring allows you to pass a JTA TransactionManager into LocalSessionFactoryBean’s “jtaTransactionManager” property, to reuse a centrally defined TransactionManager reference that can be shared with JtaTransactionManager’s “transactionManager” property.

(Emphasis mine.) Juergen goes on to mention that as another alternative, Hibernate’s cache-completion callbacks are taken care of if you use Spring’s own transaction synchronization mechanism — this avoids the need for a container-specific TransactionManagerLookup.  I don’t think Spring’s synchronization mechanism is sufficient for what we need here though, because it looks like it only supports the afterCompletion() callback, and we (through envers) are making use of the beforeCompletion() callback.

6. A Table of Our Apparent Options

Opt # Configuration Viable Option? Comments
1 Plain ol’ Spring JtaTransactionManager and Hibernate LocalSessionFactoryBean without additional configuration No Hibernate doesn’t know about Spring JTA transaction manager, synchronizations get registered wrong and are not called
2 Use Spring’s synchonization mechanism No Appears to only support afterCompletion() synchronization callback, and envers needs and uses the beforeCompletion() one
3 Have a separate javax.transaction.TransactionManager that the Hibernate LocalSessionFactoryBean and Spring’s JtaTransactionManager both reference Yes The javax.transaction.TransactionManager may have to be defined in a container-specific way
4 Specify hibernate.transaction.manager_lookup_class to Hibernate’s LocalSessionFactoryBean config Yes Which TransactionManagerLookup class is needed is container-specific, so we’d need to abstract the choice out to a configuration property somehow

I’m leaning toward going with option #4 without even trying option #3 since I can see how we would do #4 and I’m afraid that even with #3’s greater complexity it still wouldn’t get us container-independent JTA configuration.

Update (1/2/2009): Yep, option #4 is what we went with.   To abstract away the container-specificness, we maintain a list of org.hibernate.transaction.*TransactionManagerLookup classes and iterate through the list trying getTransactionManager() on each until we find one that doesn’t return null — then we populate the hibernate.transaction.factory_class configuration property with that class name.  The org.hibernate.transaction.BTMTransactionManagerLookup last in the list, and the org.hibernate.transaction.JBossTransactionManagerLookup is currently the only other lookup class in the list; but as we add support more app servers, the idea is we can just add more of the specific org.hibernate.transaction.*TransactionManagerLookup classes to the list.

The final answer

This post is part 8 in a 9-part series on Envers, Spring, and JTA. The next post is Blowing up in test mode, and the previous post is An answer.


The story thus far:

The envers beforeCompletion() hook was not running when I switched from using Spring’s HibernateTransactionManager to using its JtaTransactionManager.  I had done some digging on my own, then made a long post to the Spring Data Access forum (after an initial post to the envers forum).

The long post apparently scared everybody off ;) , so I wrote a much shorter, more focused post.

A reply to the shorter post pointed me to the SessionFactoryUtils javadoc, where I read:

Note that for non-Spring JTA transactions, a JTA TransactionManagerLookup has to be specified in the Hibernate configuration.

On the trail of how to specify a TransactionManagerLookup, I found the hibernate.transaction.manager_lookup_class setting in the Using JTA section of the Hibernate Reference Guide — but unbeknownst to me, another setting was needed…

Final Answer

A Hibernate Users forum post titled How can i use JTA with Hibernate? points to the Transaction demarcation with JTA section of the Sessions and transactions Wiki page, where we see:

  • set hibernate.transaction.manager_lookup_class to a lookup strategy for your JEE container
  • set hibernate.transaction.factory_class to org.hibernate.transaction.JTATransactionFactory

So, adding these two lines to my Hibernate spring beans file (lines 6 and 7 below) fixed the issue:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		...
<property name="hibernateProperties">
<props>
				...
<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop>
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>
			</props>
		</property>
		...
    </bean>

NOW the envers beforeCompletion() hook gets called when using the JTA transaction manager.

Whew!

An answer

This post is part 7 in a 9-part series on Envers, Spring, and JTA. The next post is The final answer, and the previous post is beforeCompletion, I need to figure this out.


A kind soul answered my second post to the Spring Data Access forum in which I had asked, “Does JtaTransactionManager support beforeCompletion synchronization?

He said, “Look at: SessionFactoryUtils#getJtaSynchronizedSession”.

The getJtaSynchronizedSession() method didn’t appear in org.springframework.orm.hibernate3.SessionFactoryUtils’ javadoc, so I pulled up the Spring source code I had checked out earlier and there it is (it’s a private method).

I right-clicked the method name in the editor and selected Open Call Hierarchy (have I mentioned that I’m loving the tool support these days?!)  The only three places it’s called from (another layer or two down) are the two getSession() methods and the one public doGetSession() method…

When I had initially pulled up  SessionFactoryUtils’ javadoc this had immediately jumped out at me:

Note that for non-Spring JTA transactions, a JTA TransactionManagerLookup has to be specified in the Hibernate configuration.

Aha: We’re not specifying a JTA TransactionManagerLookup in our Hibernate configuration.  Looks like that would be the next thing to look at.

Update: Here’s help for someone who had a similar issue.  Also, the Using JTA section of the Hibernate Reference Guide…

beforeCompletion, I need to figure this out

This post is part 6 in a 9-part series on Envers, Spring, and JTA. The next post is An answer, and the previous post is The versioning isn’t happening, part 4a: phone a friend.


I’m just getting back to the envers-JTA-hooks-not-getting-called thing — the following is from work I did last Wednesday —


A post to the Spring Data Access forum had this interesting line:

DEBUG [org.springframework.transaction.jta.JtaTransactionManager] Triggering beforeCompletion synchronization

Hey!  Just when I was beginning to worry that Spring’s JtaTransactionManager had never heard of beforeCompletion and didn’t intend to call that callback.  So… where is that line of code, and who calls it?

Where It Is, and Who Calls It

Back at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(TransactionStatus), this calls its own internal processCommit() method, which — why did we not notice this before? — does these things (among others):


prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);

TriggerBeforeCompletion() ?!  Firing up the ol’ debugger, I do make it in there.  So it *is* trying to run the beforeCompletion Synchronizations, methinks.  Perhaps they aren’t being registered?

triggerBeforeCompletion() calls TransactionSynchronizationUtils.triggerBeforeCompletion(), which calls org.springframework.transaction.support.TransactionSynchronizationManager.getSynchronizations().  (The TransactionSynchronizationManager is also the class that has a registerSynchronization(TransactionSynchronization) method — but instead of taking a javax.transaction.Synchronization, it takes a org.springframework.transaction.support.TransactionSynchronization.)

The only TransactionSynchronization that’s in there currently is a org.springframework.orm.hibernate3.SpringSessionSynchronization.

Envers’ org.jboss.envers.synchronization.VersionsSyncManager.get(EventSource session) calls session.getTransaction().registerSynchronization().  The getTransaction() call returns an org.hibernate.transaction.JDBCTransaction.

Next step: Can we get at that JDBCTransaction from JtaTransactionManager’s doCommit()?  Or alternatively, can the thing passed as an EventSource to VersionsSyncManager.get() be something that wraps a JDBCTransaction and also registers with the Spring TransactionSynchronizationManager?

It helps to have a message listener container

I couldn’t figure out why the AOP advice kept not running around the MessageListener’s onMessage() method in my integration test.  I had declared the MessageListener as a bean (the advice only gets applied to beans) and relaxed the pointcut expression so it shouldn’t have to reside in a certain subpackage; the setup looked good, but still no advice.  I ratcheted the logging level up and saw the message being sent, so why wasn’t the advice running?  I began to have that piteous, put-upon feeling you get when you think you’re doing everything right but the program is unreasonably refusing to cooperate.

Then I realized that I hadn’t declared a DefaultMessageListenerContainer in my Spring bean file.  And then I realized that I had only been seeing messages being sent, never received.  (The advice only applies when the message is received).  Oops!

I declared the message listener container bean, and messages began to be received with the advice applying as it should.  : )

The versioning isn’t happening, part 4a: phone a friend

This post is part 5 in a 9-part series on Envers, Spring, and JTA. The next post is beforeCompletion, I need to figure this out, and the previous post is The versioning isn’t happening, part 4: Registering with the wrong transaction?


The time had come to ask my questions at the Spring Data Access forum.  Following is a copy of that forum post (Whew, does it take work to try to ask a question well!) :

Sychronization hook not called when using JTA txmgr


Summary
We’re using Spring 2.5.5 on JBoss 4.2.2.GA, along with JBoss envers version 1.1.0.GA. (Envers uses Hibernate’s onPostInsert() hook to register a javax.transaction.Synchronization callback that’s called during transaction commit, to write audit records about records that were created during that transaction.)When we use Spring’s HibernateTransactionManager and write a record, the registered envers javax.transaction.Synchronization.beforeCompletion () event handler fires as expected, but when we use Spring’s jta-transaction-manager, the registered envers beforeCompletion() event handler does not fire. In this post, after sketching our configuration, I discuss what I see working the same and different between the HibernateTransactionManager and jta-transaction-manager scenarios, and close by pointing out where I think the problem might be in Spring.

Our Configuration
We’ve configured the Hibernate sessionFactory bean to hook it up to envers’ VersionsEventListener (which implements org.hibernate.event.PostInsertEventListener):

<bean id="versionsEventListener" class="org.jboss.envers.event.VersionsEventListener" />
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		...
<property name="eventListeners">
			<map>
				<entry key="post-insert">
					<ref local="versionsEventListener" />
				</entry>
				...
			</map>
		</property>
		...
	</bean>

With this configuration, every time a new row is inserted into the main table,

  1. org.jboss.envers.event.VersionsEventListener.onPos tInsert(PostInsertEvent event) is called
  2. onPostInsert() passes event.getSession() to org.jboss.envers.synchronization.VersionsSyncManag er.get(EventSource)
  3. VersionsSyncManager.get() then calls getTransaction() (which returns a JDBCTransaction) on the EventSource
  4. Finally, VersionsSyncManager.get() calls registerSynchronization() on this transaction to register a javax.transaction.Synchronization object whose beforeCompletion() method should be called during the transaction commit.

It is the code in this beforeCompletion() method that does the writing of the versioning records.

HibernateTransactionManager vs. JtaTransactionManager
When I set up my .war project’s Spring beans to use the Hibernate transaction manager:

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

then the VersionsEventListener.onPostInsert() call happens as I have descibed, and later the registered beforeCompletion() hook is called and envers writes my audit records to the database. But when I remove the above bean and use the JTA transaction manager:

	<tx:jta-transaction-manager />

the VersionsEventListener.onPostInsert() call still happens and I do see registerSynchronization() being called; but this time the envers beforeCompletion() hook never gets called (and thus the audit records are not written to the database).

Registering with a JDBCTransaction, Committing a UserTransaction?
That’s the main symptom. I’ll now speculate on the cause… : )

When I’m using the HibernateTransactionManager, I notice that its doCommit() method makes this call:

txObject.getSessionHolder().getTransaction().commit();

Here, getTransaction() returns an org.hibernate.transaction.JDBCTransaction (the same one that envers called registerSynchronization() on earlier). As part of this commit(), the JDBCTransaction calls its notifyLocalSynchsBeforeTransactionCompletion() method, which calls the javax.transaction.Synchonization.beforeCompletion( ) registered by envers. So I see how the hook works on the HibernateTransactionManager side. On the JtaTransactionManager side though, org.springframework.transaction.jta.JtaTransaction Manager.doCommit() makes this call:

txObject.getUserTransaction().commit();

Here, getUserTransaction() returns the javax.transaction.UserTransaction, and then we call commit() on that. This commit() call does not result in the envers javax.transaction.Synchronization.beforeCompletion () being called. Something I notice here is that envers’ onPostInsert() method caused registerSynchronization() to be called on a JDBCTransaction object, not the UserTransaction object. This JDBCTransaction object appears to be unused for the purposes of committing.

Why I think it might be an issue in Spring
Envers’ VersionsEventListener.onPostInsert(PostInsertEvent event) method receives an org.hibernate.event.PostInsertEvent, and it is from this event that envers eventually calls (effectively):

event.getSession().getTransaction().registerSynchronization()

Envers is registering its Synchronization object with the transaction given to it by the PostInsertEvent — I don’t immediately see how it could do any differently (though I’m far from an expert at these things)…Meanwhile, I don’t see where Spring is informing the UserTransaction about these registered synchronizations, and I don’t see Spring taking care of them itself either. It seems to my uneducated eyes that Spring should be doing one or the other of these, so that the registered callbacks aren’t just lost. Is there something I’m missing?

Thank you for your patience with this long post!
-Daniel-

The versioning isn’t happening, part 4: Registering with the wrong transaction?

This post is part 4 in a 9-part series on Envers, Spring, and JTA. The next post is The versioning isn’t happening, part 4a: phone a friend, and the previous post is The versioning isn’t happening, part 3: it’s in UserTransaction?


Looky here:

While looking at the javadoc for javax.transaction.Synchronization earlier, I had noticed this:

Using the registerSynchronization method, the application server registers a Synchronization object for the transaction currently associated with the target Transaction object.

Now, the only registerSynchronization() method I see is javax.transaction.Transaction’s registerSynchronization() method, and the one place that registerSynchronization() is called from the envers code is in org.jboss.envers.synchronization.VersionsSyncManager.get(EventSource).

Here’s an interesting thing: when I set a breakpoint on VersionsSyncManager.get(EventSource) and start stepping through the code, the first thing it does (line 47) is do a session.getTransaction().  I expected that underneath, this would be our javax.transaction.UserTransaction (since we’re running in JTA mode), but instead when we call transaction.registerSynchronization() I see that we have a org.hibernate.transaction.JDBCTransaction.

Wrong Number?

So to step back a moment, what we have here is a Synchronization being registered with a JDBCTransaction, when the actual work will be done by a javax.transaction.UserTransaction.  It seems plausible that when the UserTransaction goes to commit, it won’t run these registered synchronizations (since it seems it wouldn’tknow about them).

Why does EventSource.getTransaction() return a JDBCTransaction instead of the UserTransaction?

EventSource extends org.hibernate.Session, and the javadoc for org.hibernate.Session.getTransaction() has this interesting tidbit:

The class of the returned Transaction object is determined by the property hibernate.transaction_factory.

The closest to this that I see in the AnnotationConfig is:


hibernate.transaction.factory_class=org.springframework.orm.hibernate3.SpringTransactionFactory

So what does SpringTransactionFactory do?

Hmm: org.springframework.orm.hibernate3.SpringTransactionFactory.createTransaction(JDBCContext, Context) returns a new JDBCTransaction, even if we’re in JTA mode.  That is the transaction that is returned (I think) by org.hibernate.Session.getTransaction().  Is that a bug?  Or alternatively, is the problem that Spring’s JtaTransactionManager calls commit() on the UserTransaction directly, without taking into account the synchronizations registered on the companion JDBCTransaction object?  It seems that one way or another, someone should be taking responsibility for getting those registered callbacks called!

(Next: Time to ask for help!)

The versioning isn’t happening, part 3: it’s in UserTransaction?

This post is part 3 in a 9-part series on Envers, Spring, and JTA. The next post is The versioning isn’t happening, part 4: Registering with the wrong transaction?, and the previous post is The versioning isn’t happening, part 2: beforeCompletion.


We’re trying to figure out why the envers versioning records are not created when our project uses a JTA transaction manager — it creates the versioning records just fine when we’re using local Hibernate transactions.

In part 2, we discovered that while org.jboss.envers.synchronization.VersionsSync.beforeCompletion() is called when we’re running in Hibernate mode, it is not getting called when we’re in JTA mode, even though onPostInsert is being called just like in Hibernate mode.

To dig farther toward the root of this, let’s see where the call to beforeCompletion() comes from when we’re in Hibernate mode…

1. Hibernate mode

I deployed the hibernate-mode version again and sent a message with a method-entry breakpoint on the VersionsSync beforeCompletion() call.  This time, I set breakpoints on the lines farther up the call stack — the last six entries in the chain of calls yielding the call to beforeCompletion().  My list of breakpoints now looks like this:

AbstractPlatformTransactionManager [line: 678] - commit(TransactionStatus)
AbstractPlatformTransactionManager [line: 709] - processCommit(DefaultTransactionStatus)
HibernateTransactionManager [line: 655] - doCommit(DefaultTransactionStatus)
JDBCTransaction [line: 109] - commit()
JDBCTransaction [line: 228] - notifyLocalSynchsBeforeTransactionCompletion()
TransactionAspectSupport [line: 321] - commitTransactionAfterReturning(TransactionInfo)
VersionsSync [entry] - beforeCompletion()

Now we’ll run it in jta mode and see if we get to any of these breakpoints.

2. JTA mode

We do break at TransactionInterceptor(TransactionAspectSupport).commitTransactionAfterReturning(TransactionAspectSupport$TransactionInfo) line: 321 (but… ohhhh, I stepped too slowly and the transaction timed out, yielding a stack trace!  Trying again…)

We’re at JtaTransactionManager(AbstractPlatformTransactionManager).commit(TransactionStatus) line: 678 and then JtaTransactionManager.doCommit(DefaultTransactionStatus)… so far this is just like Hibernate mode.  org.springframework.transaction.jta.JtaTransactionManager.doCommit(DefaultTransactionStatus) mainly just does txObjext.getUserTransaction().commit() (line 1028) — this is still analogous to what we see on the Hibernate-mode side (there, it’s org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(DefaultTransactionStatus), which mainly just calls txObject.getSessionHolder().getTransaction().commit() (line 655).)

2.1. A closer look at the Hibernate-mode commit() call

Let’s look more closely at this call:

txObject.getSessionHolder().getTransaction().commit()

The getTransaction() call returns a org.hibernate.transaction.JDBCTransaction object, which we then call commit() on.  And it is this commit() method that calls org.hibernate.transaction.JDBCTransaction.notifyLocalSynchsBeforeTransactionCompletion(), which calls the beforeCompletion() method.

2.2. The Difference on the JTA Side

The call mentioned in step 1.2.1. is what causes beforeCompletion() to be called (which creates the envers versioning records) when we’re in Hibernate mode.  The analogous call when we’re in JTA mode is:

txObjext.getUserTransaction().commit();

(Called by org.springframework.transaction.jta.JtaTransactionManager.doCommit(DefaultTransactionStatus) on line 1028.)

Now, getUserTransaction() returns the javax.transaction.UserTransaction from JBoss; and then we call commit() on that.  Somewhere inside that commit() call, javax.transaction.Synchronization.beforeCompletion()* is not being called.

*VersionsSync implements javax.transaction.Synchronization; any beforeCompletion() call in an app server’s UserTransaction would reference the javax.transaction.Synchronization interface rather than org.jboss.envers.synchronization.VersionsSync, I’m sure!

In our next installment, let’s look into why UserTransaction is not calling beforeCompletion().

The versioning isn’t happening, part 2: beforeCompletion

This post is part 2 in a 9-part series on Envers, Spring, and JTA. The next post is The versioning isn’t happening, part 3: it’s in UserTransaction?, and the previous post is The versioning isn’t happening, part 1: wanderings.


We’re trying to figure out why the envers versioning records are not created when our project uses a JTA transaction manager — it creates the versioning records just fine when we’re using local Hibernate transactions.

Our approach last time was to see what differences there might be in the Hibernate configuration.  I ended up that post with

Perhaps next I should capture the values of the rest of AnnotationConfiguration’s 47 fields, and compare them all between jta mode and Hibernate mode.

-1. The Other 47 Fields

After I stopped writing last time, I did go on to capture the 47 AnnotationConfiguration fields’ values when running in Hibernate mode and when running in JTA mode, and… they were the same!  (Keith suspected they would be.)

0. No Other Apparent Magic During JBoss Startup

Still running in Hibernate mode, I followed the control flow after the creation of the Hibernate SessionFactory bean, but I didn’t see anything interesting-looking.  It’s hard to let the idea go that something’s different at startup time, but I think next I need to take a closer look at what happens during a save to the database.

1. A list of work to do later

Let’s see where the envers onPostInsert() hook gets called from, in hibernate mode (i.e., where things are working correctly). Here’s the call stack:

Daemon Thread [http-127.0.0.1-8080-1] (Suspended (entry into method onPostInsert in VersionsEventListener))
VersionsEventListener.onPostInsert(PostInsertEvent) line: 88
EntityInsertAction.postInsert() line: 131
EntityInsertAction.execute() line: 110
ActionQueue.execute(Executable) line: 279
ActionQueue.executeActions(List) line: 263
ActionQueue.executeActions() line: 167
DefaultFlushEventListener(AbstractFlushingEventListener).performExecutions(EventSource) line: 298
DefaultFlushEventListener.onFlush(FlushEvent) line: 27
SessionImpl.flush() line: 1000
GreetingDAO(GenericDAO<PO,PK>).create(PO) line: 66
GreetingDBSvcImpl.saveGreeting(GreetingPTO) line: 25
GreetingSvcImpl.saveGreeting(GreetingDTO) line: 21
HelloWorldSvcImpl.writeGreetingToDatabase(Greeting) line: 24
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39

1.1. It’s all the same in JTA mode

I set breakpoints at most of these places.  Let’s try it in jta mode:

  • We get to the session.flush() in GreetingDAO(GenericDAO<PO,PK>).create(PO) line: 66
  • We get to DefaultFlushEventListener.onFlush(FlushEvent) line: 27
  • We get to DefaultFlushEventListener(AbstractFlushingEventListener).performExecutions(EventSource) line: 298
  • We get to ActionQueue.executeActions() line: 167 with 1 element in our insertions ArrayList to do, just like in hibernate mode
  • We get to EntityInsertAction.execute() line: 110
  • We get into EntityInsertAction.postInsert(), with a org.jboss.envers.event.VersionsEventListener and a org.hibernate.search.event.FullTextIndexEventListenerin the postInsertEventListener[] array
  • We even get into VersionsEventListener.onPostInsert(PostInsertEvent)!

So why doesn’t the versioning happen?

It turns out that VersionsEventListener.onPostInsert() isn’t where the real work is done.

1.2. What VersionsEventListener.onPostInsert() is responsible for

Summary:

VersionsEventListener.onPostInsert() adds a VersionsWorkUnit to a list, to be picked up and performed later.  It doesn’t actually do the work.

Details:

org.jboss.envers.event.VersionsEventListener.onPostInsert(PostInsertEvent) does is to add the org.jboss.envers.synchronization.work.VersionsWorkUnit to the org.jboss.envers.synchronization.VersionsSync.workUnits linked list (VersionsSync is the class that implements javax.transaction.Synchronization).

3. Who picks up the work unit?

Who is responsible for picking up the work unit, then?  I notice that the javax.transaction.Synchronization specifies two methods: beforeCompletion() and afterCompletion(int).  Let’s see if anyone calls beforeCompletion() when we’re in Hibernate mode… setting breakpoints on these two methods and re-running my hibernate mode test, we break at beforeCompletion() with this call stack:

Daemon Thread [http-127.0.0.1-8080-1] (Suspended (entry into method beforeCompletion in VersionsSync))
VersionsSync.beforeCompletion() line: 125
JDBCTransaction.notifyLocalSynchsBeforeTransactionCompletion() line: 228
JDBCTransaction.commit() line: 109
HibernateTransactionManager.doCommit(DefaultTransactionStatus) line: 655
HibernateTransactionManager(AbstractPlatformTransactionManager).processCommit(DefaultTransactionStatus) line: 709
HibernateTransactionManager(AbstractPlatformTransactionManager).commit(TransactionStatus) line: 678
TransactionInterceptor(TransactionAspectSupport).commitTransactionAfterReturning(TransactionAspectSupport$TransactionInfo)line: 321
TransactionInterceptor.invoke(MethodInvocation) line: 116
ReflectiveMethodInvocation.proceed() line: 171
ExposeInvocationInterceptor.invoke(MethodInvocation) line: 89
ReflectiveMethodInvocation.proceed() line: 171
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 204
$Proxy68.writeGreetingToDatabase(Greeting) line: not available
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 585
JAXWSMethodInvoker(AbstractInvoker).performInvocation(Exchange, Object, Method, Object[]) line: 136
JAXWSMethodInvoker(AbstractInvoker).invoke(Exchange, Object, Method, List<Object>) line: 82
JAXWSMethodInvoker.invoke(Exchange, Object, Method, List<Object>) line: 97
JAXWSMethodInvoker(AbstractInvoker).invoke(Exchange, Object) line: 68
ServiceInvokerInterceptor$1.run() line: 56
SynchronousExecutor.execute(Runnable) line: 37
ServiceInvokerInterceptor.handleMessage(Message) line: 92
PhaseInterceptorChain.doIntercept(Message) line: 221
ChainInitiationObserver.onMessage(Message) line: 78
ServletDestination.invoke(ServletContext, HttpServletRequest, HttpServletResponse) line: 92
ServletController.invokeDestination(HttpServletRequest, HttpServletResponse, ServletDestination) line: 279
ServletController.invoke(HttpServletRequest, HttpServletResponse) line: 161
CXFServlet(AbstractCXFServlet).invoke(HttpServletRequest, HttpServletResponse) line: 174
CXFServlet(AbstractCXFServlet).doPost(HttpServletRequest, HttpServletResponse) line: 152
CXFServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 710
CXFServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 803
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
ReplyHeaderFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 96
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 235
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
StandardWrapperValve.invoke(Request, Response) line: 230
StandardContextValve.invoke(Request, Response) line: 175
SecurityAssociationValve.invoke(Request, Response) line: 179
JaccContextValve.invoke(Request, Response) line: 84
StandardHostValve.invoke(Request, Response) line: 127
ErrorReportValve.invoke(Request, Response) line: 102
CachedConnectionValve.invoke(Request, Response) line: 157
StandardEngineValve.invoke(Request, Response) line: 109
CoyoteAdapter.service(Request, Response) line: 262
Http11Processor.process(Socket) line: 844
Http11Protocol$Http11ConnectionHandler.process(Socket) line: 583
JIoEndpoint$Worker.run() line: 446
Thread.run() line: 595

So VersionsSync.beforeCompletion() does indeed get called when we’re in Hibernate mode.

3.1. Sidenote: The Point Where the Records Appear in the Tables

After some more lines of code, we get to org.hibernate.transaction.JDBCTransaction.commitAndResetAutoCommit(), which does a  jdbcContext.connection().commit().  Before this line is executed, neither the main record nor the versioning record appear in the table; after that line, the records do appear.

4. JTA mode: no beforeCompletion call made

Next we’ll re-run the test we did in step 3 with the jta version deployed and with all the same breakpoints, to see if org.jboss.envers.synchronization.VersionsSync.beforeCompletion() gets called…

It doesn’t.

Tune in next time for an exploration of why VersionsSync.beforeCompletion() isn’t getting called in JTA mode!