slim3-archetype-quickstartを使ってNetBeans用のslim3Mavenプロジェクトを作ってみた

slim3-archetype-quickstartと、maven-gae-pluginを使ってNetBeansslim3Mavenプロジェクトを構築してみました。
この手順でできるのは、アーキタイプで作成されたプロジェクトをローカルで動かして動作確認するところまでです。デプロイはまた後日。

2010/4/30追記
つぎのようなデメリットがあることに気がつきましたorz。ばりばり作っていきたい場合はeclipseで開発した方がいいかも。

  • HOTリローディングできない。(確認したわけじゃないですが、maven使ってるのでおそらくムリでしょう。)
  • controllerやmodel、serviceを手動で作成しなければいけない。(slim3が提供しているblankプロジェクトの場合は、antスクリプトでやってくれます。)

1.slim3-archetype-quickstartでプロジェクトを作成する。

archetype-pluginを使用するの手順1から手順5までを実行してください。

1.コマンドラインから以下を実行します
mvn archetype:generate -DarchetypeCatalog=http://slim3.googlecode.com/svn/trunk/repository
2.どのプロジェクトを作成するかを求められるので、slm3-archetype-quickstart を意味する "1" を入力します。注意)現在は選択肢が(1)以外ありません。
3.続けてgroupId, artifactId, version, packageの入力を求められるので適宜入力します。
4.最後に、入力した値の確認を求められるので、問題なければ "Y" を入力します。
5.maven用のslim3プロジェクトが作成されます。

2.NetBeansで作成したプロジェクトを開く。

eclipse:eclipseゴールを実行せずに作成したプロジェクトを開きます。

3.pom.xmlを編集してeclipse用の設定を削除する。

3.1.プロジェクトに名前を付ける

mavenのプロジェクト名が設定されていないので設定します。

<project>
    ...
    <name>slim3example</name>
    ...
</project>
3.2.maven-eclipse-pluginの記述を削除する。

eclipseと連携する必要がないので、maven-eclipse-pluginの記述を削除します。

3.3.maven-dependency-pluginの記述を削除する。

mavenで実行する分には、依存ライブラリなどのコピーは不用なので、こちらも同様に削除します。
不用になったeclipse.libプロパティも削除します。

3.4.maven-clean-pluginの記述を以下のように変更する。

slim3のテスト実行時にbuild以下に残ったファイルを削除するため以下のように変更します。

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>build</directory>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
3.5.utf-8の箇所をプロパティに置換する。

utf-8エンコーディング設定が複数箇所にあるので、以下のようなプロパティを参照するように置き換えます。

<property>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</property>

の二箇所の「utf-8」を「${project.build.sourceEncoding}」に置換します。

3.6.generated.srcプロパティをtarget/apt_generatedに変更する。

cleanゴール実行時に削除されるようにするため、「.apt_generated」を「target/apt_generated」に変更します。

<property>
    <generated.src>target/apt_generated</generated.src>
</property>

4.pom.xmlを編集してmaven-gae-plugin用の設定を追加する。

4.1.repositories、pluginRepositoriesにmaven-gae-pluginのレポジトリを追加する。

slim3のレポジトリ設定にmaven-gae-plugin用のレポジトリ設定を付け加えると以下のようになります。

    <repositories>
        <repository>
            <id>maven.seasar.org</id>
            <name>The Seasar Foundation Maven2 Repository</name>
            <url>dav:https://www.seasar.org/maven/maven2</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <updatePolicy>never</updatePolicy>
            </releases>
        </repository>
        <repository>
            <id>maven-gae-plugin-repo</id>
            <name>maven-gae-plugin repository</name>
            <url>http://maven-gae-plugin.googlecode.com/svn/repository</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>maven-gae-plugin-repo</id>
            <name>maven-gae-plugin repository</name>
            <url>http://maven-gae-plugin.googlecode.com/svn/repository</url>
        </pluginRepository>
    </pluginRepositories>
4.2.maven-gae-pluginの記述を追加する。

