1. 程式人生 > >說說如何在專案中引入 jBPM4 工作流框架以及遇到的坑兒

說說如何在專案中引入 jBPM4 工作流框架以及遇到的坑兒

由於各種原因,我們需要在專案中引入 jBPM4 工作流框架,遇到了不少問題,今記錄如下O(∩_∩)O

1 引入步驟

1.1 加入依賴包

  1. 非 Maven 專案,在 lib 包中加入 jbpm.jar。
  2. Maven 專案,加入以下配置:
<dependency>
    <groupId>org.jbpm</groupId>
    <artifactId>jbpm</artifactId>
    <version>${jbpm.version}</version>
</dependency
>

如果私服上沒有,可以自行作為第三方庫上傳到私服後,再配置 pom.xml。

1.2 整合到 Spring

<!-- jbpm 整合進 Spring -->
<bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper"
      lazy-init="default" autowire="default">
    <property name="jbpmCfg">
        <value>jbpm.cfg.xml</value
>
</property> </bean> <!-- 工作流引擎--> <bean id="processEngine" factory-bean="springHelper" factory-method="createProcessEngine"/>

名為 springHelper 的 Bean 中可以配置一個 jbpmCfg 引數,用於自定義 jbpm 配置檔案。該檔名為 jbpm.cfg.xml,預設放置在 classpath 路徑下。

1.3 配置 Hibernate

因為 jBPM4 使用的是 Hibernate 進行持久化操作,所以我們必須在此配置 jBPM4 持久化對映檔案:

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            <prop key="hibernate.temp.use_jdbc_metadata_defaults">${hibernate.temp.use_jdbc_metadata_defaults}</prop>
        </props>
    </property>
    ...

    <!-- 持久化 jBPM4 實體類-->
    <property name="mappingResources">
        <list>
            <value>jbpm.repository.hbm.xml</value>
            <value>jbpm.execution.hbm.xml</value>
            <value>jbpm.history.hbm.xml</value>
            <value>jbpm.task.hbm.xml</value>
            <value>jbpm.identity.hbm.xml</value>
        </list>
    </property>
</bean>

一般來說,通過以上步驟就可以通過注入,獲取到 jBPM4 的 processEngine 引擎啦O(∩_∩)O哈哈~

1.4 執行指令碼

在下載的 jbpm-4.4 包中,開啟 install\jdbc\ 資料夾,依據實際的資料庫型別,選擇相應的指令碼,初始化 jBPM 庫表:

2 相容 Hibernate4+

jBPM4 預設適配 Hibernate3,所以如果框架使用的是高版本的 Hibernate,那麼就必須修改 jBPM4 的原始碼做適配。

下面以 Hibernate4 為例,我們需要修改 jBPM4 這 SpringProcessEngine 與 HibernateSessionDescriptor 兩個類。

修改後的 jBPM4 原始碼如下:

1、SpringProcessEngine.java

public class SpringProcessEngine extends ProcessEngineImpl {

  private static final Log log = Log.getLog(SpringProcessEngine.class.getName());

  private static final long serialVersionUID = 1L;

  private ApplicationContext applicationContext;

  public static ProcessEngine create(ConfigurationImpl configuration) {
    SpringProcessEngine springProcessEngine = null;

    ApplicationContext applicationContext = null;
    if (configuration.isInstantiatedFromSpring()) {
      applicationContext = (ApplicationContext) configuration.getApplicationContext();

      springProcessEngine = new SpringProcessEngine();
      springProcessEngine.applicationContext = applicationContext;
      springProcessEngine.initializeProcessEngine(configuration);

      LocalSessionFactoryBean localSessionFactoryBean = springProcessEngine.get(LocalSessionFactoryBean.class);
      Configuration hibernateConfiguration = localSessionFactoryBean.getConfiguration();
      springProcessEngine.processEngineWireContext
          .getWireDefinition()
          .addDescriptor(new ProvidedObjectDescriptor(hibernateConfiguration, true));

      springProcessEngine.checkDb(configuration);

    } else {
      String springCfg = (String) configuration.getProcessEngineWireContext().get("spring.cfg");
      if (springCfg==null) {
        springCfg = "applicationContext.xml";
      }
      applicationContext = new ClassPathXmlApplicationContext(springCfg);
      springProcessEngine = (SpringProcessEngine) applicationContext.getBean
              ("jbpmProcessEngine");
    }

    return springProcessEngine;
  }

  public EnvironmentImpl openEnvironment() {
    PvmEnvironment environment = new PvmEnvironment(this);

    if (log.isTraceEnabled())
      log.trace("opening jbpm-spring" + environment);

    environment.setContext(new SpringContext(applicationContext));

    installAuthenticatedUserId(environment);
    installProcessEngineContext(environment);
    installTransactionContext(environment);

    return environment;
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T get(Class<T> type) {
    T candidateComponent = super.get(type);

    if (candidateComponent != null) {
      return candidateComponent;
    }

    String[] names = applicationContext.getBeanNamesForType(type);

    if (names.length >= 1) {

      if (names.length > 1 && log.isWarnEnabled()) {
        log.warn("Multiple beans for type " + type + " found. Returning the first result.");
      }

      return (T) applicationContext.getBean(names[0]);
    }

    return null;
  }

  @Override
  public Object get(String key) {
    if (applicationContext.containsBean(key)) {
      return applicationContext.getBean(key);
    }

    return super.get(key);
  }
}

2、HibernateSessionDescriptor.java

public class HibernateSessionDescriptor extends AbstractDescriptor {

    private static final long serialVersionUID = 1L;
    private static final Log log = Log.getLog(HibernateSessionDescriptor.class.getName());

    protected String factoryName;
    protected boolean useCurrent = false;
    protected boolean tx = true;
    protected boolean close = true;
    protected String standardTransactionName;
    protected String connectionName;

    public Object construct(WireContext wireContext) {
        EnvironmentImpl environment = EnvironmentImpl.getCurrent();
        if (environment == null) {
            throw new WireException("no environment");
        }

        // get the hibernate-session-factory
        SessionFactory sessionFactory = null;
        if (factoryName != null) {
            sessionFactory = (SessionFactory) wireContext.get(factoryName);
        } else {
            sessionFactory = environment.get(SessionFactory.class);
        }
        if (sessionFactory == null) {
            throw new WireException("couldn't find hibernate-session-factory " + (factoryName != null ? "'" + factoryName + "'" : "by type ") + "to open a hibernate-session");
        }

        // open the hibernate-session
        Session session = null;
        if (useCurrent) {
            if (log.isTraceEnabled()) log.trace("getting current hibernate session");
            session = sessionFactory.getCurrentSession();

        } else if (connectionName != null) {
            Connection connection = (Connection) wireContext.get(connectionName);
            if (log.isTraceEnabled())
                log.trace("creating hibernate session with connection " + connection);
            session = (Session)sessionFactory.openStatelessSession(connection);
        } else {
            if (log.isTraceEnabled()) log.trace("creating hibernate session");
            session = sessionFactory.openSession();
        }

        StandardTransaction standardTransaction = environment.get(StandardTransaction.class);
        if (standardTransaction != null) {
            HibernateSessionResource hibernateSessionResource = new HibernateSessionResource(session);
            standardTransaction.enlistResource(hibernateSessionResource);
        }

        return session;
    }

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

    public void setFactoryName(String factoryName) {
        this.factoryName = factoryName;
    }

    public void setTx(boolean tx) {
        this.tx = tx;
    }

    public void setStandardTransactionName(String standardTransactionName) {
        this.standardTransactionName = standardTransactionName;
    }

    public void setConnectionName(String connectionName) {
        this.connectionName = connectionName;
    }

    public void setUseCurrent(boolean useCurrent) {
        this.useCurrent = useCurrent;
    }

    public void setClose(boolean close) {
        this.close = close;
    }
}

3 相容 Activiti5+

你沒有看錯,有的專案就是這麼奇葩,已經有 Activiti5 咯,還需要整合進 jBPM4……

這兩套框架都是同一個架構師 Tom Baeyens 負責的,可謂是一脈相承,所以一些基本 Bean 的命名都是相同的,比如流程引擎 Bean 都叫做 processEngine。因此如果直接按照上述配置,就會出現 Spring Bean 命名衝突的問題。

1、重新命名 jBPM 工作流引擎 Bean

<!-- 工作流引擎-->
<bean id="jbpmProcessEngine" factory-bean="jbpmSpringHelper"
      factory-method="createProcessEngine"/>

2、在 注入時使用該名稱(比如這裡取名為 jbpmProcessEngine)

4 非 Spring 環境

是的,有的專案非常老,連 Spring 框架都沒有用,納尼……

可以寫一個工具類,把流程引擎物件作為常量返回:

public class WorkflowUtils {


    //工作流引擎
    private static ProcessEngine PROCESS_ENGINE;

    //配置檔案字首
    public static final String SPRING_CONFIG_PREFIX = "classpath:resources/";


    /**
     * 獲取工作流引擎
     *
     * @return
     */
    public static ProcessEngine getProcessEngine() {
        if (PROCESS_ENGINE == null) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext
                    (SPRING_CONFIG_PREFIX + "spring-hibernate.xml",
                            SPRING_CONFIG_PREFIX + "spring-jbpm" +
                                    ".xml");

            PROCESS_ENGINE = (ProcessEngine) applicationContext.getBean
                    ("jbpmProcessEngine");
        }
        return PROCESS_ENGINE;
    }
}

在此,我們利用 ApplicationContext 載入與 jBPM4 相關的配置檔案,然後初始化 ProcessEngine,並設定為常量。這樣,以後直接使用這個常量引擎物件就可以啦O(∩_∩)O哈哈~

只要有耐心、細心和恆心,沒有我們程式設計師解決不了的事兒O(∩_∩)O哈哈~