Hibernate JTATransactionFactory and BTMTransactionManagerLookup

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Helder Sousa
Hi all,

The configuration of BTM for Hibernate with JTA is available in this link: http://docs.codehaus.org/display/BTM/Hibernate13. In the comments section Simon Massey refers to a problem with JTATransaction and getting the user Transaction and Ludovic warns to "fix the root cause of the problem"
I did some research and post a comment in that page that I copy to this forum as sugested by Ludovic.
Here it is:

The problem that Simon refers is because Hibernate. See this link http://opensource.atlassian.com/projects/hibernate/browse/HHH-3481

Basically, when the code in method getUserTransaction() throws an exception, that exception is catched and creates a exception "string message" that calls again the method getUserTransaction() and this leads to a loop.

The hibernate guys already correct this, calling the getUserTransactionName() instead of the getUserTransaction().

Now, why the first exception of the loop occurs in first place? The first exception is:

javax.naming.NamingException: Cannot create resource instance

I did some debug, and when the application runs with tomcat, the constructor of BitronixContext() is called during startup (because of  <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.BTMTransactionManagerLookup</prop>
), but the lookup method of BitronixContext class is never called.

See the defaultInitCtx variable in "variables view". The bitronixContext is there.



Why the lookup method of BitronixContext is not called?
Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Helder Sousa
Hi all,

I did a test without tomcat, with command line only, and all works fine. In both cases I don't have the jndi.properties in classpath (http://docs.codehaus.org/display/BTM/Hibernate13#Hibernate13-SettinguptheBTMJNDIserver).
Is the jndi.properties file necessary for Tomcat? Why not for a standalone application?

My environement:
Spring 2.5.6
Hibernate 3.3.1
Bitronix: 1.3.2

Best regards,

Helder Sousa
Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Ludovic Orban
Administrator
I think you're more confused by the way JNDI works than by Hibernate or BTM themselves.

Did you configure hibernate.jndi.class and hibernate.jndi.url properties in your Hibernate config of your standalone application ?

Did you integrate BTM in Tomcat as described in http://docs.codehaus.org/display/BTM/Tomcat13 in your Tomcat application ?

jndi.properties isn't needed (and actually is ignored) inside Tomcat or any other app server having its own JNDI server. It also isn't mandatory for standalone applications but often simplifies integration.

In the Hibernate case when both jndi.class and jndi.url are configured, Hibernate will use the specified JNDI provider instead of the default one (configured in jndi.properties or by the app server). There is one exception to this rule: when the looked up JNDI name starts with a namespace (eg: java:) the lookup call is handed to a special handler (configured with the java.naming.factory.url.pkgs property) which can bypass the default and configured mechanisms.

The simple solution to the JNDI lookup problem is to follow these 3 rules in order:

1) Make sure BTM gets registered in the JNDI server of your app server when there is one.
2) Use the BTM JNDI provider when there is no other one and you absolutely need JNDI.
3) Don't use JNDI at all when possible.

I hope this helps. If not, let me know.
Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Helder Sousa
Thank you for your reply Ludovic :)

About jndi.properties file, I don't use it neither in tomcat or junit (standalone application)

About integration of BTM in tomcat, I did not integrate it because I want everything configured in Spring(1)

Take a look to the file: springConfiguration.xml.
I did the configuration of the hibernate.jndi.class property but not the hibernate.jndi.url (what is it by the way?)

With the configuration in the spring file, the standalone (junit) works fine.
Within tomcat there is a problem getting the userTransaction (that is reason for the loop in hibernate code). The lookup(...) method of BitronixContext is not invoked in tomcat.
Is it the missing hibernate.jndi.url missing property the cause?
The use of BTM JNDI provider as defined in my configuration is not enough?
Thank you for your help.
Best regards,

Hélder Sousa

(1) About my configuration approach: With all configuration (BTM, Hibernate, etc) in spring, it is indifferent where my application runs, tomcat or junit tests (standalone). I just need two spring configuration files (one for tomcat and other to junit) with the specific configuration for the corresponding environment, and all of the common configurations (BTM, Hibernate, etc) are imported.



Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Ludovic Orban
Administrator
I see your problem now. The UserTransaction name is java:comp/UserTransaction and when a JNDI lookup happens in Tomcat, the Tomcat provider takes precedence because Tomcat registered a java: namespace handler.

The hibernate.jndi.url property is not used by the BTM provider so you can ignore it.

There is a workaround but it's not very polished yet. It works in 3 steps:

1) Set the UserTransaction JNDI name to something else than the standard name (eg: BtmUserTransaction) on the BTM config object, see: http://btm.codehaus.org/api/1.3.2/bitronix/tm/Configuration.html#setJndiUserTransactionName(java.lang.String)

2) Create your own version of the BTMTransactionManagerLookup. Get the source from there: http://fisheye.labs.jboss.com/browse/Hibernate/core/trunk/core/src/main/java/org/hibernate/transaction/BTMTransactionManagerLookup.java?r=14993 then change the getUserTransactionName() method to return the value you've set in step 1 or better: return Configuration.getJndiUserTransactionName(), see: http://btm.codehaus.org/api/1.3.2/bitronix/tm/Configuration.html#getJndiUserTransactionName().

3) Configure Hibernate to use your own version of BTMTransactionManagerLookup.

Let me know if that helped.
Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Ludovic Orban
Administrator
One last thing: if you create a patch for the Hibernate BTMTransactionManagerLookup that calls Configuration.getJndiUserTransactionName() from the getUserTransactionName() method using reflexion, you should send it to the Hibernate maintainers.

Please note that the BTMTransactionManagerLookup class works for both BTM 1.2 and 1.3 so you will need to return the default java:comp/UserTransaction in case you cannot call Configuration.getJndiUserTransactionName() as this method has been added in 1.3.
Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Helder Sousa
Hi Ludovic,

Thank you for your help :)
Instead of coping the code from hibernate source, I created a class that extends the BTMTransactionManagerLookup class and override the getUserTransactionName method.
Then I configure this new class in hibernate.transaction.manager_lookup_class property and the UserTransaction JNDI name like you refer in point 1.

Here is the code:
        public String getUserTransactionName() {
                try {
                        Class transactionManagerServiceClass = Class.forName("bitronix.tm.TransactionManagerServices");
                        Method getConfigurationMethod = transactionManagerServiceClass.getMethod("getConfiguration", null);
                        Object configurationInstance = getConfigurationMethod.invoke(null, null);

                        Class bitronixConfigurationClass = configurationInstance.getClass();
                        Method getJndiUserTransactionNameMethod = bitronixConfigurationClass
                                        .getMethod("getJndiUserTransactionName", null);
                        String configuredJndiUserTransactionName = (String) getJndiUserTransactionNameMethod.invoke(
                                        configurationInstance, null);
                        if (configuredJndiUserTransactionName != null && configuredJndiUserTransactionName.trim().length() >= 0) {
                                return configuredJndiUserTransactionName;
                        }
                        return "java:comp/UserTransaction";
                }
                catch (Exception e) {
                        throw new HibernateException("Could not obtain BTM UserTransactionName", e);
                }
        }

All works fine now!
I create a issue in Hibernate: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3739 and provide a patch to the BTMTransactionManagerLookup class. I only use the "extend" approach in my environement because I don't want to have "custom" hibernate jars :)
Ludovic, I think you should warn about this issue in configuration page of BTM with hibernate (at least a link to this thread).
Best regards,

Helder Sousa
Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Ludovic Orban
Administrator
Thanks for your report and the patch.

I know the documentation is a bit vague regarding JNDI integration but this is more an issue with general understanding of JNDI itself. I don't know what the best approach to document this is.
Reply | Threaded
Open this post in threaded view
|

Re: Hibernate JTATransactionFactory and BTMTransactionManagerLookup

Ludovic Orban
Administrator
In reply to this post by Helder Sousa
Another case of JNDI soup...

The first thing you should do is to change the transaction manager's JNDI name to something that doesn't start with 'java:', see: http://docs.codehaus.org/display/BTM/Jndi13#Jndi13-Changingthedefaulttransactionmanager%27sJNDIname

The problem you're facing is that JNDI does not delegate lookup calls to the default provider when the looked up name is a URL (starting with java: in your case) but to the provider which registered a andler for that URL's scheme which must be done by any servlet container.

There is too much confusion with the default value I've chosen. Maybe I should change it in the next version or add a FAQ entry.