最後のほうで実行されるプラグインなので、maven-clean-pluginの前に追加しておくとわかりやすいかもしれません。

            <plugin>
                <groupId>net.kindleit</groupId>
                <artifactId>maven-gae-plugin</artifactId>
                <version>0.5.7</version>
                <configuration>
                    <unpackVersion>${gae.version}</unpackVersion>
                </configuration>
            </plugin>
4.3.maven-gae-plugin用のプロパティを設定する。
  • gae.homeを追加。
  • pom.xml内のappengine.versionをgae.versionに置換。

slim3で使っているappengine.versionのプロパティ名を変更したのは、maven-gae-pluginの設定にあわせてたためです。

この時点でのプロパティはつぎのようになってます。

    <properties>
        <slim3.version>1.0.3</slim3.version>
        <gae.version>1.3.3.1</gae.version>
        <gae.home>
            ${settings.localRepository}/com/google/appengine/appengine-java-sdk/${gae.version}/appengine-java-sdk-${gae.version}
        </gae.home>
        <generated.src>target/apt_generated</generated.src>
        <generated.war>war</generated.war>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

${appengine.version}を${gae.version}に置換したのは次の箇所です。

  • appengine-api-stubs
  • appengine-local-runtime
  • appengine-testing
4.4.maven-war-pluginを追加する。
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1-alpha-2</version>
                <configuration>
                    <webResources>
                        <resource>
                            <directory>src/main/webapp</directory>
                            <filtering>true</filtering>
                            <includes>
                                <include>**/appengine-web.xml</include>
                            </includes>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>
4.5.warディレクトリを「webapp」という名前に変更し、src/mainディレクトリに移動する。

maven-war-pluginのデフォルトのプロジェクト構成では、src/main/webappがwar用のファイルを格納するディレクトリになるためです。
NetBeansのファイルビューでは操作できなかったため、Explorerから変更しました。

変更後のディレクトリ構造は次の通りです。

    pom.xml
    src
    +---main
    |   +---java
    |   |   +---tutorial
    |   |       +---slim3example
    |   |           +---controller
    |   |           |   +---IndexController.java
    |   |           |
    |   |           +---model
    |   |           |   +---Slim3Model.java
    |   |           |
    |   |           +---service
    |   |               +---Slim3Service.java
    |   |
    |   +---resources
    |   |   +---logging.properties
    |   |
    |   +---webapp
    |       +---WEB-INF
    |           +---appengine-web.xml
    |           +---queue.xml
    |           +---web.xml
    |
    +---test
        +---java
            +---tutorial
                +---slim3example
                    +---controller
                    |   +---IndexControllerTest.java
                    |
                    +---service
                        +---Slim3ServiceTest.java

4.6.buildのoutputdirectory設定を削除する。

warディレクトリに出力する必要がなくなり、またmavenのデフォルト設定で問題なかったので削除しました。

        <outputDirectory>${generated.war}/WEB-INF/classes</outputDirectory>

