3 Replies Latest reply on Jun 10, 2010 9:59 AM by jelies

    Injected hibernate SessionFactory is not used by jBPM 4.3?

    helmers

      Hi there,

       

      I'm using jBPM 4.3 and I want jBPM to use my own SessionFactoryImpl object, instead of creating its own. I'm doing the following to inject my SessionFactory into jBPM:

       

      new Configuration().setResource("jbpm/jbpm.cfg.xml").setHibernateSessionFactory(hibernateSessionFactory).buildProcessEngine();
      But jBPM ignores what I pass in, and creates its own SessionFactory.

      Does anyone know what I should do to make this work? Is there an additional jBPM configuration property that I need to set? Could it be a bug in jBPM?

      I've attached a Maven project that you can use to debug this problem. I'll explain how you can debug it to see what's going wrong. If you want to know about the project structure (not really required to debug the jbpm problem), read the section at the end of this mail.
      Cheers,
      Guido
      Note: We're using Guice for dependency injection; dependencies are configured in the *Module classes. Warp-persist is used for transaction management in Guice. I'm not sure if guice and warp-persist are in the central maven repos, so I've put those libraries in the lib folder.


      How to debug

      Import the maven project in your IDE.

      (In Eclipse: mvn clean eclipse:clean eclipse:eclipse -Declipse.useProjectReferences=true -DdownloadSources=true -DdownloadJavadocs=true
      You may have to install the guice libraries in your local maven repository. Do a "mvn clean compile" to check whether the project is complete).

      Put a break point on the following lines / methods (basically everything in the code base related to a hibernate SessionFactory):
      1.   My code:
        1. JbpmLayerModule.provideProcessEngine()
      2.   jBPM code:
        1.   ConfigurationImpl.setHibernateSessionFactory(Object)
        2.   HibernateSessionDescriptor.construct(WireContext)
        3.   HibernateSessionFactoryDescriptor.construct(wireContext)

      Now run IssueServiceIntegrationTest.testCreate() in debug mode. On the above breakpoints, you'll see information similar to the following:
      [1.1] hibernateSessionFactory=org.hibernate.impl.SessionFactoryImpl@5c6c0a(id=92)
      [2.1] processEngineWireContext=WireContext "process-engine"(id=112)
      [2.2] [Indirectly called by CheckDbCmd.execute()] wireContext="transaction"(id=166)
      It's doing this: sessionFactory = environment.get(SessionFactory.class);

      When I look in the 'environment' variable, I see there are 2 contexts in there: A 'transaction' context and a 'process-engine' context. The 'transaction' context contains ZERO SessionFactory definitions. The 'process-engine' context contains TWO SessionFactory definitions:
      • org.hibernate.impl.SessionFactoryImpl->ProvidedObjectDescriptor (with .providedObject=the one I passed in)
      • org.hibernate.SessionFactory->HibernateSessionFactoryDescriptor (with .name="org.hibernate.SessionFactory", all other fields null).
      So my SessionFactory is available in the 'process-engine' context, under key "org.hibernate.impl.SessionFactoryImpl", which does not match the environment.get(SessionFactory.class), that's why we end up in the following HibernateSessionFactoryDescriptor...

      [2.3] and it's doing this:
      configuration = wireContext.get(Configuration.class);
      ...
      SessionFactory sessionFactory = configuration.buildSessionFactory();
      In other words, loading a new hibernate Configuration from scratch, and a new SessionFactory.

      Questions

      It seems that in [2.2] (that is HibernateSessionDescriptor.construct()) something's going wrong.
      • How can I have jBPM use the SessionFactory instance that was stored in the 'process-engine' WireContext, instead of using its own default mechanism to load hibernate configuration and construct a session factory?
      • Did I forget some configuration properties in the jbpm.cfg.xml?* Is it a bug in jBPM?
      • Isn't it scary that the 'process-engine' wireContext contain a mapping for 'SessionContext' and one for 'SessionContextImpl'. Depending on whether you're requesting the interface or implementation, you're obtaining a different session factory (I guess that's what this whole post is about...?)

      I do see that in HibernateSessionDescriptor, there's a 'factoryName' property. If this property is set, it will lookup the SessionFactory in the 'transaction' wireContext under that 'factoryName'. Two questions:
      1. Can I set the 'factoryName' somehow, from a jbpm configuration file? How?
      2. If so, will it make any difference? Because it will then be looking up the SessionFactory in the 'transaction' wireContext, instead of the 'process-engine' wireContext (in which my SesionFactory is contained).

      Background info on attached project

      The project is called 'jbpmtest'. It's a multimodule maven project, with the following (slightly simplified) dependency graph: [ service -> dao -> test -> domain -> commons ].


      (About the domain: There's only one class in the domain, called 'Issue'. We store every Issue locally, and in addition an Issue has its own jBPM process instance; This is all managed by the IssueService. But this isn't relevant to the jBPM problem really..)

      The Guice module structure (comparable to Spring's application
      context) is as follows:

      ServiceLayerIntegrationTestModule
        |
        |__ TestDataSourceModule
        |    |
        |    |__ JndiDataSourceCreator <-- Makes a datasource available under Jndi name 'jbpmtestDs'
        |
        |__ DataLayerModule
        |    |
        |    |__ bind(Configuration).to(HibernateConfigurationProvider) (*)
        |    |
        |    |__ PersistenceModule
        |         |
        |         |__ HibernatePersistenceModule (**)
        |
        |__ ServiceLayerModule
            |
            |__ JbpmLayerModule (***)

      (*) Reads hibernate.cfg.xml and combines it with some other (programmatic) properties into a hibernate Configuration.
      (**) Created with "PersistenceService.usingHibernate().across(UnitOfWork.TRANSACTION).buildModule()", using (*).
      (***) The wiring of jBPM beans is done in the JbpmLayerModule. It constructs a ProcessEngine singleton, which in turn is used to provide
      the actual jBPM services (like RepositoryService, ExecutionService, etc.).

      jBPM's ProcessEngine is created like this:

          @Provides
          @Singleton
          ProcessEngine provideProcessEngine(SessionFactory hibernateSessionFactory) {
              return new Configuration().setResource("jbpm/jbpm.cfg.xml")
                .setHibernateSessionFactory(hibernateSessionFactory).buildProcessEngine();
          }

      You can see that org.jbpm.api.Configuration is passed in a hibernateSessionFactory; it's the SessionFactory created by Guice (somewhere inside the PersistenceService).

      Please note that the whole Guice configuration isn't really relevant here, I just explained it for you to get a better understanding of the test project. The only interesting thing here is that JbpmLayerModule.provideProcessEngine() injects a SessionFactory object (provided by Guice!) into the jBPM Configuration, and that a later stage, jBPM seems to have constructed its own SessionFactory! I was hoping the SessionFactory to be a singleton, and that passing it into the jBPM Configuration causes jBPM to *not* read the hibernate.cfg.xml again and construct a second SessionFactory...
        • 1. Re: Injected hibernate SessionFactory is not used by jBPM 4.3?
          helmers

          As sketched in my story above, jBPM stores the SessionFactory in the "process-engine" WireContext (see ConfigurationImpl), but looks it up in the "transaction" WireContext. In my simple view of life, that explains why it doesn't work. So I've overridden the ConfigurationImpl.setHibernateTransactionFactory() to store the descriptor *also* in the "transaction" WireContext, like this:

           

           

          ProcessEngine processEngine = new ConfigurationImpl() {
               @Override
               public ConfigurationImpl setHibernateSessionFactory(Object hibernateSessionFactory) {
                    ProvidedObjectDescriptor descriptor = new ProvidedObjectDescriptor(hibernateSessionFactory, true);
                    getProcessEngineWireContext().getWireDefinition().addDescriptor(descriptor);
                    getTransactionWireDefinition().addDescriptor(descriptor);
                    return this;
               }
          }.setResource("jbpm/jbpm.cfg.xml").setHibernateSessionFactory(hibernateSessionFactory).buildProcessEngine();

           

           

          Now jBPM does use my injected SessionFactory (verified by doing the same debug session as described above; it's now obtaining the injected object wherever it's looking up a SessionFactory).

           

          I'm not very familiar with the different jBPM WireContexts, so I hope a jBPM expert can take a look at my 'workaround', and say some smart stuff about whether this is can be considered a bug in the standard ConfigurationImpl, or whether I'm doing something dangerous here with nasty side-effects...

           

          Thanks for your help,

           

          Guido

           

          ps: The problem described here: https://community.jboss.org/thread/150582 remains. I now have one SessionFactory, but it's still not possible to run my own stuff and jBPM stuff in one single transaction.

          • 2. Re: Injected hibernate SessionFactory is not used by jBPM 4.3?
            rebody

            Hi Guido,

              You should remove the <hibernate-configuration/> and <hibernate-session-factory/> tags from tx.hibernate.cfg.xml. If you don't do that, jBPM will overwrite Sessionfactory that you provided and create SessionFactory be itselft.

            • 3. Re: Injected hibernate SessionFactory is not used by jBPM 4.3?
              jelies

              Hi Guido,

              Guido Helmers wrote:

               

              I do see that in HibernateSessionDescriptor, there's a 'factoryName' property. If this property is set, it will lookup the SessionFactory in the 'transaction' wireContext under that 'factoryName'. Two questions:
              1. Can I set the 'factoryName' somehow, from a jbpm configuration file? How?

               

              I was debbugging the jbpm internal code and I found it when jbpm is parsing the config files. You should put the name of the session factory in the jbpm.tx.spring.cfg.xml:

               

              {code:xml}

              <transaction-context>

                <transaction type="spring" />

                <hibernate-session current="true" factory="factory_bean_name_here" />

              </transaction-context>

              {code}

               

              Hope this helps!

               

              Thanks {user:username=rebody} for the solution for transaction-manager name (see [this message|http://community.jboss.org/message/543979#543979]!)