突然 mvn eclipse:m2eclipse が実行できなくなった

つい先日までまったく問題なく Maven のコマンドが実行できていたのですが、今日突然実行できなくなってしまいました。基本的なコマンドはおおよそ問題がないのですが、差し当たって実行できなかったのは次のコマンド。

mvn eclipse:m2eclipse

実行時のエラー内容はこんな感じです。

>mvn eclipse:m2eclipse
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Required goal not found: eclipse:m2eclipse in org.apache.maven.plugins:maven-eclipse-plugin:2.8
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 second
[INFO] Finished at: Tue Mar 02 13:55:09 JST 2010
[INFO] Final Memory: 7M/12M
[INFO] ------------------------------------------------------------------------

pom.xml の定義情報をもとに m2eclipse 用の設定ファイルを生成するアレです。少し調べてみたところ

mvn eclipse:clean
mvn eclipse:eclipse

は問題がないようです。
なんでだろう、などと思いながらローカルリポジトリの中 (.m2 以下) を見てみると、今日の日付で 2.8 というフォルダができている!もしや、と pom.xml の中身を確認すると、 maven-eclipse-plugin のバージョン指定がありませんでした。

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-eclipse-plugin</artifactId>
  <configuration>
    <buildcommands>
      <java.lang.String>org.eclipse.jdt.core.javabuilder</java.lang.String>
      <java.lang.String>org.maven.ide.eclipse.maven2Builder</java.lang.String>
    </buildcommands>
    <projectnatures>
      <java.lang.String>org.eclipse.jdt.core.javanature</java.lang.String>
      <java.lang.String>org.maven.ide.eclipse.maven2Nature</java.lang.String>
    </projectnatures>
  </configuration>
</plugin>

指定がないということなので自動的に最新バージョンを取得してきて、そのプラグインで実行しようとしたのでしょう。もしかしたら 2.8 にはバグがあるのかもしれませんね。と、ここまでわかったので、以前のバージョン (2.7) を明示的に指定するようにしました。

そして再度 mvn eclipse:m2eclipse を実行してみると…今度は BUILD SUCCESSFUL でした。ふぅ…こんなことでハマりたくないです。

Tomcat のライブラリを使用して SHA1 や MD5 のハッシュ値を計算する方法

コマンドプロンプトやターミナルで TOMCAT_HOME/bin に移動して次のコマンドを実行するだけです。

# ./digest.sh -a SHA1 hoge
hoge:31f30ddbcb1bf8446576f0e64aa4c88a9f055e3c
# ./digest.sh -a MD5 hoge
hoge:ea703e7aa1efda0064eaa507d9e8ab7e

今までハッシュ値求めるだけでも面倒だなぁ…なんて思っていましたが、これを知っていれば簡単ですね。

Firefox で Ajax 通信するときに文字化け発生

ここ数日ハマってしまっている問題が Firefox 3.5 で Ajax 通信したときに日本語文字列が文字化けしてしまってサーバサイドで正常に処理ができないというもの。

環境はこのような感じです。

現象

端的に言うとブラウザ上の入力項目に日本語を入れ、その内容を jQuery.ajax() でサーバにリクエストするとその内容が化けてしまうというものです。しかしこの化け方が奇妙で、これまでに見たことがありません。例えば、入力項目に「あああああ」と入れてリクエストすると「BBBBB」となってしまうのです。「あいうえお」だと「BDFHJ」。何か規則性がありそう?

調査

まずは jQuery.ajax() にて JSON 変換してリクエストしている部分に着目し、Firebug でリクエストヘッダを見てみました。すると、その時点では文字は化けていませんでした。

次に jQuery.ajax() のオプションを見直してみたところ、あることがわかりました。このアプリケーションではすべての Ajax リクエストを POST で行っているのですが、試しに GET に変更してみたところ文字化けしないのです。入力項目が複雑で、かつ送信するデータが非常に大きいため POST メソッドで送信せざるを得ないのです。

と、まぁまだ完全に原因を突き詰められているわけではなく、解決方法も見いだせていないのでこれからしばらくハマり続けなければなりません。もし何かわかったらまたエントリを書きたいと思います。

properties ファイルの値を動的に取得する方法

現在のプロジェクトではあまり一般的ではない、そして古い思想のフレームワーク (フレームワークと呼べるようなモノではなく、単に基盤といった感じ) を利用しているため、メッセージ出力部分が非常に貧相です。そこで Java の標準的なやり方でメッセージファイルを外出しにして、java.util.Properties クラスを利用して値を取得、表示するように実装しました。

このようなことは過去にも何度もやっているのですが、少しだけ悩んでしまいました。メッセージファイル (ここでは messages.properties) ファイルに次のような記述があった場合、{0} や {1} にどうやって任意の値を流し込んで、全体のメッセージを取得するか、ということです。

warn.1001 = 接続に失敗しました。
warn.1002 = このフィールドは {0}〜{1} の長さを持つ必要があります。

messages.properties 自体の読み込みは java.util.Properties クラスだけでできるのですが、フォーマットを加えるとなるとそうはいかないわけで、どうやるのかを忘れてしまいました。結果的には java.text.MessageFormat クラスを利用すれば解決です。

import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Properties;

public class MessageTester {

    public void run() throws Exception {
        // message.properties ファイル
        final InputStream inputStream = this.getClass().getClassLoader()
                .getResourceAsStream("messages.properties");

        Properties props = new Properties();
        // ファイルをロードする
        props.load(inputStream);

        // シンプルなメッセージ出力
        System.out.println(props.getProperty("warn.1001"));

        // 引数を与えてフォーマットしてメッセージ出力
        System.out.println(MessageFormat.format(props.getProperty("warn.1002"),
                "8", "32"));
    }

    public static void main(String[] args) throws Exception {
        MessageTester tester = new MessageTester();
        tester.run();
    }

}

実行結果はこちら。

接続に失敗しました。
このフィールドは 8〜32 の長さを持つ必要があります。

単純なメッセージ出力は

System.out.println(props.getProperty("warn.1001"));

これで大丈夫です。次にフォーマットの場合。

System.out.println(MessageFormat.format(props.getProperty("warn.1002"), "8", "32"));

MessageFormat#format は第1引数にフォーマット対象となる文字列を、それ以降の引数で流し込む値を指定してやります。第2引数は可変長引数として定義されていますので、必要なだけ与えてやることができます。ここでは "8" が {0} に、"32" が {1} に入り、結果的に「このフィールドは 8〜32 の長さを持つ必要があります。」と出力されます。

いつもやっていることなのにいざというときに思い出せない、こんなことで時間を使いたくないですね〜。ということで未来の自分のためのメモエントリでした。

TortoiseSVNをコマンドラインから実行する方法

現在参画しているプロジェクトでのソース管理は Subversion を使用しています。しかしひとつの生成物 (WAR) に対してリポジトリが3つ必要となり、同期作業などが非常に煩雑になってしまっています。当初は3つのリポジトリを何とか集約できないものかと試行錯誤してみたものの、使用している Web フレームワークと開発環境などの影響でどうしてもまとめることができませんでした。

調べた結果 TortoiseSVNコマンドラインから起動して半自動的に動かすことができることがわかりました。

今回のプロジェクトの構成

単純なアプリケーションであれば、いつも Subclipse を使って Eclipse プロジェクトごと同期を取るのですが、今回の構成の場合にはそのプロジェクト内に3つのソース管理リポジトリがあるわけなので、Subclipse は使わずに TortoiseSVN で個別管理しています。
(Eclipse プロジェクト内にあるリソースは全てフォルダリンクしているだけなので、実体はローカルマシンの各所に点在している状態です)

TortoiseSVN のインストール確認

当然ですが TortoiseSVN がインストールされていることが前提です。インストーラの入手はこちらから。
コマンドプロンプトを起動して次のコマンドが実行できることを確認します。コマンドが通れば TortoiseSVN のバージョン情報ダイアログが表示されます。

C:\Documents and Settings\xxxxx>TortoiseProc


TortoiseSVNコマンドプロンプトから実行してみる

次に試しに TortoiseSVNコマンドプロンプトから実行してみます。何かしらのリソースがチェックアウトされていて同期可能な状態であることが前提です。ここでは C:\foo\bar ディレクトリを更新するという想定のコマンドを示します。

C:\Documents and Settings\xxxxx>TortoiseProc /command:update /path:"C:\foo\bar"

実行すると更新内容を示すダイアログが表示されます(ここでは更新リソースがありませんが…)。

これではダイアログを閉じる作業が手動となってしまうのであまりよろしくありません。そこでコマンド引数を少し変更してやります。

C:\Documents and Settings\xxxxx>TortoiseProc /command:update /path:"C:\foo\bar" /closeonend:3

最後に追加した /closeonend:3 というのは「エラー、競合、マージがなければ自動で閉じる」という意味です。実行すると、先ほどは残ってしまっていたダイアログが(エラーなどがなければ)自動で閉じられます。

バッチファイルを作成する

コマンドプロンプトからの実行を踏まえて、SVN 更新するバッチファイルと SVN コミットするバッチファイルを作成します。

setenv.cmd
パス情報など、環境ごとに設定する内容を記述したファイル
autoupdate.cmd
SVN 更新するバッチファイル
autocommit.cmd
SVN コミットするバッチファイル
setenv.cmd の内容
set DIR1=C:\foo\bar
set DIR2=C:\hoge\resource
set DIR3=D:\svn\repos
autoupdate.cmd の内容
call setenv.cmd
TortoiseProc /command:update /path:%DIR1% /closeonend:3
TortoiseProc /command:update /path:%DIR2% /closeonend:3
TortoiseProc /command:update /path:%DIR3% /closeonend:3
autocommit.cmd の内容
call setenv.cmd
TortoiseProc /command:commit /path:%DIR1% /closeonend:3
TortoiseProc /command:commit /path:%DIR2% /closeonend:3
TortoiseProc /command:commit /path:%DIR3% /closeonend:3

ここで作成したバッチファイルを全て同じディレクトリに置いておけば、後は autoupdate.cmd や autocommit.cmd をダブルクリックするだけで3リポジトリに対して更新とコミットが行えます。ただし、コミットに関してはコミットコメントを入力する必要があるのでコミットダイアログが表示されっぱなしになります。もっと詳細なコマンドオプションはTortoiseSVN の自動化を参照してください。