Friday, October 1, 2010

Sun Glassfish and Oracle XE Distributed Transactions (XA)

So, we're using Glassfish v2.1.1, currently with Oracle 10g XE, running on a Java 6 runtime, using ojdbc14.jar. And we wanted to use distributed transactions :) We are also using JBoss Seam 2.2, but that's unrelevant, fortunately.

We configured the connection pool to use oracle.jdbc.xa.client.OracleXADataSource, and we disabled 'Return non-transactional connections' of course. When we tried to access the database from our Seam-connected web tier, the following exceptions came up:

[#|2010-10-01T12:09:12.383+0200|INFO|sun-appserver2.1|javax.enterprise.system.container.ejb|_ThreadID=22;_ThreadName=httpSSLWorkerThread-8091-1;|
javax.ejb.EJBException: nested exception is: javax.transaction.SystemException: org.omg.CORBA.INTERNAL: JTS5031: Exception [org.omg.CORBA.INTERNAL:   vmcid: 0x0  minor code: 0 completed: Maybe] on Resource [rollback] operation.  vmcid: 0x0  minor code: 0  completed: No
javax.transaction.SystemException: org.omg.CORBA.INTERNAL: JTS5031: Exception [org.omg.CORBA.INTERNAL:   vmcid: 0x0  minor code: 0 completed: Maybe] on Resource [rollback] operation.  vmcid: 0x0  minor code: 0  completed: No
 at com.sun.jts.jta.TransactionManagerImpl.rollback(TransactionManagerImpl.java:350)
 at com.sun.enterprise.distributedtx.J2EETransactionManagerImpl.rollback(J2EETransactionManagerImpl.java:1150)
 at com.sun.enterprise.distributedtx.J2EETransactionManagerOpt.rollback(J2EETransactionManagerOpt.java:433)
 at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:3801)
 at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:3619)
 at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1388)
 at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1325)
 at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:205)
 at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:127)
javax.ejb.EJBException: nested exception is: javax.transaction.SystemException: org.omg.CORBA.INTERNAL: JTS5031: Exception [org.omg.CORBA.INTERNAL:   vmcid: 0x0  minor code: 0 completed: Maybe] on Resource [rollback] operation.  vmcid: 0x0  minor code: 0  completed: No
 at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1395)
 at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1325)
 at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:205)
 at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:127)
[#|2010-10-01T14:20:10.992+0200|WARNING|sun-appserver2.1|javax.enterprise.system.core.transaction|_ThreadID=18;_ThreadName=httpSSLWorkerThread-8091-1;_RequestID=4131aa28-3401-4edd-bf90-54f605bcbb8e;|JTS5041: The resource manager is doing work outside a global transaction
oracle.jdbc.xa.OracleXAException
 at oracle.jdbc.xa.OracleXAResource.checkError(OracleXAResource.java:1120)
 at oracle.jdbc.xa.client.OracleXAResource.start(OracleXAResource.java:249)
 at com.sun.gjc.spi.XAResourceImpl.start(XAResourceImpl.java:222)
 at com.sun.jts.jta.TransactionState.startAssociation(TransactionState.java:305)
 at com.sun.jts.jta.TransactionImpl.enlistResource(TransactionImpl.java:205)
 at com.sun.enterprise.distributedtx.J2EETransaction.enlistResource(J2EETransaction.java:607)
 at com.sun.enterprise.distributedtx.J2EETransactionManagerImpl.enlistResource(J2EETransactionManagerImpl.java:372)
 at com.sun.enterprise.distributedtx.J2EETransactionManagerOpt.enlistResource(J2EETransactionManagerOpt.java:144)
 at com.sun.enterprise.resource.SystemResourceManagerImpl.enlistResource(SystemResourceManagerImpl.java:98)
 at com.sun.enterprise.resource.PoolManagerImpl.getResource(PoolManagerImpl.java:216)
 at com.sun.enterprise.connectors.ConnectionManagerImpl.internalGetConnection(ConnectionManagerImpl.java:337)
 at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:189)
 at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:165)
 at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:158)
 at com.sun.gjc.spi.base.DataSource.getConnection(DataSource.java:108)
 at org.hibernate.connection.DatasourceConnectionProvider.getConnection(DatasourceConnectionProvider.java:92)
 at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446)
 at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
 at org.hibernate.jdbc.AbstractBatcher.prepareSelectStatement(AbstractBatcher.java:145)
 at org.hibernate.id.SequenceGenerator.generate(SequenceGenerator.java:96)
 at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122)
 at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
 at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
 at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
 at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
 at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:646)
 at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:620)
 at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:624)
 at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.jboss.seam.persistence.EntityManagerInvocationHandler.invoke(EntityManagerInvocationHandler.java:46)
 at $Proxy263.persist(Unknown Source)

When switching to ojdbc5.jar or ojdbc6.jar, a further detail came in the logs. The exception above doesn't explain the cause of the OracleXAException (in OracleXAResource.start). This seems to be fixed in later drivers, so in the logs we can see:

java.sql.SQLException: ORA-06550: line 1, column 13:
PLS-00201: identifier 'JAVA_XA.XA_START_NEW' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

Searching for this I got completely mislead. Forum entries say that there is no XA support in the XE version of 10g. Indeed, when I tried the whole stuff with Oracle 11g, it worked fine.

I wanted to try the stuff out myself to see how these XA handling looks like on the API level. I created a J2SE native app following the explanation and the code samples found here http://archive.devx.com/java/free/articles/dd_jta/jta-2.asp and here http://download.oracle.com/docs/cd/B14117_01/java.101/b10979/xadistra.htm. And they worked fine, on Oracle 10g XE, with ojdbc14.jar. So obviously, the 10g XE version does support XA.

But why is it then that under Glassfish, it tries to use some fancy JAVA_XA package? It's my habit to jump into the source code of anything I can find, and even read the Eclipse class view showing the JVM level code. So I found that from my J2SE stuff, a T4CXAConnection is returned from the OracleXADataSource, but somewhy under glassfish, it is OracleXAConnection. So why is this difference? Browsing the binary of the OracleXADataSource class I found two suspicious properties named useNativeXA and thinUseNativeXA. And indeed, under glassfish, they were both false, despite their default values of useNativeXA=false, thinUseNativeXA=true.

Searching for these properties I found some explanations here http://download-west.oracle.com/docs/cd/B19306_01/java.102/b14355/xadistra.htm#BGBBHCFC and here http://openesb-users.794670.n2.nabble.com/Definition-of-Oracle-XA-datasources-td4461045.html. So, the Glassfish admin console proposes a default value of the useNativeXA=false, and setting this property also disables thinUseNativeXA. So we either remove this property, or set it to true (the additional properties page of the connnection pool properties), and it'll work fine.

So, after all, I should just have read "Oracle® Database JDBC Developer's Guide and Reference" to be aware of this feature in the jdbc driver :) . Hope it'll help you guys.

3 comments:

  1. Yeah, hi. I've got some issue with my XA transactions on Glassfish 3 (not 2.1) using ojdbc6 Oracle driver. Can you confirm the exact place this property is set? I've been trying to set the property in the set up of the jdbc-connection-pool (which is an oracle.jdbc.xa.client.OracleXADataSource), is that right?

    I've tried setting property names of "useNativeXA", "thinUseNativeXA" "nativeXA" and "NativeXA" to "true" and none of it seems to make any difference to the behaviour of the driver.

    But no one is completely explicit about exactly what the property is named and where it is set.

    thanks.

    ReplyDelete
  2. Hi. The property is set under the 'Additional Properties' tab of the Connection Pool (under glassfish 2.1 at least). The name is "NativeXA" and its value is "true". The pool is an oracle.jdbc.xa.client.OracleXADataSource (of type javax.sql.XADataSource). The "Non Transactional Connections" property is not enabled.

    If this doesn't help, you might check the class of the actual connection returned (in debug mode).

    Please let me know if there are further problems!

    ReplyDelete
  3. Excellent thank you I will try it out tomorrow in the office and let you know how it goes.

    ReplyDelete