0
votes

I am trying to develop jBPM spring boot application but getting Unable to lookup JNDI name [jdbc/jbpm-ds] error.

To achieve so, I have used jbpm-7.36.0.Final, Spring-Boot-2.2.6 and jbpm-spring-boot 7.36.0 Final along with spring boot Bitronix api. Below is my pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jta-bitronix</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <!-- <version>5.2.6.RELEASE</version> -->
    </dependency>
    <dependency>
        <groupId>com.microsoft.sqlserver</groupId>
        <artifactId>mssql-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.kie</groupId>
        <artifactId>jbpm-spring-boot-starter-basic</artifactId>
        <version>${runtime.version}</version>
    </dependency>
</dependencies>

In my application.properties file I have configured below values

#hibernate configuration
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.hbm2ddl.auto=update

#JTA enabled
spring.jta.enabled=true

#bitronix Non-XA transaction manager configuration
spring.datasource.xa.properties.url=jdbc:sqlserver://10.1.5.209:1433;databaseName=MRR3;
spring.datasource.xa.properties.uniqueName=jdbc/jbpm-ds
spring.datasource.xa.properties.username=sa
spring.datasource.xa.properties.password=password_123
spring.datasource.xa.properties.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.xa.data-source-class-name=bitronix.tm.resource.jdbc.lrc.LrcXADataSource

Below is the code snippet

    @Bean
    public StatefulKnowledgeSession newStatefulKnowledgeSession() throws Exception {
        RuntimeEnvironmentBuilder builder = null;
        if (usePersistence) {
            TransactionManager tm = TransactionManagerServices.getTransactionManager();

            entityManagerFactory = Persistence.createEntityManagerFactory("org.jbpm.persistence.jpa");//Line where Exception occurs
            System.out.println(entityManagerFactory.getProperties());

            Environment env = EnvironmentFactory.newEnvironment();
            env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);
            env.set(EnvironmentName.TRANSACTION_MANAGER, tm);

            builder = RuntimeEnvironmentBuilder.Factory.get().newDefaultBuilder()
                    .entityManagerFactory(entityManagerFactory)
                    .addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER, tm);

            builder.knowledgeBase(readKnowledgeBase);

            StatefulKnowledgeSession ksession = JPAKnowledgeService
                    .newStatefulKnowledgeSession(readKnowledgeBase, null, env);

            return ksession;
            // builder =
            // RuntimeEnvironmentBuilder.Factory.get().newDefaultBuilder()
            // .entityManagerFactory(entityManagerFactory);
        }
    }

Sharing important log info

2020-05-11 23:41:12.122  INFO 18852 --- [main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: org.jbpm.persistence.jpa
    ...] {} <>
2020-05-11 23:41:12.155  WARN 18852 --- [main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'newStatefulKnowledgeSession' defined in class path resource [com/citiustech/mrr/controller/AbstractionProcessController.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.kie.internal.runtime.StatefulKnowledgeSession]: Factory method 'newStatefulKnowledgeSession' threw exception; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] {} <>
.
.
.
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:275)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
    at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:179)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:119)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935)
    at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:56)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
    at com.citiustech.mrr.controller.AbstractionProcessController.newStatefulKnowledgeSession(AbstractionProcessController.java:51)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    ... 20 common frames omitted
Caused by: org.hibernate.engine.jndi.JndiException: Unable to lookup JNDI name [jdbc/jbpm-ds]
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.locate(JndiServiceImpl.java:100)
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.configure(DatasourceConnectionProviderImpl.java:98)
    at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:100)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:246)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.buildJdbcConnectionAccess(JdbcEnvironmentInitiator.java:145)
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:66)
    at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
    at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:94)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
    ... 39 common frames omitted
Caused by: javax.naming.NameNotFoundException: unable to find a bound object at name 'jdbc/jbpm-ds'
    at bitronix.tm.jndi.BitronixContext.lookup(BitronixContext.java:83)
    at bitronix.tm.jndi.BitronixContext.lookup(BitronixContext.java:67)
    at javax.naming.InitialContext.lookup(InitialContext.java:421)
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.locate(JndiServiceImpl.java:97)
    ... 48 common frames omitted

