Search code examples
javahibernatejpadroolsbitronix

Cannot make Bitronix manage my datasource/transactions in Spring Boot project


I have been trying to make JPAKnowledgeService for about 3 days now, and I am quite close to giving up, it just seems as too much configuration and detail work for what it is/does. However,

I had this problem initially, which is gone away after I added

java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory

into my jndi.properties file, as the answer suggests. I was able to create a StatefulKnowledgeSession finally, and thought the work is over. But in drools chat, the same guy suggested that my transactions may have been being handled by Hibernate instead of Bitronix, which might make my persistence non-transactional altogether.

And I guess he is right since whenever I tried to insert an Object into the Knowledge session and call fireAllRules, I was stuck at:

executing transaction with 0 enlisted resource

followed by:

transaction timed out: a Bitronix Transaction with GTRID [3132372E302E312E310000000000AFB9D800000006], status=MARKED_ROLLBACK, 0 resource(s) enlisted (started Thu Jan 01 05:11:56 EET 1970)

After that what I changed is; I updated my persistence.xml as follows:

    <?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
 http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
    <persistence-unit  name="org.jbpm.persistence.jpa" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>java:comp/env/jdbc/jbpm</jta-data-source>
        <class>org.drools.persistence.info.SessionInfo</class>
        <properties>
            <property name="hibernate.jndi.class" value="bitronix.tm.jndi.BitronixInitialContextFactory"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <property name="hibernate.max_fetch_depth" value="3"/>
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionManagerLookup" />
        </properties>
    </persistence-unit>
</persistence>

added this line into my application.properties:

spring.datasource.jndi-name=java:comp/env/jdbc/jbpm

and gave a jndi name to my datasource for embedded tomcat by following these instructions.

and the error came back:

Caused by: java.lang.NullPointerException: null
    at org.drools.persistence.jta.JtaTransactionManager.getStatus(JtaTransactionManager.java:273) ~[drools-persistence-jpa-6.5.0.Final.jar:6.5.0.Final]
    at org.drools.persistence.jpa.AbstractPersistenceContextManager.getApplicationScopedEntityManager(AbstractPersistenceContextManager.java:78) ~[drools-persistence-jpa-6.5.0.Final.jar:6.5.0.Final]
    at org.drools.persistence.jpa.JpaPersistenceContextManager.getApplicationScopedPersistenceContext(JpaPersistenceContextManager.java:55) ~[drools-persistence-jpa-6.5.0.Final.jar:6.5.0.Final]
    at org.drools.persistence.SingleSessionCommandService.<init>(SingleSessionCommandService.java:103) ~[drools-persistence-jpa-6.5.0.Final.jar:6.5.0.Final]
    ... 43 common frames omitted

The tables related to JPAKnowledgeService are created in the database, so I guess my JNDI registration is successful, but I don't seem to be able to find Bitronix as my transaction manager since JtaTransactionManager seems as null. What am I doing wrong? I am frustrated and clueless.


Solution

  • Apparently, I didn't have to register my default datasource as JNDI with Tomcat, but make Bitronix directly manage it as follows:

    @Bean
    public PoolingDataSource setupPoolingDataSource() {
        PoolingDataSource pds = new PoolingDataSource();
        pds.setUniqueName("jdbc/jbpm");
        pds.setClassName("bitronix.tm.resource.jdbc.lrc.LrcXADataSource");
        pds.setMaxPoolSize(50);
        pds.setAllowLocalTransactions(true);
        pds.getDriverProperties().put("user", "username");
        pds.getDriverProperties().put("password", "password");
        pds.getDriverProperties().put("url", "jdbc:mysql://localhost/databaseName?useUnicode=yes&characterEncoding=UTF-8&useSSL=false");
        pds.getDriverProperties().put("driverClassName", "com.mysql.jdbc.Driver");
        pds.init();
        return pds;
    }
    

    and deleted these stuff:

    @Bean
    public TomcatEmbeddedServletContainerFactory tomcatFactory() {
        return new TomcatEmbeddedServletContainerFactory() {
    
            @Override
            protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                    Tomcat tomcat) {
                tomcat.enableNaming();
                return super.getTomcatEmbeddedServletContainer(tomcat);
            }
    
            @Override
            protected void postProcessContext(Context context) {
    
                ContextResource resource = new ContextResource();
                resource.setName(name);
                resource.setType(DataSource.class.getName());
                resource.setProperty("url", "...");
                resource.setProperty("username", "...");
                resource.setProperty("password", "...");
                resource.setProperty("driverClassName", "...");
                resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
                context.getNamingResources().addResource(resource);
            }
        };
    }
    
    @Bean
    public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
    
        JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
        bean.setJndiName("...");
        bean.setProxyInterface(DataSource.class);
        bean.setLookupOnStartup(false);
        bean.afterPropertiesSet();
        return (DataSource)bean.getObject();
    }
    

    everything else stays the same and it works!