SSJ整合jBPM4.3

来源:互联网 发布:java8 base64源码 编辑:程序博客网 时间:2024/05/23 15:52

原文地址

There have been a few discussion threads over using jBPM4 with JPA – with or without Spring. And a few “managed to work” kind of hacks came up. For example, prior to version 4.3, there was SpringConfiguration.setSessionFactory() where you inject the SessionFactory object obtained from the JPA entity manager factory. Another solution may be, you can have two sets of configurations – one for your regular business model and another for jBPM4. And you must admit none of these are really clean solutions.


  Ideally this should same as working with Hibernate-jBPM4. You create a session factory configuration and pass the jBPM-xxx.hbm.xml files to the session factory. A sample session factory may look like this:


<hibernate-configuration>
  <session-factory>
     <property name="hibernate.dialect">...</property>
     <property name="hibernate.format_sql">true</property>
     .

     .

     .

     <mapping />

    <mapping resource="myResource.hbm.xml" />

     <mapping resource="jbpm.repository.hbm.xml" />
     <mapping resource="jbpm.execution.hbm.xml" />
     <mapping resource="jbpm.history.hbm.xml" />
     <mapping resource="jbpm.task.hbm.xml" />
     <mapping resource="jbpm.identity.hbm.xml" />
  </session-factory>
</hibernate-configuration>


    Things should be similar with JPA as well:

<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/persistencehttp://java.sun.com/xml/ns/persistence/persistence" version="1.0">
    <persistence-unit name="sample">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <mapping-file>jbpm.execution.hbm.xml</mapping-file>
        <mapping-file>jbpm.repository.hbm.xml</mapping-file>
        <mapping-file>jbpm.task.hbm.xml</mapping-file>
        <mapping-file>jbpm.history.hbm.xml</mapping-file>
        <class>org.santanu.MyDomainPojo</class>
        <class>org.santanu.AnotherMyDomainPojo</class>
        <properties>
        ...
        </properties>
    </persistence-unit>
</persistence>
  
  The assumption here is that your JPA provider is hibernate.


   So let me list the steps we need to do to achieve this:
  1. The class I loved the most when it comes to handle db access in jBPM4 isorg.jbpm.pvm.internal.session.DbSession. By default we have an implementation of that interface,org.jbpm.pvm.internal.hibernate.DbSessionImpl. This implementation uses current hibernate session to do all database operations. But we want to use JPA APIs. So we need to have another implementation of DbSession that will use JPA APIs instead of Hibernate classes.
  2. Once we have the org.jbpm.pvm.internal.session.DbSession implementation we need configure jBPM to use this. So we need to replace <db-session/> with our own tag in jBPM configuration file.
  3. To support the tag in configuration we need to create binding and descriptor classes.
  4. We also need to configure entity manager instead of hibernate session. This involves creating the binding and descriptor classes.
  5. One more configuration is required for creating/accessing entity manager factory. But if the entity manager factory is created by some other component (if framework like Spring is there in our application then by all likelihood entity manager factory wil be created and managed by that) - then we may not need that. So if we assume Spring to be our container then we can give this configuration a skip.
    Ideally with these changes we should be able to use JPA instead of using Hibernate (directly). But when was the last time life has been ideal?? So there has to be some twist in the tail.org.jbpm.pvm.internal.session.DbSession appeared to be a nice way to implement "DAO" pattern (http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html).  And all other classes will go through the DbSession implementation to execute the queries. But that is not the case. There are classes which gets the Hibernate Session directly from environment and uses that. In fact there are quite a few such cases (a search in Eclipse has shown 117 direct access to hibernate session in jBPM4 code). It looks like the idea of DbSession was lost in translation. Or may be I did not get the idea correct, but whatever it is, it helps to channelize all the db operation through one such class.


    Changing 100+ classes not to use hibernate session is quite a task. And that will be a decently wide spread change on existing jBPM code. Lets minimize the impact on the code. We will have the regular org.jbpm.pvm.internal.wire.descriptor.HibernateSessionDescriptor with the only modification that it gets the session from the entity manager if we are using JPA.


    JPA Implementation of DbSession - We can have a DbSession implementation org.jbpm.pvm.internal.jpa.JpaDbSessionImplwhich somewhat followsorg.jbpm.pvm.internal.hibernate.DbSessionImpl and have the same set of methods implemented using Entity manager instead of Hibernate Session.

    Binding classes for JpaDbSession - We need a binding and a descriptor for JpaDbSession. This is just a part of our regular exercise to register a new configuration tag.  Both the binding and descriptor for this can be fairly simple.


public class JpaDbSessionBinding extends WireDescriptorBinding {
    public static final String TAG = "jpa-session";
    public JpaDbSessionBinding() {
          super(TAG);
    }
    public Object parse(Element element, Parse parse, Parser parser) {
         JpaDbSessionDescriptor descriptor = new JpaDbSessionDescriptor();
         return descriptor;
     }
}


public class JpaDbSessionDescriptor extends AbstractDescriptor {
    public Object construct(WireContext wireContext) {
        return new JpaDbSessionImpl();
    }

    public void initialize(Object object, WireContext wireContext) {
            EntityManager entityManager = wireContext.get(EntityManager.class);
            if (entityManager == null) {
                throw new WireException("couldn't find entity manager "
                   + (entityManagerName != null ? "'" + entityManagerName +                                                   "'" + "by type ")  + "to create db-session");
            }
            // inject the session
            ((JpaDbSessionImpl) object).setEntityManager(entityManager);
    }

    public Class<?> getType(WireDefinition wireDefinition) {
        return JpaDbSessionImpl.class;
    }
}

     The configuration we achieve here looks like this:

<jpa-session/>
    Binding classes for EntityManager -  We need to use JPA EntityManager instead of Hibernate Session. So we need to introduce another tag in the configuration file -
         <entity-manager/>
  
   This is supposed to replace hibernate session.
    But this one will be little more spicy than this. The primary reason is that there is no standard way to obtain the relevant EntityManager from the EntityManagerFactory. We miss methods like getCurrentSession() of Hibernate in JPA. We can use @PersistenceContext to get the EntityManager injected to a server managed component like Servlets or EJBs. The same technique works for Spring beans as well. So we need a Spring bean for Spring, some session bean or servlet for a JEE server managed application - in short for different environments we need different classes that can provide us the EntityManager.


    In such a scenario we can allow a class to be plugged in that can do the required lookup. Then we can evolve different possible lookup classes for different target environments and then automatically use the correct lookup class for the environment. We can focus on a Spring version of such EntityManager accessor class here.



    The interface for entity manager accessor looks like this:
package org.jbpm.pvm.internal.jpa;

import javax.persistence.EntityManager;
import org.jbpm.api.cmd.Environment;

/**
* @author Santanu
*/
public interface EntityManagerAccessor {
  /**
   * Implementing class should return the relevant entity manager.
   * @param environment
   * @return
   */
   public EntityManager getEntityManager(Environment environment);
}
    And a spring implementation of the same may be this:
package org.jbpm.pvm.internal.jpa;

import java.util.HashMap;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.jbpm.api.cmd.Environment;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;

/**
* @author Santanu
*/
public class SpringEntityManagerAccessor implements EntityManagerAccessor {
    @Override
    @SuppressWarnings("unchecked")
    public EntityManager getEntityManager(Environment environment) {
        EntityManagerFactory entityManagerFactory
                          = environment.get(EntityManagerFactory.class);
        return EntityManagerFactoryUtils
                .getTransactionalEntityManager(entityManagerFactory, new HashMap());
    }
}

   Rest of the binding and descriptor code are simple...
package org.jbpm.pvm.internal.wire.binding;

import org.jbpm.pvm.internal.wire.descriptor.JpaEntityManagerDescriptor;
import org.jbpm.pvm.internal.xml.Parse;
import org.jbpm.pvm.internal.xml.Parser;
import org.w3c.dom.Element;

/**
* @author Santanu
*/
public class JpaEntityManagerBinding extends WireDescriptorBinding {
    public static final String TAG = "entity-manager";
     
    public JpaEntityManagerBinding() {
        super(TAG);
    }

    /* (non-Javadoc)
     * @see org.jbpm.pvm.internal.xml.Binding
     *       #parse(org.w3c.dom.Element, org.jbpm.pvm.internal.xml.Parse,
     *                                   org.jbpm.pvm.internal.xml.Parser)
     */
    @Override
    public Object parse(Element element, Parse parse, Parser parser) {
        JpaEntityManagerDescriptor descriptor
                               = new JpaEntityManagerDescriptor();
        ...
        ...
        if (element.hasAttribute("accessor-class")) {
            descriptor.setProviderClassName(
                    element.getAttribute("accessor-class"));
        }
        if (element.hasAttribute("create-new")) {
            descriptor.setCreateNew(
                   Boolean.valueOf(element.getAttribute("create-new")));
        }
        ...
        return descriptor;
    }
}

package org.jbpm.pvm.internal.wire.descriptor;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.apache.commons.lang.StringUtils;
import org.jbpm.pvm.internal.env.EnvironmentImpl;
import org.jbpm.pvm.internal.jpa.EntityManagerAccessor;
import org.jbpm.pvm.internal.wire.WireContext;
import org.jbpm.pvm.internal.wire.WireDefinition;
import org.jbpm.pvm.internal.wire.WireException;

/**
* @author Santanu
*/
public class JpaEntityManagerDescriptor extends AbstractDescriptor {

    ...

   /* (non-Javadoc)
    * @see org.jbpm.pvm.internal.wire.Descriptor
    *         #construct(org.jbpm.pvm.internal.wire.WireContext)
    */
    @SuppressWarnings("unchecked")
    @Override
    public Object construct(WireContext wireContext) {
        EnvironmentImpl environment = EnvironmentImpl.getCurrent();
       if (environment==null) {
         throw new WireException("no environment");
       }
  
       EntityManagerFactory entityManagerFactory = null;
       if (StringUtils.isNotBlank(factoryName)) {
     entityManagerFactory
               = (EntityManagerFactory)wireContext.get(factoryName);
       } else {
       entityManagerFactory = environment.get(EntityManagerFactory.class);
       }
  
      if (entityManagerFactory == null) {
          throw new WireException("No entity manager factory found");
       }
  
       EntityManager entityManager = null;
       // here we fight to get the entity manager
       // entity manager can be obtained in multiple ways
      // it can be injected by the container
       // it can be a "resource local" entity manager, where onus is
       // on the application to manage entity manager
  
      // lets allow the user to plug in some codeto get the EntityManager
      if ((entityManager == null) && StringUtils.isNotBlank(providerClassName)) {
       try {
            Class providerClass = Class.forName(providerClassName);
            EntityManagerAccessor entityManagerProvider
                  = (EntityManagerAccessor)providerClass.newInstance();
            entityManager = entityManagerProvider.getEntityManager(environment);
        } catch (ClassNotFoundException e) {
            throw new WireException("Problem loading class " + providerClassName, e);
        } catch (InstantiationException e) {
            throw new WireException("Problem while creating object of type "
                                                   + providerClassName, e);
        } catch (IllegalAccessException e) {
            throw new WireException("Problem while creating object of type "
                                                  + providerClassName, e);
        }

        // else if we are allowed to create an entity manager and have a factory
        // somewhere in the wire context then we can easily create one
        if ((entityManager == null) && create) {
           entityManager = entityManagerFactory.createEntityManager();
        }
        ...
        ...
        returnentityManager;
    }

    ...
    ...
    public Class<?> getType(WireDefinition wireDefinition) {
        return EntityManager.class;
    }
}