I am also interested to know if below understanding of mine is correct or not

  1. jBPM only supports JTA type transaction i.e. global transaction. You can't use RESOURCE_LOCAL transaction type while integrating jBPM.
  2. When I say transaction type RESOURCE_LOCAL, means only one datasource transaction will be shared across the application.
  3. jBPM RuntimeManager uses 'org.jbpm.domain' persistence unit name (Mandatory type) to handle all it's internal table operations.
  4. All the application specific transaction can be handled by a default persistence unit name i.e.'org.jbpm.persistence.jpa' . This can be override by a custom persistence context.
  5. JTA is further divided into two types XADatasource type and Non-XADatasource type. XADatasource should be use when datasource is in application server container. And Non-XADatasource is useful for EJB based application.
  6. XADatasource required below configuration

    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

    and

    <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform" /> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.WeblogicJtaPlatform" />

  7. Non-XADatasource required below configuration

    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    and

    <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>

  8. Is point 6 or 7 necessary, if so how to without using persistence.xml.

  9. What is the use of below property and what jar has this class

    spring.jpa.properties.hibernate.transaction.manager_lookup_class=org.hibernate.transaction.BTMTransactionManagerLookup

2
remove .xa from properties. Recently I'm trying to do the same with Hikari check my topic stackoverflow.com/questions/61735853/…Evgeniy
hi @Evgeniy, I removed the .xa.properties from the datasource configuration and re-run my application but facing the same issue. I think the bitronix transaction manager is of XADatasource type instead of Non-XADatasource type. So in case of XADatasource, it tries to fetch the Datasource connection from JNDI name 'jdbc/jbpm-ds'. But it is possible that if someway I tell bitronix TXM that use a Non-XADatasource then it will look for Datasource connection from application.properties and might resolve my issue.Satyaprakash Nayak
It seems you need to define that datasource in context.xml too. The jBPM very probably trying to identify the datasource through the container, and not via spring (in other words, the container does not know that you have a datasource within the application). Another seemingly useful link: persistence JBPM5.3 on tomcat with mysqlm4gic
@m4gic Yes, apparently you are correct. I also thought of that but it is true then there must be big blunder. Because I can clearly see jBPM is able to drop/create internal tables if I configure datasource at application level but when it comes to custom tables or database connection it is looking for datasource at server level. Anyway I really appreciate your suggestion and the link you have shared. The reference source is really useful, I will definitely try that and let you if it's working or not.Satyaprakash Nayak

2 Answers

2
votes

There are some misunderstandings:

  • jbpm-spring-boot-starter-basic is being used but, you are not using any spring loaded bean to create your StatefulKnowledgeSession. The main idea of a starter is avoid low level frameworks configurations.
  • This parameter: spring.datasource.xa.properties.uniqueName does not appear in documentations and is camelCase.

Solution 1 : Spring boot

Use official version and its configurations

#data source configuration
spring.datasource.username=sa
spring.datasource.password=sa
spring.datasource.url=jdbc:h2:./target/spring-boot-jbpm;MVCC=true
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.type=org.apache.tomcat.jdbc.pool.XADataSource

#hibernate configuration
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

#transaction manager configuration
spring.jta.narayana.transaction-manager-id=1

As we can see, jbpm-spring-boot-starter-basic relies on spring-boot datasource engine without any new database configuration. Why? Because StatefulKnowledgeSession just need a classic database connection.

Maybe this starter initialize a StatefulKnowledgeSession ready to use in any spring java bean with an @Autowire

Solution 2 : Spring + manual configurations

A simple research shows me, to create a StatefulKnowledgeSession from a JPAKnowledgeService are required the classic:

EntityManagerFactory and TransactionManager

Also this is seen in your code :

Environment env = EnvironmentFactory.newEnvironment();
            env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);
            env.set(EnvironmentName.TRANSACTION_MANAGER, tm);

And in this example:

http://www.javased.com/index.php?source_dir=droolsjbpm-integration/drools-container/drools-spring/src/main/java/org/drools/container/spring/beans/StatefulKnowledgeSessionBeanFactory.java

public static class JpaConfiguration {
  private EntityManagerFactory emf;
  private PlatformTransactionManager tm;
}
//..
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY,
jpaConfiguration.getEntityManagerFactory() );
env.set( EnvironmentName.TRANSACTION_MANAGER,
jpaConfiguration.getPlatformTransactionManager() );

So the solution could be just configure a spring boot to have an elegant autowires:

//@SpringBootApplication or @Configuration
public class SpringMainApplication{
  @Autowire
  private EntityManagerFactory emf;
  @Autowire
  private PlatformTransactionManager tm;
  @Autowire
  private KnowledgeBase kbase;

  @Bean
  public KnowledgeBase readKnowledgeBase() throws Exception {
    List<RuleResource> resources = new ArrayList<RuleResource>();
    resources.add(new RuleResource("zzz/explore/ruleengine/rules/CalcPI.drl", ResourceType.DRL));
    return KnowledgeBaseHelper.readKnowledgeBase(resources);
  }

  @Bean
  public StatefulKnowledgeSession newStatefulKnowledgeSession() throws Exception {
    //if KnowledgeBase autowire does not works, just call directly
    //KnowledgeBase kbase =  readKnowledgeBase();
    Environment env = KnowledgeBaseFactory.newEnvironment();
    env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf);
    env.set( EnvironmentName.TRANSACTION_MANAGER,tm);

    StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession(kbase, null, env);
    return ksession;
  }

}

Now, we need configure spring boot this instances ready to use:

@Autowire
private EntityManagerFactory emf;
@Autowire
private PlatformTransactionManager tm;

We can do that with:

//@SpringBootApplication or @Configuration
class SpringMainApplication {

  //spring boo twill auto-provide a Bean of type DataSource
  // if the properties are there
  @Autowired
    private DataSource dataSource;

  @Bean
  public EntityManagerFactory entityManagerFactory() {

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.acme.domain");
    factory.setDataSource(dataSource);
    factory.afterPropertiesSet();

    return factory.getObject();
  }

  @Bean
  public PlatformTransactionManager transactionManager() {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory());
    return txManager;
  }
}

There are a lot of documentations and examples to create an EntityManagerFactory and TransactionManager. Check references section.

Also maybe this configurations are not required if you use appropriately the starter

Solution 3 : Manual instances creation

As you can see, just an entitymanager and transactionmanager are required. Also to create an entitymanager a valid Datasource is required. So you just need something like:

//@SpringBootApplication or @Configuration
public class SpringMainApplication{

  @Bean
  public KnowledgeBase readKnowledgeBase() throws Exception {
    List<RuleResource> resources = new ArrayList<RuleResource>();
    resources.add(new RuleResource("zzz/explore/ruleengine/rules/CalcPI.drl", ResourceType.DRL));
    return KnowledgeBaseHelper.readKnowledgeBase(resources);
  }

  @Bean
  public StatefulKnowledgeSession newStatefulKnowledgeSession() throws Exception {

    //Crete datasource
    //https://gist.github.com/jrichardsz/8b8dffcf1cdf42444654abc227d9f4c1
    //or use the spring provided datasource

    //create an entity manger
    EntityManagerFactory emf = ...;
    //create an transaction manger
    PlatformTransactionManager tm =....;

    Environment env = KnowledgeBaseFactory.newEnvironment();
    env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf);
    env.set( EnvironmentName.TRANSACTION_MANAGER,tm);

    StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession(kbase, null, env);
    return ksession;
  }

}

}

Note

  • These snippets are not tested becasuse a drools and database is not easy to configure and you have not shared a minimal runable example.

  • I will add the http link of this research

References

0
votes

As suggested by @JRichardsz, I made below changes in my code to make it work with Spring Boot

@Autowired
private AbstractPlatformTransactionManager jtaPlatform;

@PersistenceUnit(name = "org.jbpm.persistence.jpa")
private EntityManagerFactory entityManagerFactory;

@Bean
public StatefulKnowledgeSession newStatefulKnowledgeSession() throws Exception {
    RuntimeEnvironmentBuilder builder = null;
    if (usePersistence) {
        builder = RuntimeEnvironmentBuilder.Factory.get().newDefaultBuilder()
                .entityManagerFactory(entityManagerFactory)
                .addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER, jtaPlatform);

        builder.knowledgeBase(readKnowledgeBase);

        RuntimeManager manager = RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(builder.get());

        return (StatefulKnowledgeSession) manager.getRuntimeEngine(EmptyContext.get())
                .getKieSession();
    } 
}

For me xa.properties works along with Bitronix, but if it does not work for you then you can switch to Spring provided datasource.