SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

近未来の技術トレンドを先取り! 「Tech-Sketch」出張所

オープンソースのワークフローエンジン「Activiti」チュートリアル

近未来の技術トレンドを先取り! 「Tech-Sketch」出張所 第9回

4.業務データとActiviti側のデータを跨いだトランザクション管理の実現

 「3.アプリケーションの開発」で開発したアプリケーションは、トランザクション管理において問題が残っています。

 その問題とは、「業務データ(交通費申請テーブル)とActiviti側で管理されているデータで、別々のトランザクション管理になっていること」です。

 このため、交通費申請のデータ更新が成功した後でActiviti側のデータ更新が失敗した場合、データの整合性が失われてしまいます。

 今回、上記問題の解決策として「業務側のトランザクションマネージャをActiviti側と共用する」という方法を紹介したいと思います。

1)データアクセス設定の確認

業務データ側

 今回のアプリでは、Seasar2のJTA実装であるS2JTAを利用しています。

 以下のファイルに、データアクセスに関する設定を記述します。

src/main/resources/jdbc.dicon
<!-- for H2 -->
<component name="xaDataSource"
    class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
    <property name="driverClassName">
        "org.h2.Driver"
    </property>
    <property name="URL">
        "jdbc:h2:tcp://localhost:9092/demo;SCHEMA=ACTIVITI_TUTORIAL"
    </property>
    <property name="user">"sa"</property>
    <property name="password">""</property>
    <destroyMethod>
        @org.seasar.framework.util.DriverManagerUtil@deregisterAllDrivers()
    </destroyMethod>
</component>

<!-- コネクションプール -->
<component name="connectionPool"
    class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl">
    <property name="timeout">600</property>
    <property name="maxPoolSize">10</property>
    <property name="allowLocalTx">true</property>
    <destroyMethod name="close"/>
</component>

<component name="DataSource" 
           class="org.seasar.extension.dbcp.impl.DataSourceImpl" />

Activiti側

 以下のファイルに設定を記述しています。

src/main/resources/activiti.cfg.xml
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

    <!-- process engine configuration -->
    <bean id="processEngineConfiguration"
        class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcUrl"
            value="jdbc:h2:tcp:localhost/demo;SCHEMA=ACTIVITI_TUTORIAL;" />
        <property name="jdbcDriver" value="org.h2.Driver" />
        <property name="jdbcUsername" value="sa" />
        <property name="jdbcPassword" value="" />
        <property name="databaseSchemaUpdate" value="false" />
        <property name="jobExecutorActivate" value="false" />
        <property name="history" value="full" />
    </bean>
</beans>
補足9

 Activitiのプロセスエンジン生成時に参照される情報です。

 org.activiti.engine.impl.cfg.StandaloneProcessEngineConfigurationを使用しているため、プロセスエンジンのトランザクション管理は、業務データ側から独立しています。

2)データアクセス設定の修正

  • 修正対象ファイル:src/main/resources/activiti.cfg.xml

 以下のように修正してください。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

    <!-- (1)jndi lookup S2JTA Transaction Manager -->
    <jee:jndi-lookup id="s2TxManager" jndi-name="jta/TransactionManager">
        <jee:environment>
            java.naming.factory.initial=org.seasar.extension.j2ee.JndiContextFactory
        </jee:environment>
    </jee:jndi-lookup>

    <!-- (2)jndi lookup S2 DataSource -->
    <jee:jndi-lookup id="s2DataSource" jndi-name="jdbc/DataSource">
        <jee:environment>
            java.naming.factory.initial=org.seasar.extension.j2ee.JndiContextFactory
        </jee:environment>
    </jee:jndi-lookup>

    <!-- (3)process engine configuration -->
    <bean id="processEngineConfiguration" 
          class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
        <property name="dataSource" ref="s2DataSource" />
        <property name="transactionManager" ref="s2TxManager" />
        <!-- using externally managed transactions -->
        <property name="transactionsExternallyManaged" value="true" />
        <property name="databaseSchemaUpdate" value="false" />
    </bean>
</beans>

【解説】修正後のactiviti.cfg.xml

(1)jndi lookup S2JTA Transaction Manager

 JNDIを利用して、業務データ側で使っているものと同じ「S2JTA TransactionManager」を取得しています。

(2)jndi lookup S2 DataSource

 JNDIを利用して、業務データ側で使っているものと同じ「DataSource」を取得しています。

(3)process engine configuration

 Activitiのプロセスエンジン生成時に参照される情報です。

 org.activiti.cdi.CdiJtaProcessEngineConfigurationを使用するように変更しています。

 これにより、JTAによるトランザクション管理をactiviti側で行うことができるようになります。また、dataSourceとtransactionManagerのプロパティに(1)(2)で取得したものを指定しています。

3)修正後の動作確認

 申請時([申請]ボタン押下処理)に、Activiti側で例外が発生するようコードを変更してみましょう。

 tutorial.action.paperwork.TransportationInputAction#doApplyメソッド内でruntimeService.startProcessInstanceByKeyの第1引数に存在しないプロセス定義ファイルのIDを指定してみます。

runtimeService.startProcessInstanceByKey(
    "xxxxxxxxxxx",          // 存在しないプロセス定義ファイルのIDを指定
    Long.toString(transportation.transportationId),
    variableMap);
補足10

 上記コードをもとに交通費申請を行うと、Activiti側で例外が発生します。

 申請処理は、「(1)交通費申請テーブルへのデータ登録」→「(2)Activiti側のプロセス開始」という順番で実行されますが、(2)で例外が発生しても、(1)のデータ登録がロールバックされていることを確認できます。

補足11

 エラーが発生した際の画面表示。

【注意】

 今回のアプリケーションは、Activiti APIの説明にフォーカスするため、例外処理は一部簡略化した実装となっています。ご了承ください。

5.おわりに

 今回は、「交通費申請」という単純で分かりやすい業務を対象にワークフローアプリケーションを開発してみました。Activitiで用意されているAPIを利用すると、「プロセス定義ファイル」どおりにフローの制御が簡単に行えることがお分かりいただけたと思います。

 ただし、今回開発したアプリケーションは、実運用を考えるとまだまだ解決しなければならない課題が残っています。例を挙げると、以下のようなものです。

  1. 自分に承認依頼が届いている申請データのみを表示させるなどの画面の実装
  2. 「差戻し」「取下げ」など、ワークフローアプリにおいて一般的に登場しそうな機能の実装

 上記課題に対する具体的な解決策は、また別の機会に掲載したいと考えていますので、ご期待ください。

 次回のテーマは、『ScalaのWebアプリケーションフレームワークである「Play2」』です。

 まずは、開発環境の準備を中心に説明させていただき、以降の連載で「Play2を利用した簡単なアプリケーション」を開発したいと思います。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
近未来の技術トレンドを先取り! 「Tech-Sketch」出張所連載記事一覧

もっと読む

この記事の著者

佐藤 政美(TIS株式会社)(サトウ マサミ)

TIS株式会社 コーポレート本部 戦略技術センター所属。 TISのアプリケーション開発基盤『Xenlon~神龍』の開発および導入支援を中心に活動中。オープンソースのライブラリを活用し、アプリケーションを素早く構築することに興味があり、「開発現場に喜ばれるモノを提供したい!」と日々、Xenlonの改良・機能...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/7495 2013/11/27 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー