3 Replies Latest reply on Feb 5, 2009 8:24 AM by jhalliday

    Standalone JBossTS with Hibernate Shards

    david.pellegrini

      Hi,

      I'm trying to get JBossTS working in a standalone setup. The application will eventually run in JBossAS, but right now I need to get it running in a JUnit test environment (hence the standalone requirement).

      The app has been working using the Hibernate Transaction API, and I am trying to get it working against the JTA API as recommended in "Java Persistence with Hibernate" and other references. Given the tight relationship between Hibernate and JBoss, I elected to integrate with JBossTS, especially since it can run standalone.

      According to the examples, I just have to add the following to my Hibernate configuration:

       <property name="transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>
       <property name="transaction.factory.class">org.hibernate.transaction.JTATransactionFactory</property>
      

      The examples also make reference to a datasource. To complicate matters, my database is sharded using Hibernate shards. I have not set up my database shards as "datasources" but instead configured via distinct cfg.xml files (which works fine until I throw JBossTS into the mix). How important is it to have my shards set up and referenced as datasources?

      In any event, when I run, the SessionFactories for the shards are created as expected, but then it can't find the TransactionManager. Here's an excerpt from the log file:
       2009-02-01 12:29:08,537 INFO [main] util.NamingHelper(49): JNDI InitialContext properties:{java.naming.provider.url=file:/opt/jndi, java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory}
       2009-02-01 12:29:08,564 INFO [main] impl.SessionFactoryObjectFactory(114): Bound factory to JNDI name: HibernateSessionFactory0
       2009-02-01 12:29:08,565 WARN [main] impl.SessionFactoryObjectFactory(124): InitialContext did not implement EventContext
       2009-02-01 12:29:08,565 INFO [main] util.NamingHelper(49): JNDI InitialContext properties:{java.naming.provider.url=file:/opt/jndi, java.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory}
       2009-02-01 12:29:08,567 ERROR [main] session.HibernateUtil(84): Initial SessionFactory creation failed.
       org.hibernate.HibernateException: Could not locate TransactionManager
       at org.hibernate.transaction.JNDITransactionManagerLookup.getTransactionManager(JNDITransactionManagerLookup.java:60)
       at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:357)
       at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1327)
       at org.hibernate.shards.ShardedConfiguration.buildSessionFactory(ShardedConfiguration.java:251)
       at org.hibernate.shards.ShardedConfiguration.buildShardedSessionFactory(ShardedConfiguration.java:177)
       at com.spokesoftware.spokebook.hibernate.session.HibernateUtil.<clinit>(HibernateUtil.java:76)
      


      It seems that Hibernate is trying to lookup the TransactionManager via JNDI at the location specified by JBossTransactionManagerLookup, but no TransactionManager has been created and left there. So, what more do I need to do to wire in the JBossTS? BTW, the JBossTS jars and their dependent lib dirs are all in the classpath.

      Looking for the missing link ...

      Thanks!

        • 1. Re: Standalone JBossTS with Hibernate Shards
          jhalliday

          Hi David

          The various hibernate *TransactionManagerLookup alternatives basically cause hibernate to look in different places in JNDI for the transaction manager, since the name under which it is bound is not standardized. The JBoss one is for use inside the app server - it knows where JBossAS binds the transaction manager. There isn't one specifically for JBossTS. For that matter, JBossTS standalone does not bind the transaction manager in JNDI at all by default. It can't assume you even have a JNDI. There is a JNDIManager class that you can use to bind the TransactionManager and UserTransaction into JNDI if you like. Just configure it to use the same locations that JBossAS does and then the JBossAS version of the hibernate lookup class should work.

          How are you going to do XAResource enlistment in the absence of a JCA, or do you have one?

          • 2. Re: Standalone JBossTS with Hibernate Shards
            david.pellegrini

            Thanks for your response jhalliday. Your first paragraph cleared away some of the fog. I guess I need to explicitly bind JBossTS's TransactionManager and UserTransaction implementations into JNDI, perhaps during the JUnit test setUp().

            Your second paragraph threw me a bit. I was trying to figure out a response that doesn't portray me as a complete noob, but it's probably too late for that... :-)
            I was working under the presumption (delusion?) that JBossTS was self-contained. What is this XAResource enlistment of which you speak? What's the quickest/easiest/simplest way to satisfy that for a JUnit testing environment such as what I'm building? I guess I have more reading to do on this subject, but a few sentences that cut to the chase would be very helpful to focus my efforts.

            Thanks again!

            David

            • 3. Re: Standalone JBossTS with Hibernate Shards
              jhalliday

              > I was trying to figure out a response that doesn't portray me as a complete noob, but it's probably too late for that... :-)

              No comment :-)

              > I was working under the presumption (delusion?) that JBossTS was self-contained.

              It is. But it's a self contained Transaction Manager, not a genie in a bottle that's going to jump out and write your code for you.

              > What is this XAResource enlistment of which you speak?

              'scuse me whilst I find a wall to bash my head on. You may want to go get yourself a nice strong coffee whilst you wait, the next bit is going to be tricky...

              Ready? Right then, here goes...

              XA TransactionManagers work in terms of coordinating XAResources. During the first phase of the transaction commit process, each resource is told to prepare, then during the second phase they are told to commit or, if something went wrong, to rollback.

              Notice anything interesting about that paragraph? Conspicuous by its absence is any mention of databases, datasources or connections. A TransactionManager cares naught for such things, it's only concerned with XAResources. As it happens for most applications those XAResources are associated to database connections, but they don't have to be. They may represent message queuing systems, distributed replication systems or pretty much anything else with transaction state.

              Lets focus on databases for a moment. JDBC provides Connections, which have commit() and rollback() methods. You can either get a Connection from the DriverManager, or via a DataSource. But give a Connection to a TransactionManager and it will sulk - there is no prepare() method, so a Connection can't be driven in two phase commit.

              There are two solutions to this. The first is a kludge: wrap the Connection in an XAResource implementation whose prepare() method calls commit() on the Connection and whose commit() method does nothing. Look up 'last resource gambit', 'last resource commit optimization, or 'LRCO' for more on this.

              The second way is to go back to the database driver and see if it supports XADataSource as well as DataSource. An XADataSource has the ability to provide XAConnections. These in turn supply XAResources and Connections, associating the two such that work done by the application over the Connection is committed or rolledback as determined by the Transaction Manager's interaction with the XAResource.

              In a full JEE app server like JBossAS you don't have to worry about any of this. The JBoss JCA maps <local-datasource> to the former method and <xa-datasource> to the latter. It automatically enlists XAResources with the transaction and does other clever stuff like making sure the right thing happens when you keep a connection handle open across transaction boundaries. Your app can merrily call getConnection on a datasource looked up via JNDI and expect the connection to behave properly in the transaction.

              So what to do when the app server is not around to do the heavy lifting for you? You need to either rework the code so it explicitly enlists XAResources with the TransactionManager for each database connection, or find something to do it for you. JBossTS comes with a TransactionalDriver that is one option. If you need a DataSource approach rather than a DriverManager one then you can wrap the TransactionalDriver, as the tomcat integration code does. Or you can also look at XAPool, although it does not handle asynchronous rollback properly.

              Got a headache now? Serves you right for asking noob questions :-)