[BPMN] BPMN1.1 と BPMN1.2 の違い

BPMN(Business Process Modeling Notation) の新バージョン 1.2 が今月に入って公開されていたので、1.1 から何が変わったのかを調べてみた。1.0 から 1.1 は結構変わってたしなあ。

というわけで仕様書を見てみたのだが、はっきり言って違いがない。BPEL へのマッピングの説明のところはあまり細かくみていないが、少なくとも仕様の本体部分に関して言えば「何も違いはない」と言い切って良いと思う。ほとんどが文法の誤りの修正とかの些細な変更である。

一カ所だけ「仕様に関わる変更かな?」と思うところを見つけた。

* A source Gateway MUST NOT be of type Parallel.

[http://www.omg.org/spec/BPMN/1.1/PDF [9.5.3 Inclusive Gateways]

* A source Gateway MUST NOT be of type Parallel or Complex.

[http://www.omg.org/spec/BPMN/1.2/PDF [9.5.3 Inclusive Gateways]

これは条件付きシーケンスフローを使った場合のゲートウェイ種別に関する記述で、Complex ケートウェイであってもいけないという制約が追加されている。ただこれは意味を考えると当然で、もともと記述漏れがあったところを補っただけと考えられる。

というわけで、結論。

BPMN1.1 と BPMN1.2 は何も違いはない。

認識が間違っていたら誰か教えてください・・・

[Java] デフォルトロケール

Java では動作環境によってデフォルトのロケールエンコーディングが異なる。この違いによって、

  • ResourceBundle を利用したときのメッセージの言語
  • Reader/Writer を使うときのエンコーディング

が違ってくる。

ロケールは Locale.setDefault() メソッドでデフォルトを変更することができるが、JVM 起動時のシステムプロパティの設定によっても変更ができるようである。アプリケーション自体に変更を加えられないがデフォルトエンコーディングを変更したいときにはこの方法を使う。具体的には JVMコマンドラインオプションを以下のように設定する。

-Duser.language=en -Duser.region=US

こうすることで JVM 上でのデフォルトロケールが en_US になる。

同様に文字のエンコーディングを変えたいときには以下のように設定する。

-Dfile.encoding=UTF-8

ただ、ファイルのエンコーディングに関してはデフォルトエンコーディングに依存するコードはあまり書くべきじゃないかなと思う。これまで何回も動作環境を変えたときに文字化けが発生する問題に出くわした(自分が仕込んだんだが・・・)。

たとえばファイルを読み込むのに InputStreamReader を使うなら、

new InputStreamReader(in);

ではなく、

new InputStreamReader(in, "UTF-8");

のようにエンコーディングを明示的に指定する。これなら挙動が動作環境に依存しない。

SLF4J を使う

Hibernate のバージョンを 3.3 に上げたら、ロギングフレームワークが SLF4J に変わっていた。そのまま実行しようとすると以下のエラーが出た。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

SLF4J ではどのログ実装を使うかという指定を StaticLoggerBinder というクラスで静的に行うようだ。たとえば Log4J を使うなら Log4J 用の StaticLoggerBinder クラスを配置する。それぞれのログ実装毎に異なる StaticLoggerBinder クラスが用意されている。

これは commons-logging と比べると設定の柔軟性には欠けるのだけど、より安全であり問題が発生しにくいということらしい。実際そこまでの柔軟性は必要ないのでこれでなんら不都合はない。Hibernate を始め多くのプロジェクトが SLF4J に移行しているようだ。

そこで StaticLoggerBinder 実装を組み入れることにする。本当はこのタイミングでログ実装も LOGBack に変えてしまうのがいいのかもしれないけども、まだ同一プロジェクト内で commons-logging + Log4J を使っているところも多いので、とりあえず Log4J のまま使うことにした。Maven リポジトリを検索して、以下の dependency を追加する。

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.5.6</version>
		</dependency>

これで行けるだろうと思ったら、今度は以下のエラーが出た。

java.lang.IllegalAccessError: tried to access field org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory

これは slf4j-api とのバージョンのミスマッチが問題のようだ。hibernate 3.3 の依存ライブラリとして読み込まれた slf4j-api は 1.4.2 だったので、slf4j-log4j12 も 1.4.2 を使うようにした。

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.4.2</version>
		</dependency>

これで問題無くアプリケーションが実行できるようになった。

[Java] Java SE 6 では @Override アノテーションがインターフェイス実装メソッドで使える

最近 Java SE 6 を使っているが、JDK 1.4 から 1.5(5.0?) の劇的な変化に比べると大きな仕様変更はなく、正直なところそれほど違いを意識して使っていない。

ひとつ、本当にささいな点ではあるが微妙に便利になっているのが、@Override アノテーションの仕様。1.5 ではインターフェイスを実装したメソッドでは使えなかったのが、Java SE 6 では使えるようになっている。なんでこれまで使えなかったんだろうって感じ。

しかし、持っているノート PC が CoreDuo の MacBook で、Apple から JDK 6 が提供されていないのがつらい。32 bit 版を提供するのってそんなに難しいことなんだろうか。SoyLatte を試しに使っているが、JAXB まわりでうまく動かなかったりでちゃんと使えていない(原因は別かもしれないが)。

[Spring][Quartz] Quartz の Job から Spring の ApplicationContext にアクセスする

Spring には Quartz の統合が含まれており、簡単に Quartz のスケジューラが使えるようになっている。Spring 上で Quartz を動かすと、当然ジョブから ApplicationContext で定義したサービスを利用したいケースがでてくる。

Spring で Quartz を使うには SchedulerFactoryBean を利用するが、SchedulerFactoryBean には applicationContextSchedulerContextKey というプロパティが用意されており、これを設定すると指定されたキーで SchedulerContext に ApplicationContext が登録される。

  <bean id="scheduler"
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="applicationContextSchedulerContextKey">
      <value>applicationContext</value>
    </property>
  </bean>

こうしておくことで、「applicationContext」という名前で ApplicationContext が登録されるので、Job のほうから以下のようにして参照することができる。

public class FooJob implements Job {

    private static final String APPLICATION_CONTEXT_KEY = "applicationContext";

    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException {

        ApplicationContext applicationContext;
        try {
            applicationContext = (ApplicationContext)context.getScheduler().getContext().get(APPLICATION_CONTEXT_KEY);
        } catch (SchedulerException e) {
            throw new JobExecutionException("ApplicationContext の取得に失敗しました。", e);
        }
        ...
    }

}

[PostgreSQL] PostgreSQL での現在時刻取得

PostgreSQL で現在時刻を取得しようとして now() 関数を使うと、えらく古い時刻を返されることがある。

実は PostgreSQL の now() 関数は、その関数が評価されたタイミングの時刻ではなく、トランザクションを開始した時刻を返す。それはそれで使い道があるのかもしれないけど、知らないとはまる。

まあトランザクションの開始時刻でもほぼ現在時刻とかわらんでしょと思うかもしれないけど、コネクションプーリングの実装によっては、トランザクションを開始した状態でコネクションをプールさせておき、そのコネクションをそのまま提供するものがある(commons-dbcp がそうだった)。そうした場合、now() で返される時刻は最後にそのプールされたコネクションが返却された時刻になるので、相当古い時刻を返すことがある。

そうした問題があるためか、PostgreSQL は 8.2 でいくつかの種類の現在時刻を取得する関数が追加された。

transaction_timestamp()
now() と同じで、現在のトランザクションの開始時刻を返す。
statement_timestamp()
現在の問い合わせがサーバに届いた時刻を返す。したがって同じSQL文の中で複数呼び出しても同じ時刻が返ってくる。
clock_timestamp()
実際の現在時刻を返す。同じSQL文の中で複数呼び出しても異なる時間が返ってくる可能性がある。

より厳密な現在時刻を取得したいときなどは、これらの関数を使うことになる。

[Struts2] アクション名にスラッシュを許す

struts2 では、ネームスペース名とアクション名でアクセス時のパスが決定される。

http://HOST_NAME/CONTEXT_NAME/NAMESPACE_NAME/ACTION_NAME.suffix

ネームスペースには普通にスラッシュを含めて階層化できるけど、DefaultActionMapper を使っている場合にアクション名にスラッシュを含めることができるかどうかは設定による。関係のある設定項目は以下の2つ。

  • struts.enable.SlashesInActionNames
  • struts.mapper.alwaysSelectFullNamespace


1番目のものは名前の通りで、普通これだけでできると思う。けど期待通りに動作しなかったので調べてみると、2番目の設定が関係していた。DefaultActionMapper では struts.mapper.alwaysSelectFullNamespace が true になっているとネームスペース名としてスラッシュで終わる最長の文字列を決めうちで採用するので、実質アクション名にはスラッシュを含めることができない。これはデフォルトでは false になっているんだけど、codebihind-plugin の方で true に書き換えられていた。わかりにくいなあ。

そもそもなぜ struts.enable.SlashesInActionNames が設定項目になっているかというと、これを許可すると任意のパスからのデフォルトネームスペース("")にあるアクションを呼ぶときの挙動が変わってしまうかららしい。設定を false にしておくと任意のパスプレフィックスでデフォルトネームスペースのアクションを呼べたのが、設定を true になると呼べなくなってしまう。これを回避して呼び出し可能にするためには、デフォルトスペースのアクション名にワイルドカードを使って、"**/someAction" というふうにすれば良い。