    The last thing that is pending is the patch-up code we need to make the code dependent directly on hibernate session to work. We need to make sure that if the session is looked up we return the delegating session from the entity manager. For this we need a little change in the existing HibernateSessionDescriptor ans HibernateSessionBinding.We introduce a boolean to specify whether this is running in a JPA environment.

public class HibernateSessionBinding extends WireDescriptorBinding {

          public HibernateSessionBinding() {
             super("hibernate-session");
          }

          public Object parse(Element element, Parse parse, Parser parser) {
              HibernateSessionDescriptor descriptor = new HibernateSessionDescriptor();

              if (element.hasAttribute("jpa")) {
                  Boolean isJpa = XmlUtil.attributeBoolean(element, "jpa", false, parse);
                  descriptor.setJpa(isJpa);
                  //if its jpa then we need this much
                  if (isJpa) {
                       return descriptor;
                  }
              }
              ...
              ...
           }
          ...
          ...
      }
        
      public class HibernateSessionDescriptor extends AbstractDescriptor {
          ...
          ...
          //now we need some more if this class works to satisfy hibernate
          //session seekers
          //in a JPA environment - the strategy works only if the JPA
          //provider is hibernate
          protected boolean jpa = false;
                   
          public Object construct(WireContext wireContext) {
               ...
               if (jpa) {
                   return getSessionFromEntityManager(environment);
               }
               ....
               ....
          }

          private Session getSessionFromEntityManager(EnvironmentImpl environment) {
               EntityManager entityManager = environment.get(EntityManager.class);
               Session session = (Session)entityManager.getDelegate();
               return session;
          }
          ...
          ...
       }

    Putting these all together, the jBPM configuration with these changes look like this (only the modified parts are depicted here):

<transaction-context>
     ...
     <!--db-session/-->
     <jpa-session/>
     ...
     ...
     <transaction type="spring" />
     <entity-manager provider-class="org.jbpm.pvm.internal.jpa.SpringEntityManagerProvider"/>
     <hibernate-session jpa="true"/>
     ...
</transaction-context>

       
    I believe JPA wil be introduced in jBPM5. I just hope the point of smooth gel with hibernate in the process of moving to JPA is not ignored. Hibernate is stil and probably will by far remain the most popular and most mature ORM implementation in Java.

源代码提供

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 伤口旁边肿了怎么办啊 伤口痂掉了红肿怎么办 屁屁拉屎出血了怎么办 鞋小了一码怎么办妙招 长了毛周角化怎么办 孕妇拉不出来是怎么办 孕妇严重便秘拉不出来怎么办 10小孩肛裂出血怎么办 水痘结痂蹭掉了怎么办 水痘留下的黑印怎么办 水痘痂掉了有坑怎么办 出水痘留下的疤怎么办 宝宝出水痘抓破怎么办 水痘结痂碰掉了怎么办 结痂掉了有坑怎么办 水痘留下的红印怎么办 点痣留下的凹坑怎么办 得水痘留下坑了怎么办 脚磨破了化脓了怎么办 水痘被扣的破了怎么办 水痘睡觉压破了怎么办 水痘破了化脓了怎么办 外阴长水痘破了怎么办 脸上的水痘破了怎么办 小孩出水痘破了怎么办 50多岁子宫肌瘤怎么办 一到晚上肛门痒怎么办 肛门和外阴很痒怎么办 脚磕到了肿了怎么办 脚碰到了肿了怎么办 腿被车子撞肿了怎么办 肛门长了小疙瘩怎么办 屁眼长了个包怎么办 屁股上长了个肉球怎么办 过敏了全身都痒怎么办 吃了螃蟹全身痒怎么办 全身痒眼睛肿了怎么办 感染了hpv病毒怎么办16 高危型hpv阳性52怎么办 腰椎盘突出腿疼怎么办 腰椎间盘突出压迫神经怎么办