I'm encountering a strange behaviour for which I have not been able to pinpoint the cause and was hoping that someone could help point me in the right direction.
I have an application running under JBoss EAP 7.0 (Wildfly 10), but only having the datasource specified at the container level. I am trying to fully manage the JDOPersistenceFactory from within the JEE (EJB) application.
My persistence.xml is configured as follows (located in my EJB jar in the META-INF/ folder:
<persistence-unit name="cache" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>java:jboss/datasources/DS</non-jta-data-source>
<mapping-file>package.jdo</mapping-file>
<exclude-unlisted-classes/>
<properties>
<property name="datanucleus.connection.resourceType" value="RESOURCE_LOCAL"/>
<property name="datanucleus.transaction.type" value="RESOURCE_LOCAL"/>
<property name="datanucleus.storeManagerType" value="rdbms"/>
<property name="datanucleus.schema.autoCreateAll" value="false"/>
<property name="datanucleus.ConnectionFactoryName" value="java:jboss/datasources/DS"/>
<property name="datanucleus.connectionPoolingType" value="dbcp2-builtin"/>
</properties>
</persistence-unit>
I have instantiated my connection factory statically as follows:
public class DataNucleusPersistenceManagerFactory {
// hack to provide access to the pmfs from a static context
private static Map<PersistenceUnit, PersistenceManagerFactory> pmf = new ConcurrentHashMap<>();
static{
PersistenceManagerFactory persistenceManagerFactory = JDOHelper.getPersistenceManagerFactory("cache");
pmf.put(PersistenceUnit.CACHE, persistenceManagerFactory);
}
/**
* Retrieve PersistenceManagerFactory from legacy code
* @param type
* @return
*/
public static PersistenceManagerFactory getPersistenceManagerFactory( PersistenceUnit type){
return pmf.get(type);
}
All my Stateless EJBs are defined as BeanManagedTransactions by extending a base class with the following interceptor defined:
@TransactionManagement(TransactionManagementType.BEAN)
public class BaseManagerBean implements BaseManager {
PersistenceManager pm;
@AroundInvoke
public Object log(InvocationContext inv) throws Exception {
pm = DataNucleusPersistenceManagerFactory.getPersistenceManagerFactory( CACHE );
pm.begin();
Object ret = inv.proceed();
pm.commit();
}
}
This allows for the logic for my bean method to simply use the pm within its logic without needing to worry about transaction boundaries/etc. I have simplified the interceptor for illustration purposes.
My issue that I am encountering is when I have nested EJBs (ie: one EJB which calls another). In this context, I end up with nested transactions, since each EJB method call will create a new PM and manage its own transaction.
When this happens, the inner transaction completes successfully, but the outer one throws and exception that:
Caused by: java.sql.SQLException: IJ031019: You cannot commit during a managed transaction
at org.jboss.jca.adapters.jdbc.BaseWrapperManagedConnection.jdbcCommit(BaseWrapperManagedConnection.java:1063)
at org.jboss.jca.adapters.jdbc.WrappedConnection.commit(WrappedConnection.java:834)
at org.datanucleus.store.rdbms.ConnectionFactoryImpl$EmulatedXAResource.commit(ConnectionFactoryImpl.java:734)
at org.datanucleus.transaction.ResourcedTransaction.commit(ResourcedTransaction.java:348)
at org.datanucleus.transaction.ResourcedTransactionManager.commit(ResourcedTransactionManager.java:64)
at org.datanucleus.TransactionImpl.internalCommit(TransactionImpl.java:417)
at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:288)
at org.datanucleus.api.jdo.JDOTransaction.commit(JDOTransaction.java:99)
... 128 more
Stepping through the code, falling into the IronJacamar libraries, I see that the ConnectionWrappers are treating this as an XA transaction. I also see that from the `org.datanucleus.store.rdbms.ConnectionFactoryImpl$EmulatedXAResource` resource which the DN CF creates.
This is where I get confused. I'm not sure why this is happening, nor if this is "normal"/expected behaviour. I have tried to reproduce this in a small test application by embedding 2 txs, with the same PU defined, but it works properly there. I have not yet tried embedding two EJBs in a test application.
I can only suspect that I have an errant configuration somewhere which is causing this, but cannot identify what. My Datasource is configured the same for my test application. Just for reference:
<datasource jndi-name="java:jboss/datasources/DS" pool-name="DS" enabled="true">
<connection-url>jdbc:sqlserver://${db.tx.host};databaseName=${adams.db.tx.name}</connection-url>
<driver>sqlserver</driver>
<pool>
<min-pool-size>5</min-pool-size>
<max-pool-size>2000</max-pool-size>
</pool>
<security>
<user-name>${adams.db.tx.username}</user-name>
<password>${adams.db.tx.password}</password>
</security>
<validation>
<check-valid-connection-sql>select getdate()</check-valid-connection-sql>
</validation>
</datasource>
<drivers>
<driver name="sqlserver" module="com.microsoft.sqlserver">
<xa-datasource-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</xa-datasource-class>
</driver>
</drivers>
What am I missing, doing wrong? Do I have a incorrect or missing configuration? What is DN / WF seeing this as an XA transaction? Or is this expected behaviour when working with nested EJBs due to the inherent stateless / pooling behaviour of EJBs? If the latter, am I forced to move to Container Managed Transactions such that no bean commits its own transaction? If not, how would I know which transaction is the outer transaction?
Thanks for any insights.
Eric