ここまでの手順を適用したpom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>tutorial</groupId>
    <artifactId>slim3example</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>slim3example</name>
    <properties>
        <slim3.version>1.0.3</slim3.version>
        <gae.version>1.3.3.1</gae.version>
        <gae.home>
            ${settings.localRepository}/com/google/appengine/appengine-java-sdk/${gae.version}/appengine-java-sdk-${gae.version}
        </gae.home>
        <generated.src>target/apt_generated</generated.src>
        <generated.war>war</generated.war>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <repositories>
        <repository>
            <id>maven.seasar.org</id>
            <name>The Seasar Foundation Maven2 Repository</name>
            <url>dav:https://www.seasar.org/maven/maven2</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <updatePolicy>never</updatePolicy>
            </releases>
        </repository>
        <repository>
            <id>maven-gae-plugin-repo</id>
            <name>maven-gae-plugin repository</name>
            <url>http://maven-gae-plugin.googlecode.com/svn/repository</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>maven-gae-plugin-repo</id>
            <name>maven-gae-plugin repository</name>
            <url>http://maven-gae-plugin.googlecode.com/svn/repository</url>
        </pluginRepository>
    </pluginRepositories>

    <dependencies>
        <dependency>
            <groupId>org.slim3</groupId>
            <artifactId>slim3</artifactId>
            <version>${slim3.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slim3</groupId>
            <artifactId>slim3-gen</artifactId>
            <version>${slim3.version}</version>
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.ant</groupId>
                    <artifactId>ant</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-stubs</artifactId>
            <version>${gae.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-local-runtime</artifactId>
            <version>${gae.version}</version>
            <scope>test</scope>
        </dependency>
    <!--
    <dependency>
    <groupId>com.google.appengine</groupId>
    <artifactId>appengine-testing</artifactId>
    <version>${gae.version}</version>
    <scope>test</scope>
    </dependency>
    -->
    </dependencies>
    <build>
        <finalName>slim3-maven</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <version>1.4</version>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>apt-maven-plugin</artifactId>
                    <version>1.0-alpha-3</version>
                    <dependencies>
                        <dependency>
                            <groupId>org.slim3</groupId>
                            <artifactId>slim3-gen</artifactId>
                            <version>${slim3.version}</version>
                        </dependency>
                    </dependencies>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${generated.src}</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <outputDirectory>${generated.src}</outputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>process</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1-alpha-2</version>
                <configuration>
                    <webResources>
                        <resource>
                            <directory>src/main/webapp</directory>
                            <filtering>true</filtering>
                            <includes>
                                <include>**/appengine-web.xml</include>
                            </includes>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>

            <plugin>
                <groupId>net.kindleit</groupId>
                <artifactId>maven-gae-plugin</artifactId>
                <version>0.5.7</version>
                <configuration>
                    <unpackVersion>${gae.version}</unpackVersion>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>build</directory>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

5.mavenでgae:unpackゴールを実行しgae-java-sdkを準備する。

gae-java-sdkをダウンロードしてきて、レポジトリ内に展開するというゴールです。これをやっとかないとgae:runが実行出来ません。
これはmaven-gae-pluginを最初に使う時か、gaeのバージョンを変更したら一回だけやっておけば良いです。

6.mavenでgae:runゴールを実行しローカルでテストする。

localhost:8080で起動します。Hello World!と時刻が表示されていれば成功です。
リロードすれば表示されている時刻が追加されていきます。

※テスト実行時に、buildディレクトリ以下にテスト用データストア(local_db.bin)が作成されるのですが、これを変更する方法はまだ分かりません。slim3のorg.slim3.tester.AppEngineTesterクラスが関係してそうなのですが...

前向きなヒューマンエラー対策をしよう。

SIerが仕切っている開発現場でありがちなのが、何かミスを犯すと、そのミスを防止するようにすごく手間がかかるチェックが追加されて、開発効率とモチベーションが下がるというダメなパターン。
たとえば、「今年度は申請書(EXCELシート)書いて上司の判子もらわないと svn commit すらできない職場で仕事することになりました。 - SiroKuro Page」とか。

これはプロセスマネジメントでもなんでもない、管理ごっこだ。管理したつもりになって自己満足しているに過ぎない!!

プロセスをマネジメントしたければプロセスを削れ: DESIGN IT! w/LOVE
では、次のように述べられている。

プロセスマネジメントにありがちな間違いのひとつに、ミスを減らそうとして、そのチェックをするプロセスを増やしてしまうということがある。

もちろん、すべての場合にそれが間違いというわけではない。
そのチェックが機械によって自動的に行われるのであれば大丈夫だし、そもそものプロセスが単純なものであれば、ひとつ工程が増えても大きな問題にはならない。

ミスを減らそうとして、そのチェックをするプロセスを増やしてしまうことが問題になるのは、そのミスが実はすでに多すぎて複雑な工程からなるプロセスそのものが負担となり、業務の遂行を圧迫しているケースだ。
すでに多すぎて複雑すぎるところに新たにチェック工程など追加すれば、業務の圧迫度合いはより大きくなり、また違うところでミスが起きやすくなるのは目に見えている。

この主張に同意する。

息の長いプロジェクトだと、チェックとか認証手続きとかで本来の仕事の割合がかなり減って、仕事自体が罰ゲームみたいなことになってる場合が多々ある。

こういった仕事の効率を下げるようなルールは、ほとんどがささいなミス=ヒューマンエラーが原因でつくられていて、ちょっと注意すれば防げそうに見えるんで、チェックリストとかで対策しがち。

だけど、チェックリストを作ったからといって、それで対策が完璧になることはない。チェックリストをチェックしているのは人間だからだ。たとえば、「チェックリストの1つの項目をチェックし忘れた。」とか、「面倒だったので、ざるチェックで通していた」とか、ありがち。そして、チェックリストのチェックリストが作られたりするというアホな状況が発生するわけだ。

結局のところ、人間が作業しているが故に発生しているヒューマンエラーに対して、人間がチェックを入れるという対策をしても、問題の発生位置が変わるだけで根本的な対策にはなってないのだ。


ソフトウェア開発からムダを減らし、もっと効率良く価値を提供していくためには、このような後ろ向きな対策ではなく、チェックを機械化したり、ヒューマンエラーにすぐに気づく様にするなどといった、もっと前向きな対策をする必要がある。


チェックリストが機械的にチェック可能なモノなら機械化するなり、自働化するなりして人間が関わらないようにする。たとえば、コーディング規約を守らせたいなら、いちいち人間が調べてコーディング規約を守らせるのではなく、checkstyleなりコードフォーマッタを使うなりして、自動的にチェックするようにすれば手間もかからないし、見落としもなくなる。


コミットをミスってビルドが壊れたというのが問題なら、デイリービルドするなり、継続的インテグレーションをするなりして、ビルドが壊れたことをすぐに検知できるようにする。ビルドが壊れたことにすぐに気づけば、どのコミットが問題なのかを突き止めるのも簡単なはずだ。


逆に考えるんだ。「ミスをしないようにする」んじゃなくて「ミスをしても大丈夫なようにする」んだ。それがプロセスの改善だ。


ソフトウェア開発では、そのほとんどの工程に人間が関わっている。だとすれば、ヒューマンエラーをいかに少なくするかが開発効率の向上の鍵となるのではないか。


参考:ヒューマンエラーの理論と対策

mime-utilのサンプルをもう1つ作ってみた。

種類を判定したいファイルをドロップすると、mime-utilを使って種類を判定し、MimeTypeを出力する。

GUIの構築に使っているのはswixml2というフレームワークで、XMLで宣言的にswingのGUIを作成できる。いまのところまだ完成度は低いし、ドキュメントも整備されてないけど、JSR 295:Beans BindingやJSR-296:Swing Application Frameworkなどに対応しているなど、完成度が上がれば十分使えるフレームワークになると思う。これからに期待。

余談

このところmime-utilの本家サイトに繋がらないけど、どうしたんだろう。sourceforceのプロジェクトは更新されてるみたいだけど。会社つぶれちゃったのかな?

DECOCAでプランニングポーカーを作ってみた

個人用名刺でも作ろうかと思ったのだけど、裏が真っ白だとつまらない。そんなわけで裏面をプランニングポーカーにしてみた。
実際はトランプと比べて小さくて扱いにくいし、名刺として配っちゃうとプランニングポーカーとしては使えないという重大な欠陥がww


作り方

  1. OpenOffice Draw
    1. のページ設定:縦11.40cm x 横22.80cm、余白上下左右 それぞれ0.15cm
    2. がんばってプランニングポーカーの図案を作る
    3. PNG画像としてエクスポート
  2. JTrim
    1. 色相を90度くらいずつ変えて色違いを作成
  3. DECOCA
    1. PNG画像をDECOCAで裏面用画像として登録

作ったPNG画像と図案はここに置いておきます。
プランニングポーカー.zip 直

OpenOffice Drawのファイルは小さい数字のレイアウトが崩れてしまうみたいです。
レイアウトを修正して保存しても、開き直すとずれちゃってるので、あきらめてそのままにしてます。

認定テストスイートの重要性

ミドルウェアや言語のランタイム実行環境などの相互運用性(もっと簡単に言うと互換性)を確保するためには、認定テストスイートが重要になる。

たとえば、webブラウザレンダリングの非互換性の問題は、Acidテストができ、そのテストをクリアしようと各ブラウザの開発者が努力したことで、レンダリングの互換性が向上した。
Webブラウザ標準適合性のわなとAcidテストの正体(1/3) − @IT


また、一般的でない環境用のJREJDKは、Java SE Test Compatibility Kit (TCK)をパスしなければ、javaと名乗ることが出来ない。これもjavaがどこでも同じように動作するということを保証するためにやっていることだ。
http://journal.mycom.co.jp/news/2009/07/17/046/index.html


このように互換性を確保するうえで、その互換性の基準となるテストスイートを用意することはすごく重要なのだ。仕様書をきちんと書いていても、その解釈しだいで微妙に異なる実装がされてしまい、その実装の差によって互換性が失われてしまうのではないかと思う。


その昔、CORBAミドルウェアのベンダーがいろいろ混じっていて、相互運用性の問題でデスマーチと化したプロジェクトもあったらしい。

標準化団体が策定した仕様に基づいた製品の相互運用性には注意したい。たとえばXML署名だと、あるXML署名ライブラリで署名したXML文書を、別のXML署名ライブラリで検証すると検証に失敗するなんて例が考えられる。
そういうわけでXML署名についてはIPAガイドライン(PDF)をだしているけど、仕様の隅をつつくような検証をしなければいけないので、かなりコストがかかりそうだ。

jetty-maven-pluginがwindows環境で動かないので調べてみた

jetty-maven-pluginのバージョンが7.0.1.v20091125だとエラーが出てjettyは起動するものの、アプリケーションが動いてない状態になる。バージョンを6あたりに下げると動く。

現象

現象は、JETTY-1166と同じ。

以下はJIRAに登録されているスタックトレース

2010-01-01 20:54:50.593:INFO::jetty-7.0.1.v20091125
2010-01-01 20:54:50.640:WARN::Failed startup of context JettyWebAppContext@8825a
5@8825a5/zipcodeservice,file:/C:/projects/ext/davidkarlsen.com/zipcodeservice/zi
pcodeservice-war/src/main/webapp/,file:/C:/projects/ext/davidkarlsen.com/zipcode
service/zipcodeservice-war/src/main/webapp/
java.net.URISyntaxException: Illegal character in path at index 18: file:/C:/Doc
uments and Settings/karltdav/.m2/repository/org/mortbay/jetty/jetty-maven-plugin
/7.0.1.v20091125/jetty-maven-plugin-7.0.1.v20091125.jar
        at java.net.URI$Parser.fail(URI.java:2809)
        at java.net.URI$Parser.checkChars(URI.java:2982)
        at java.net.URI$Parser.parseHierarchical(URI.java:3066)
        at java.net.URI$Parser.parse(URI.java:3014)
        at java.net.URI.(URI.java:578)
        at java.net.URL.toURI(URL.java:918)
        at org.eclipse.jetty.webapp.WebInfConfiguration.preConfigure(WebInfConfi
guration.java:81)
        at org.mortbay.jetty.plugin.MavenWebInfConfiguration.preConfigure(MavenW
ebInfConfiguration.java:117)
        at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:347
)
        at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContex
t.java:102)
(以下省略)

エラーメッセージから、「file:/C:/Documents and Settings/」という部分に含まれるスペースがまずいらしいことが分かる。
URLにはスペースを含めることができるが、URIにはスペースを含めることが出来ないみたいだ。

問題の箇所のコードを見てみる

与えているURLが悪いのかとjetty-maven-plugin側のURIとURLを変換している箇所を修正してみたが、問題が解決しなかったので、どうもjetty-server側の問題らしい事が分かった。

jetty-serverで例外が投げられている箇所のコードは次の通り。
http://download.eclipse.org/jetty/stable-7/xref/org/eclipse/jetty/webapp/WebInfConfiguration.html#71

71          ClassLoader loader = context.getClassLoader();
72          while (loader != null && (loader instanceof URLClassLoader))
73          {
74              URL[] urls = ((URLClassLoader)loader).getURLs();
75              if (urls != null)
76              {
77                  URI[] containerUris = new URI[urls.length];
78                  int i=0;
79                  for (URL u : urls)
80                  {
81                      containerUris[i++] = u.toURI();
82                  }
83                  containerJarNameMatcher.match(containerPattern, containerUris, false);
84              }
85              loader = loader.getParent();
86          }

81行目の「u.toURI()」という処理でURISyntaxExceptionが投げられている。
jetty6では問題無く使えていたので、何が変わったのか見てみたら、toURI()を使わないで処理していたらしい。

対策

81行目の「u.toURI()」で問題が起きているのは、URIに含める事が出来ないスペースを含むためだから、それをエスケープしてやれば問題なさそうだ。

で次のように修正したら、jetty-maven-pluginから無事起動できた。

71          ClassLoader loader = context.getClassLoader();
72          while (loader != null && (loader instanceof URLClassLoader))
73          {
74              URL[] urls = ((URLClassLoader)loader).getURLs();
75              if (urls != null)
76              {
77                  URI[] containerUris = new URI[urls.length];
78                  int i=0;
79                  for (URL u : urls)
80                  {
81                      containerUris[i++] = new URI(URIUtil.encodePath(u.toString()));
82                  }
83                  containerJarNameMatcher.match(containerPattern, containerUris, false);
84              }
85              loader = loader.getParent();
86          }
URIUtil.encodePath()を使った理由

ここで使っているhttp://download.eclipse.org/jetty/stable-7/xref/org/eclipse/jetty/util/URIUtil.htmlというクラスはjetty-server内のものなんだけど、URLEncoderではなく、これを使ったのには理由がある。

Oracle Technology Network for Java Developers | Oracle Technology Network | Oracleに以下のように記載されていたからだ。

URI クラスは特定の状況において、そのコンポーネントフィールドに対してエスケープ処理を実行することに注意してください。.URL のエンコードとデコードを管理する際の推奨の方法は、URI を使用して、これら 2 つのクラス間の変換を toURI() と URI.toURL() を使って行うことです。

URLEncoder クラスと URLDecoder クラスを使用することもできますが、これらは HTML 形式のエンコーディング専用です。また、このエンコーディングは、RFC2396 で定義されているエンコーディング方式と同じものではありません。

これだけだと、よく分からなかったのでさらに調べてみた。

この違いの詳細は、
studyinghttp.net - このウェブサイトは販売用です! - 解説 仕様書 利用 技術 である 手法 日本語訳 プログラミング リソースおよび情報

Subbu’s Blog
に記述されていた。

ごくごくおおざっぱにまとめると、

HTML形式のエンコードでは、スペースが"+"になり、
URI形式のパーセントエンコード(RFC3986)では、スペースが"%20"にエンコードされる。

とのことらしい。だから、HTML形式のエンコードを行うURLEncoderはこの場合は使えない。
幸いにもJettyにはhttp://download.eclipse.org/jetty/stable-7/xref/org/eclipse/jetty/util/URIUtil.htmlというクラスがあり、
ソースコードを見たところ、ちょうどよく利用出来そうな感じだったのでそれを利用した。

URLEncoderとURIUtilの違いを見てみる。
確認用コード

	public void testURLEncode() throws UnsupportedEncodingException, MalformedURLException {
		File file = new File("C:/Documents and Settings/cnaos/My Documents/tmp");
		String urlEncodedStr = URLEncoder.encode(file.toURL().toString(), "UTF-8");
		String uriUtilStr = URIUtil.encodePath(file.toURL().toString());
		System.out.println("URLEncoder result="+urlEncodedStr);
		System.out.println("URIUtil result="+uriUtilStr);
	}

結果

URLEncoder result=file%3A%2FC%3A%2FDocuments+and+Settings%2Fcnaos%2FMy+Documents%2Ftmp%2F
URIUtil result=file:/C:/Documents%20and%20Settings/cnaos/My%20Documents/tmp/

だいぶ違いますね。

ローカルビルドして試してみたい人向けの情報

※jetty-7.0.1.v20091125を上書きするので、すでに何かにjetty7を使っているのであれば、やらない方が無難です。

1.まずjetty-serverを以下の場所からチェックアウトします。

http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/tags/jetty-7.0.1.v20091125

本来ならtrunkからチェックアウトするところなんでしょうけど、jetty-maven-pluginのビルドを省くためにこうしてます。

2.次のパッチを当てます。
Index: jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java
===================================================================
--- jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java (リビジョン 1374)
+++ jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java (作業コピー)
@@ -78,7 +78,7 @@
                 int i=0;
                 for (URL u : urls)
                 {
-                    containerUris[i++] = u.toURI();
+                    containerUris[i++] = new URI(URIUtil.encodePath(u.toString()));
                 }
                 containerJarNameMatcher.match(containerPattern, containerUris, false);
             }
3.ビルドして、ローカルのmavenレポジトリにインストールします。
mvn -Dmaven.test.skip=true install

「mvn -Dmaven.test.skip=true」でテストをスキップしてしまっているのは、どうしても通らないテストがあったためです。
ホントはやっちゃいけないんだけど。

org.eclipse.jetty.server.AbstractConnectorTestのtestMultipleRequests()や
org.eclipse.jetty.server.handler.StatisticsHandlerTestのtestSuspendExpire()やtestSuspendComplete()
あたりのテストが成功したり、しなかったりしてました。

テストコードを見てみると、sleep(10) = 10ミリ秒とかやたらとsleep期間が短いのが気になって、ちょっとsleepを長めにしてみたけど、けっきょく変わらなかった。
下手にテストコードをいじるのも怖いので、そのままにorz。

まとめ

  • jetty-maven-pluginでjetty7をwidows環境で動かすことができるようになった。
  • URLとURIの変換には注意。
  • 似ているけど、ちょっと違うものはバグの温床になりやすい。

さて、これからバグトラッカに登録しなければいけないんだが、どうしたものか。
jetty-serverのバグトラッカに登録したらいいのか、
jetty-maven-pluinのバグトラッカに登録したらいいのか。

普段使ってるeclipseプラグイン

普段使ってるeclipseプラグインを書き出してみる。
使っているeclipseのバージョンはPleiades All in Onejavaパッケージ(Pleiades All in One Java), pleiades-e3.5-java_20100226.zipです。これが出来たおかげで、ずいぶん楽になりました。

プラグインを入れすぎると不安定になったり、重くなったりするので、必要なものだけ入れておくのが良いみたいです。

プラグインをインストールする際の注意

eclipse環境のバックアップを作る

もし、あなたがeclipseを開発に使っているのなら、プラグインをインストールする前に、必ずバックアップを取ってください。ディレクトリをまるごとコピーしておくだけでもいいです。
相性の悪いプラグインをインストールすると、eclipseが起動すらしなくなり、eclipseを再インストールするしか無くなる場合があるので、必ずバックアップを取ってください。

1つずつインストールする

これも念のためです。

更新マネージャとウィルス対策ソフトのwebフィルタリング機能

webフィルタリング機能を持ったウィルス対策ソフトなどを利用している場合、プラグインのインストール途中でタイムアウトが発生する場合があるみたいです。
どうも大きいzipやjarをダウンロードしようとしたときに起きているらしく、ウィルス対策ソフトのwebフィルタリング機能がダウンロード中のファイルをインターセプトして検査している間、eclipseの更新マネージャに対しては応答が全く無いように見えてしまうのが原因ではないかと考えています。
プラグインをインストールに失敗する場合は一時的にwebフィルタリング機能をoffにした方が良いかもしれません。

Pleiades All in One Javaに同梱されているプラグイン

追加で入れているプラグイン

おすすめ度は私の独断と偏見に基づくものです。

PMD
用途 バグ対策
おすすめ度 ★★★★★
公式URL http://pmd.sourceforge.net/
更新マネージャURL http://pmd.sourceforge.net/eclipse
参考 Eclipseで使える静的解析ツール (3/4):Eclipseで使えるテストツールカタログ(2) - @IT
説明 FindBugsで見つけられないようなバグに対しての備えのために入れています。
djUnit
用途 テスト、コードカバレッジ取得用
おすすめ度 ★★★★★
公式URL djUnit
更新マネージャURL http://works.dgic.co.jp/djunit/update/3.5.x/site.xml
参考 http://www.atmarkit.co.jp/fjava/rensai3/eclipseplgn02/eclipseplgn02_2.html
説明 テストケースごとに実行してみて、カバレッジを確認するのに向いてます。カバレッジ測定用にはjCoverageを使ってるらしいです。
EclEmma
用途 テスト、コードカバレッジ取得用
おすすめ度 ★★★
公式URL http://www.eclemma.org/
更新マネージャURL http://update.eclemma.org/
説明 まとめてテストを実行したあと、カバレッジレポートを確認するのに向いてます。カバレッジ測定用にはEMMAを使っているらしいです。
JDepend4Eclipse
用途 クラス・パッケージの依存関係解析用
おすすめ度 ★★★★
公式URL http://andrei.gmxhome.de/jdepend4eclipse/
更新マネージャURL http://andrei.gmxhome.de/eclipse/
参考 Eclipseで使えるメトリクス計測ツール (5/5):Eclipseで使えるテストツールカタログ(3) - @IT
説明 クラスやパッケージの抽象度・結合度などのメトリクスを調べる事ができ、適切なクラス設計やパッケージ設計ができてているかの調査に使うことができます。
CAP
用途 クラス・パッケージの依存関係解析用
おすすめ度 ★★★★★
公式URL http://cap.xore.de/
更新マネージャURL http://cap.xore.de/update/
参考1 Eclipseで使えるメトリクス計測ツール (3/5):Eclipseで使えるテストツールカタログ(3) - @IT
参考2 メトリクスとは何か | Think IT(シンクイット)
説明 JDepend4Eclipseと比較して、どのクラスが循環参照の輪に組み込まれているのか調べやすいのではないかと思います。
Eclipse HTML Editor(Project Amateras)
用途 HTML,JSP,XMLなどのファイル編集用
おすすめ度 ★★★
公式URL http://amateras.sourceforge.jp/cgi-bin/fswiki/wiki.cgi?page=EclipseHTMLEditor
更新マネージャURL なし
説明 標準エディタとの使い分け用に。
q4e
用途 Mavenのサポート追加
おすすめ度
公式URL http://code.google.com/p/q4e/
更新マネージャURL http://q4e.googlecode.com/svn/trunk/updatesite-iam/
参考URL http://www.atmarkit.co.jp/fjava/index/index_maven2.html
説明 いま人柱中。
ivyde
用途 Apache ivyのサポート追加
おすすめ度 ★★★
公式URL http://ant.apache.org/ivy/ivyde/
更新マネージャURL http://www.apache.org/dist/ant/ivyde/updatesite
参考1 http://journal.mycom.co.jp/articles/2008/12/03/apacheivy/index.html
参考2 EclipseのWTPでApache Ivy v.s m2eclipseその2 - Ivyってどうよ - ぼそっと
説明 ivyはantでMavenのようなライブラリの依存関係の解決とダウンロードを行うantタスクを提供します。このプラグインはivy.xmlなどの編集機能を提供したり、ivyでダウンロードしたjarファイルをプロジェクトのクラスパスに組み込んだりしてくれます。レポート生成とかやり始めると、結局mavenに戻ってきちゃうんですけどね。

試しに入れてみたもの

HgEclipse
用途 Mercurialのサポート追加
おすすめ度
公式URL http://javaforge.com/project/HGE
更新マネージャURL http://hge.javaforge.com/hgeclipse
説明 これも人柱中。