Announcement Announcement Module
Collapse
No announcement yet.
Standalone Java app with jbossjta/jbosscache/toplinkJPA/jtds xa datasource - Help Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Standalone Java app with jbossjta/jbosscache/toplinkJPA/jtds xa datasource - Help

    I am working on a J2SE standalone app that uses the following.

    Spring 2.5 - Mostly for dependency injection, orchestration, AOP, transactional scoping
    Toplink JPA - version 2 build 41 for ORM
    JTDS driver - XAEnabled(?) JTDSDataSource driver
    ActiveMQ JMS
    Jboss Cache 3.3

    We are evaluating between JBoss and Atomikos Transaction managers.
    Atomikos - version 3.5.4
    JBOSS JTA - version 4.6.1 GA

    We have the UT, TM, Spring JTATM, EntityManagerFactory etc all configured based on spring's documentation. The problem I am having right now is with using JBoss JTA.

    This is what I have done.
    1. I have the driver configured to be jboss's TrasactionalDriver.
    2. Since its a standalone j2se application, for jboss to find the XA Datasource, I have implemented the DynamicClass interface. Here I return an instance of net.sourceforge.jtds.jdbcx.JtdsDataSource.
    3. I give the name of this class for the DynamicClass property for TransactionalDriver.
    Spring configuration looks something like this below.
    <!-- Construct JBoss UserTransactionManager, needed to configure Spring -->
    <bean id="jbossTransactionManager" class="com.arjuna.ats.internal.jta.transaction.arj unacore.TransactionManagerImple">
    </bean>

    <!-- Also use JBoss UserTransactionImp, needed to configure Spring -->
    <bean id="jbossUserTransaction" class="com.arjuna.ats.internal.jta.transaction.arj unacore.UserTransactionImple">
    </bean>

    <!-- Configure the Spring framework to use JTA transactions from Atomikos -->
    <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTran sactionManager">
    <property name="transactionManager" ref="jbossTransactionManager" />
    <property name="userTransaction" ref="jbossUserTransaction" />
    <property name="transactionSynchronizationName" value="SYNCHRONIZATION_ALWAYS" />
    </bean>

    <bean id="dsProps" class="java.util.Properties">
    <constructor-arg>
    <props>
    <prop key="user">${jdbc.userName}</prop>
    <prop key="password">${jdbc.password}</prop>
    <prop key="DYNAMIC_CLASS">${jdbc.jboss.classname}</prop>
    </props>
    </constructor-arg>
    </bean>

    <bean id="jbossDataSource" class="org.springframework.jdbc.datasource.DriverM anagerDataSource">
    <property name="driverClassName">
    <value>com.arjuna.ats.jdbc.TransactionalDriver</value>
    </property>
    <property name="url" value="${jdbc.jboss.url}"/>
    <property name="connectionProperties">
    <ref bean="dsProps"/>
    </property>
    </bean>


    <bean id="xaDataSource" class="net.sourceforge.jtds.jdbcx.JtdsDataSource">
    <property name="databaseName" value="${jdbc.dbName}"/>
    <property name="serverName" value="${jdbc.dbHostName}"/>
    <property name="user" value="${jdbc.userName}"/>
    <property name="password" value="${jdbc.password}"/>
    </bean>

    4. I have set up MS Sql server for XA (all the service packs, MSDTC settings, role/user privelage settings.)
    5. I have transactional advices setup so that an "init" method in a DAO class is transactional (read-only actually, since all this does it read some info from the database for server startup purposes.
    6. The DAO class has toplink JPA's EntityManager injected into it. It's init method implementation will perform the following against 2 different entities. Something that looks like below.

    Query query1 = entityManager.createQuery(somejpasql)
    List<> entities = query.getResultsList()

    Query query2 = entityManager.createQuery(somejpasql)
    List<> entities = query1.getResultsList()


    When I boot up my application, I can see that the DynamicClass implementation is invoked, it connects to the database. But here is the problem. When my DAO bean is initialized, the init method is invoked. The first query above works. The second call, however, throws an exception.

    Internal Exception: java.sql.SQLException: ConnectionImple.registerDatabase - [com.arjuna.ats.internal.jdbc.enlistfailed] enlist of resource failed

    I debugged through jboss source code, and the problem is JTDSDataSource.start() is invoked from TransactionImple.enlistResource() and, that throws the following exception.

    javax.transaction.xa.XAException: XAER_DUPID: The XID identifies an existing transaction.

    This is what the stack looks like
    XASupport.raiseXAException(int) line: 680
    XASupport.xa_start(Connection, int, Xid, int) line: 191
    JtdsXAResource.start(Xid, int) line: 105
    TransactionImple.enlistResource(XAResource, Object[]) line: 660
    ConnectionImpleJDBC3(ConnectionImple).registerData base() line: 766
    ConnectionImpleJDBC3(ConnectionImple).prepareState ment(String) line: 178
    DatabaseAccessor.prepareStatement(DatabaseCall, AbstractSession) line: 1147
    SQLCall(DatabaseCall).prepareStatement(DatabaseAcc essor, AbstractRecord, AbstractSession) line: 597
    DatabaseAccessor.basicExecuteCall(Call, AbstractRecord, AbstractSession) line: 470
    DatabaseAccessor.executeCall(Call, AbstractRecord, AbstractSession) line: 437
    ServerSession.executeCall(Call, AbstractRecord, DatabaseQuery) line: 465
    IsolatedClientSession.executeCall(Call, AbstractRecord, DatabaseQuery) line: 137
    EJBQLCallQueryMechanism(DatasourceCallQueryMechani sm).executeCall(DatasourceCall) line: 213
    EJBQLCallQueryMechanism(DatasourceCallQueryMechani sm).executeCall() line: 199
    EJBQLCallQueryMechanism(DatasourceCallQueryMechani sm).executeSelectCall() line: 270
    EJBQLCallQueryMechanism(DatasourceCallQueryMechani sm).selectAllRows() line: 600
    EJBQLCallQueryMechanism(ExpressionQueryMechanism). selectAllRowsFromTable() line: 2240
    EJBQLCallQueryMechanism(ExpressionQueryMechanism). selectAllReportQueryRows() line: 2206
    ReportQuery.executeDatabaseQuery() line: 774
    ReportQuery(DatabaseQuery).execute(AbstractSession , AbstractRecord) line: 609
    ReportQuery(ObjectLevelReadQuery).execute(Abstract Session, AbstractRecord) line: 677
    ReportQuery(ObjectLevelReadQuery).executeInUnitOfW ork(UnitOfWorkImpl, AbstractRecord) line: 731
    RepeatableWriteUnitOfWork(UnitOfWorkImpl).internal ExecuteQuery(DatabaseQuery, AbstractRecord) line: 2219
    RepeatableWriteUnitOfWork(AbstractSession).execute Query(DatabaseQuery, AbstractRecord) line: 937
    RepeatableWriteUnitOfWork(AbstractSession).execute Query(DatabaseQuery, Vector) line: 909
    EJBQueryImpl(EJBQueryImpl).executeReadQuery() line: 346
    EJBQueryImpl(EJBQueryImpl).getResultList() line: 453


    Now, the whole init() method is marked for read-only transaction using a tx-advice. Why does the second call fail with this problem. Shouldnt the same connection/resource used for the second call too. Does JTDSDatasource have problems? Or am I missing some configuration for either JBoss JTA or spring JTATransactionManager wrapper or toplink JPA?

    Any help or pointers are appreciated.
    Thanks

  • #2
    Update:

    Looks like the problem is with the jtds XA datasource or the configuration for the datasource. I swapped the JTA transaction manager from Jboss Jta to Atomikos. And I get the same error, though, in this case the error is more clear. Instead of the "Cannot enlist resource" SQLException atmoikos's exception clearly says, the transaction id is duplicate.

    To remove JTDS from the picture, i now swapped the XA datasource from jtds to microsoft's XA enabled datasource. I am still using Atomikos JTA. Now, the duplicate transaction id problem is gone. However I see a new problem. This seems to be an issue with combination of toplink and atomikos. Atomikos jta logs says something like "waiting for a free connection in the pool" and then times out.

    I have set the max connection pool size to be 10, this is in the configuration for AtomikosDatasourceBean. One single call to entityManager.find() on an entity that has a few one-to-many relationships seems to run out of connections (In this case this one entity is mapped to a table with one to many relationships with 4 other tables in the database). From the MS jdbc driver log, I can see that a single JPA calls mulitiple get connections. If i bump up the max connections to the 100s, it seems to work.

    Atomikos documentation says that even if the connections are closed in the middle of a transaction, tx manager doesnt release it to the pool till the transaction is commited/completed, which kind of makes sense. But I would think toplink JPA implementation would use the same connection to fetch all the data from all the tables for this one entity, especially when its one single "find" call. Why would it use multiple connections to satisfy one single call. Seems awfully ineffecient, unless there is a configuration that toplink allows to change this behavior.

    I will most likely have to post this on the toplink/jpa forums. But if anybody has any pointers for this problem, please let me know.
    Thanks

    Comment


    • #3
      This is the atomikos documentation that talks about the tx manager hanging on to the connections till the commit.

      http://www.atomikos.org/Documentatio...ConnectionPool

      Comment


      • #4
        Connections in Toplink

        Hi,

        I have no experience with Toplink (though we can research for you if you want to buy developer support at Atomikos).

        However, this sounds like something related to the closing of connections. Would there be a way to configure Toplink to release ('close') pooled connections earlier than it seems to be doing?

        This would make a difference because Atomikos JDBC connections (once closed and returned to the pool) are reused by the same thread - if it is for one and the same ongoing transaction.

        HTH

        Comment

        Working...
        X