G::blog

主にVert.xやYokeについての覚書

Play Framework に Vert.x を埋め込んで、SockJS のサンプルを稼働させる

 Play Framework に、Vert.x を埋め込んで SockJS のサンプルが稼働するようにしてみました。

 DelegateServerクラスが、Vert.x の Verticle になります。Devモードでこのクラスを触るとリロードの対象にもなります。
 まだやってませんが、モデルへのアクセスも問題なく可能そうです。

 実験的なものですが闇に消えるのも悲しいので、公開しておきました。
 

#ついに来週の月曜日に、Vert.x 2.0 がリリースのようですね(^o^)。


Play Framework 2.1.2:
https://github.com/sgougi/play21-vertx-sockjs-sample

Play Framework 1.2.5:
https://github.com/sgougi/playone-vertx-sockjs-sample

gradlew から classpathを指定して、 auto-redeploy が機能するようにモジュールを実行することについて

次の二つの記事では、Yoke と Vert.x 2 のそれぞれの視座から、gradlew を用いて classpath を指定しさらに auto-redeploy を機能させるモジュールを実行する方法をご紹介しました。

これらの試行錯誤は、Play Framework の auto-reload と似たような環境を Vert.x 2でも欲しくて行ったものでした。

これを実現するために作成したのが、両方の記事で紹介した次のタスクでした。

task runModEclipse(dependsOn: copyMod, description: 'Run the module using all the build dependencies (not using installed vertx') << {
  setSysProps()
  def classpath = [new URL("file:./bin/")] as URL[]
  def pm = PlatformLocator.factory.createPlatformManager()
  def latch = new CountDownLatch(1)
  pm.deployModuleFromClasspath(moduleName, null, 1, classpath, new AsyncResultHandler<String>() {
      public void handle(AsyncResult<String> asyncResult) {
          if (asyncResult.succeeded()) {
              println "CTRL-C to stop server"
          } else {
              println "Failed to deploy module"
              asyncResult.cause().printStackTrace()
              latch.countDown()
          }
      }
  });
  latch.await(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}

このタスクは、両記事で紹介した簡単なコードでは問題なく動作するのですが、記事を書いた後で、リソースの取得が意図したように行えないことがわかりました。

例えば、Verticleの具象クラスの #start()で、次のように記述したとします。

request.response().sendFile("index.html")

gradlew runModで実行した場合は、この記述で src/main/resources/index.html が取得できるのですが、わたしの作成したタスクを使って実行した場合は適切に取得ができず、次のようにすると取得できました。

request.response().sendFile("src/main/resources/index.html")


原因を調べていくと、vertx の context が持つ、pathAdjustment が null になっているのが原因でした。
で、もう少し詳しく調べてみると、私が作成したタスクで使っている pm.deployModuleFromClasspath() の先で呼び出している次のメソッドの実装に起因していることがわかりました。

org.vertx.java.platform.impl.DefaultPlatformManager:

  private void deployModuleFromCP(boolean redeploy, String depName, ModuleIdentifier modID,
                                  JsonObject config,
                                  int instances,
                                  URL[] classpath,
                                  final Handler<AsyncResult<String>> doneHandler) {
    checkWorkerContext();
    JsonObject modJSON = loadModJSONFromClasspath(modID, new URLClassLoader(classpath, platformClassLoader));
    if (modJSON == null) {
      throw new PlatformManagerException("Failed to find mod.json on classpath");
    }
    deployModuleFromModJson(redeploy, modJSON, depName, modID, config, instances, null, null, Arrays.asList(classpath), doneHandler);
  }

上記の最後の行の deployModuleFromModJson()の呼び出しの7と8番目の引数が null になっているために、vertx の context が持つ pathAdjustment が null のままになってしまうようです。

この推測の確認のために、次のようなコードを書いてみると正常に取り出せました。

import java.nio.file.Paths;

import org.vertx.java.core.Handler;
import org.vertx.java.core.file.impl.PathAdjuster;
import org.vertx.java.platform.Verticle;

import com.jetdrone.vertx.yoke.Yoke;
import com.jetdrone.vertx.yoke.engine.GroovyTemplateEngine;
import com.jetdrone.vertx.yoke.middleware.Router;
import com.jetdrone.vertx.yoke.middleware.Static;
import com.jetdrone.vertx.yoke.middleware.YokeRequest;

import org.vertx.java.core.impl.VertxInternal;

public class SampleVerticle extends Verticle {
    @Override
    public void start() {

    	if(((VertxInternal)vertx).getContext().getPathAdjustment() == null)
    		((VertxInternal)vertx).getContext().setPathAdjustment(Paths.get("bin"));
    	
        Yoke yoke = new Yoke(vertx);

		yoke
		.engine("html", new GroovyTemplateEngine())
		.use("/static", new Static(".")) 
		.use(new Router()
			.get("/", new Handler<YokeRequest>() {
				@Override
				public void handle(YokeRequest request) {
					System.out.println(PathAdjuster.adjust((VertxInternal)vertx, (String)"index.html"));
					request.response().sendFile("index.html");					
				}
			})
		);
        
        yoke.listen(8080); 	
		
    }
}

上記のコードのポイントは、

    	if(((VertxInternal)vertx).getContext().getPathAdjustment() == null)
    		((VertxInternal)vertx).getContext().setPathAdjustment(Paths.get("bin"));

の部分です。

gradle のプロジェクトを Eclipse で取込むと、src/main/resources の下のリソースは、bin の下にコピーされるため、Paths.get("bin") としています(ただし、行われるのは単なるコピーなので、Paths.get("src/main/resources") でも問題はないように思います)。


で、このあたりを Vert.x の Google グループに乏しいライティング能力で伝えた結果、次のような回答を得ました。

https://groups.google.com/d/msg/vertx/rLHrkjoJEig/upYamJDCNd0J:Running Verticle in directly in Eclipse (vert.x 2.0)

./gradlew runMod doesn't support passing parameters such as -cp 
currently, although it could easily be extended to do this. 

Instead you can use 

vertx runmod -cp <classpath> 

There is a document in the vert.x 2.0 docs on the website which 
describes how to do this. 

まだ、gradlew コマンドでは、auto-redeploy は正常に動作しないようです。


私は、とりあえずサポートがされるまでは、作成した runModEclipse と次のコードを利用して、冒険を続けたいと思っています(Pull Requestできればよいのだが…)。

    	if(((VertxInternal)vertx).getContext().getPathAdjustment() == null)
    		((VertxInternal)vertx).getContext().setPathAdjustment(Paths.get("bin"));

補足

はっきりしないけど、次のイシューと関係するのかも。(関係したみたい)
https://github.com/vert-x/vert.x/issues/651

追記

・ 対応してもらえたようだ:  https://groups.google.com/d/msg/vertx/rLHrkjoJEig/X9V8xG2L7aMJ
・ 2.0.0-RC3からWindowsでも runModEclipse が正常に動作するようになりました。

Yoke 用の Middleware を作成する

Yoke は、Middleware という概念を持っていて、その実装であるさまざまな Middleware を組み合わせてアプリケーションを構築することが可能です(Yoke には、直ぐに利用できる 14種類の Middleware が現時点ではバンドルされています)。

この記事では、アプリケーション(トップレベルのModule)の中に簡単な Middleware を作成し利用する方法を説明します。しかし、Yoke や Vert.x の内部の詳細について、わかっていないことがまだまだ多く、大雑把で間違いを含んだ記述にならざるを得ない点、あらかじめご了承ください。発見があればその都度追記していきたいと思います。

1. Yoke の Gradle テンプレートを入手し、プロジェクトの作業ディレクトリを作る

まず、Gradle テンプレートを取得し、それを利用してプロジェクトの作業ディレクトリを作成します。

$ curl -0 http://pmlopes.github.io/yoke/yoke-gradle-template.tar.gz | tar -zx
$

名前を変えます (この記事ではyoke-mwという名前にします)。

$ mv yoke-gradle-template yoke-mw
$ cd yoke-mw
$

2. Verticle を書く

今回は、JavaScript で Verticle を書きます。
src/main/resources の下に、以下の内容の JavaScript ファイルを作ります。
ファイル名は HelloWorldVerticle.js とします。

var Yoke = require('yokejs/Yoke');

var yoke = new Yoke();
yoke.use(function (req) {
  req.response().end('Hello World');
});

yoke.listen(8080);

3. mod.jsonファイルを編集する

src/main/resources の下に、mod.json があります。次のように修正します。

{
  "main": "HelloWorldVerticle.js",
    "includes": "com.jetdrone~yoke~1.0.0-beta2"
}

"main" には、「2. Verticleクラスを書く」で作成した JavaScriptファイルを src/main/resources からの相対パス名で記述します。

4. 最新の Vert.x を利用するように gradle.properties を修正する

現時点の最新は、2.0.0-CR1 なので、次のように修正しました。

# The version of Vert.x
#vertxVersion=2.0.0-beta3
vertxVersion=2.0.0-CR1

5. アプリケーションを実行しブラウザで閲覧してみる

コンソール(ターミナル)から実行します。

linux/unix系は、

% ./gradlew runMod

windowsは、

% gradlew.bat runMod

です。

うまく起動したら、ブラウザで http://localhost:8080 にアクセスしてみます。

Hello World と表示されたら OK です。

6. Middleware を作ってみる

5.までは、簡単な Yoke のサンプルの作り方の説明でした。
というわけで、いよいよ目的の簡単な Middleware を作っていきます。

今回は Middleware クラスは Javaで書きます。Middleware は、Groovyでも書けそうですが、まだやれておらず問題なく実際に作れるかは定かではありませんがおそらく大丈夫でしょう。JavaScriptでの記述もまだやれていないのですが、Router.js(yoke/src/main/resources/yokejs/middleware/Router.js)を参考にすれば実装できそうです。

では、実装しています。
まず、Javaで記述する Middleware クラスは "com.jetdrone.vertx.study.middleware" というパッケージに今回は置きます。
もっと短いパッケージ名にすることも可能ですが、パッケージの頭は "com"あるいは"org"といった値でないと Rhino が org.mozilla.javascript.EcmaError: ReferenceError: "XXXX" is not defined. といった例外を出します。たとえば、"middleware" とか "jp.co.hoge" といったパッケージ名だとそうなりました。"com"や"org"以外にもうまくいくものがあるかもしれませんが、まだ探せていません。それ以前に、何か設定をすれば "middleware" とか "jp.co.hoge"を含めてなんでもうまくいきそうな気もしますが、まだそのためにどうすればよいのか見つかっていません。というわけでここでは、"com.jetdrone.vertx.study.middleware" というパッケージを利用します (このあたり何かわたしの勘違いがあるかもしれませんので注意してください)。

今回作成する Middleware は、#handle() が呼び出されると標準出力に文字列を出すだけです。

package com.jetdrone.vertx.study.middleware;

import org.vertx.java.core.Handler;

import com.jetdrone.vertx.yoke.Middleware;
import com.jetdrone.vertx.yoke.middleware.YokeRequest;

public class SimpleMiddleware extends Middleware {

	@Override
	public void handle(YokeRequest request, Handler<Object> next) {
		System.out.println("SimpleMiddleware#handle");
		next.handle(null);		
	}

}

このクラスのJavaファイル(SimpleMiddleware.java)を src/main/java/com/jetdrone/vertx/study/middleware に作成します。


次に、この Middleware を JavaScript から要求(require())できるようにスタブを書きます。

src/main/resources/middleware の下に、SimpleMiddleware.js という JavaScript ファイルを作成し以下のよう記述します。

module.exports = com.jetdrone.vertx.study.middleware.SimpleMiddleware;


これで、SimpleMiddleware を利用する準備が整いました。

2. で作成した、HelloWorldVerticle.js ファイルを開き以下のように修正します。

var Yoke = require('yokejs/Yoke');
var SimpleMiddleware = require('middleware/SimpleMiddleware') // 上で作成したスタブを記述する

var yoke = new Yoke();

yoke
  .use(new SimpleMiddleware())
  .use(function (req) {
     req.response().end('Hello World');
  })
  .listen(8080);


編集し終わったら gradlew runMod を実行し、ブラウザで http://localhost:8080 にアクセスしてみます。
ブラウザに、Hello world と表示され、さらにコンソールにも SimpleMiddleware#handle と出力されたなら無事作業完了です。

tag リリースから Vert.x のライブラリを作る方法

Vert.x の GitHub の タッギングリリースから目当てのソースのアーカイブを取得します(もちろん git cloneして、git checkout で取得しても構いません)。

そのアーカイブを展開し、展開されたフォルダに移動し、gradlew dist を実行します。

linux/unix系は、

./gradlew dist 

Windowsは、

gradlew.bat dist 

です。

ビルドが正常に終了すると、build/distributions の下に、ライブラリのアーカイブが作られています。

Yoke でセッションを管理する

今回は、Yoke のセッション管理の方法についての短い記事になります。

1. セッション管理に関する資料

Yoke でセッションを管理するには、 Middleware の CookieParserSession(現在ページが存在しません) を組み合わせて使います。

これらを組み合わせた使い方についての説明は、Yoke のコミッターの Paulo さんが、Googleグループにポストされている次のメッセージが現時点では一番詳しいと思います(現時点では、公式のドキュメントでは十分な記述がされていません)。

https://groups.google.com/d/msg/vertx/2DrXx-E8Zso/1Tv4xOfCjy0J

2. 実際に動くコード

実際に動かしてみるのが手っ取り早いと思いますので、Pauloさんが Vert.xの Googleグループにポストされたメッセージ中のコードを踏襲して、実際に動くコードにしてみました。

package com.jetdrone.yoke;

import javax.crypto.Mac;

import org.vertx.java.core.Handler;
import org.vertx.java.platform.Verticle;

import com.jetdrone.vertx.yoke.Yoke;
import com.jetdrone.vertx.yoke.middleware.CookieParser;
import com.jetdrone.vertx.yoke.middleware.Router;
import com.jetdrone.vertx.yoke.middleware.Session;
import com.jetdrone.vertx.yoke.middleware.YokeRequest;
import com.jetdrone.vertx.yoke.util.Utils;

public class SessionSampleVerticle extends Verticle {

	public void start() {

		final Mac hmac = Utils.newHmacSHA256("my super secret signature secret key");
		// create Yoke
		final Yoke yoke = new Yoke(vertx);
		// since sessions are stored in cookies we need to parse then so use the
		// cookie parser middleware
		yoke.use(new CookieParser(hmac));
		// i don't want to bother with session validation so lets use the
		// Session middleware
		yoke.use(new Session(hmac));

		// lets code our app
		yoke
			.use(new Router()
			// use this path check that the cookies are ok on your browser debugger
			.get("/", new Handler<YokeRequest>() {
				@Override
				public void handle(YokeRequest request) {
					System.out.println(request.getSessionId());
					request.response().end();
				}
			})
			// go here to create a new session
			.get("/new", new Handler<YokeRequest>() {
				@Override
				public void handle(YokeRequest request) {
					// setting the sessionId informs the session middleware
					// to create a session cookie
					request.setSessionId("some generated secure id string here");
					request.response().end();
				}
			})
			// go here to delete the session
			.get("/delete", new Handler<YokeRequest>() {
				@Override
				public void handle(YokeRequest request) {
					// setting sessionId to null is same as delete session
					request.setSessionId(null);
					request.response().end();
				}
			}))
			.listen(8080);
	}

}

(Yoke の Ver. 1.0.0-beta2で動作確認をしました)

/new にアクセスすると、"yoke.sess" という名前のクッキーをブラウザが食べます。
値は、"s:some generated secure id string here.E817WXRKKY9FZqdDy1ym07mWDEG91+PQ" といったものが設定されます。
その値の "." 以降の文字列は、セッションIDの改ざんが行われないように、Mac を利用して生成されたサインになっています。また、値の先頭にある"s:"は、そのクッキーの値にはサインが含まれていることを明示しています。

/ にアクセスすると、現在のセッションIDが、コンソール(ターミナル)に出力されます。

/delete にアクセスすると、"yoke.sess" という名前のクッキーはブラウザから削除されます。

Vert.x 2 のGradleテンプレートを用いた auto-redeploy 可能な Eclipse プロジェクトの作成方法

先日は、「Yoke の Gradle テンプレートを用いた auto-redeploy 可能な Eclipse プロジェクトの作成」としてYokeを使ったアプリケーションを開発するための Eclipseプロジェクトの作り方を不十分ながら記事にしました。

今回も現時点でわかっている範囲での覚書という位置づけですが、 Vert.x の Gradle テンプレートを用いた純粋な Vert.x アプリケーション(実行可能なモジュール)を開発するための Eclipseプロジェクトの作り方について書きます。

今回は、簡単なサービスを書く言語として、Groovy を使いました。しかし、Javaでも大きくは変わらないはずです。

1. Vert.x の Gradle テンプレートを取得し、展開したフォルダに適当な名前を付ける。

Vert.x Gradle Template を訪ねて、【ZIP】ボタンを押して Gradle テンプレートの ZIP アーカイブをダウンロードします。vertx-gradle-template-master.zip という名前のファイルが、ダウンロードされるでしょう。

それを展開し、名前を適当に変えます。

% unzip vertx-gradle-template-master.zip
% mv vertx-gradle-template-master vertx-hello-world
% 

2. 必要により gradle.properties ファイルを修正する。

この記事を書いている時点では、Vert.x 2 の最新は、beta5 でした。Gradelテンプレートに含まれている gradle.properties ファイルにおいてもそのように記述されていたので、何もしないで閉じました。
きっちりした方は、モジュール名など変えるなどされてもよいでしょう。

3. Verticle の内容を変えて、ブラウザでアクセスしてみる。

src/main/groovy/example にある GroovyVerticle.groovy ファイルをHTTPサービスをするように修正します。

package example

import org.vertx.groovy.platform.Verticle

class GroovyVerticle extends Verticle {
    def start() {
	vertx.createHttpServer().requestHandler{ request ->
   		request.response.end("<html><body><h1>Hello from vert.x!</h1></body></html>")
	}.listen(8080)    
    }

}


次に、ターミナルから

% gradlew runMod

と実行します。これにより必要なモジュールなどがダウンロードされてサービスが起動します。

% gradlew runMod
(省略)
:compileJava UP-TO-DATE
:compileGroovy
:processResources
:classes
:copyMod
:pullInDeps
Pulling in dependencies for module com.mycompany~my-module~1.0.0-SNAPSHOT. Please wait
Dependencies pulled in successfully
:modZip
:jar SKIPPED
:signArchives SKIPPED
:assemble
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:copyMod
:runMod
CTRL-C to stop server
> Building > :runMod

といった状態になれば OK です。

※注意: Vert.x の要求によりJDK 1.7.0かそれ以降が必要です。


ここで、ブラウザら http://localhost:8080/ にアクセスしてみると、"Hello from vert.x!" と表示されるはずです。

表示されたなら CTRL-Cを入力して一旦終了します。

4. Eclipseにインポートする

% gradlew eclipse

と入力してみます。


すると、

% gradlew eclipse
:eclipseClasspath
Download http://repo1.maven.org/maven2/io/vertx/testtools/2.0.0-beta2/testtools-2.0.0-beta2.pom
Download http://repo1.maven.org/maven2/io/vertx/lang-groovy/2.0.0-beta4/lang-groovy-2.0.0-beta4-sources.jar
Download http://repo1.maven.org/maven2/io/vertx/testtools/2.0.0-beta2/testtools-2.0.0-beta2-sources.jar
Download http://repo1.maven.org/maven2/io/vertx/testtools/2.0.0-beta2/testtools-2.0.0-beta2.jar
:eclipseJdt
:eclipseProject
:eclipse

BUILD SUCCESSFUL

Total time: 11.735 secs

というふうに出力されます。

ディレクトリを眺めてみると、.settings や .classpath などの Eclipse で開発しているとき遭遇するおなじみのファイルが作られています。

というわけで、このディレクトリを Eclipse の Import メニュー項目から表示される Import ダイアログの中にある "Existing Projects int Workspace" を使ってインポートします。

無事インポートできるのですが、src/test/java/com/mycompany/myproject/test/unit/ExampleUnitTest.java がエラーになってしまうので、適当に対応します。わたしは、エラーが起きているimport文などをコメントアウトしました。

※ 依存関係を変更した場合は、再度 gradlew eclipse を実行する必要があるようです。

5. 編集して、再度実行して、そのまま編集してみる。

src/main/groovy/example/GroovyVerticle.groovy の中のブラウザに返している文字列を "Hello from vert.x!" から "Hello from vert.x with Eclipse!" にして、

% gradlew runMod

を実行してみます。


無事起動したら、ブラウザで http://localhost:8080/ にアクセスしてみます。すると当然ですが、"Hello from vert.x with Eclipse!" と表示されます。

では、そのままの状態で、Eclipse から再度編集してみます。"Hello from vert.x with Eclipse!" から "Hello from vert.x with Eclipse!!" と "!" を一つ加えてみることにします。で、ブラウザをリロードします。まっとうな人生であるなら、何も変化がないはずです。

CNTRL-C を入力して一旦終了します。

6. auto-redeploy が効くようにする。

では、編集内容が反映するように、auto-redeploy のための設定をしていきます。

まずは、先日の記事と同様に、gradle/vertx.gradle の適当な位置に、次のタスクを追加します。

task runModEclipse(dependsOn: copyMod, description: 'Run the module using all the build dependencies (not using installed vertx') << {
  setSysProps()
  def classpath = [new URL("file:./bin/")] as URL[]
  def pm = PlatformLocator.factory.createPlatformManager()
  def latch = new CountDownLatch(1)
  pm.deployModuleFromClasspath(moduleName, null, 1, classpath, new AsyncResultHandler<String>() {
      public void handle(AsyncResult<String> asyncResult) {
          if (asyncResult.succeeded()) {
              println "CTRL-C to stop server"
          } else {
              println "Failed to deploy module"
              asyncResult.cause().printStackTrace()
              latch.countDown()
          }
      }
  });
  latch.await(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}

※ このタスクでは、org.vertx.java.core.file.impl.PathAdjuster 正常に機能せずしないことが分かりました。リソースの取得が正常に動作しません。詳しくは、この記事を参照してください。


※ これは、ビギナーが作ったなんちゃってタスクですので、もっと良い方法があるかもしれません。

次に、Eclipse から src/main/resources/mod.json に、 "auto-redeploy": true を加えます。

{
  "main":"groovy:example.GroovyVerticle",

  // If your module is going to be registered in the Vert.x module registry you will also need the following
  // mandatory fields
  "description":"Put description of your module here",
  "licenses": ["The Apache Software License Version 2.0"],
  "author": "Joe Bloggs",

  // Optional fields
  "developers": ["Other Dev 1", "Other Dev 2"],
  "keywords": ["gerbils", "cars", "cheese", "chocolate", "underpants", "herring"],
  "homepage": "url to your project page",
  "auto-redeploy": true
}

設定はこれだけです。
これらの設定により、Eclipse が生成するクラスファイルや移動するリソースファイルの変更を Vert.x がキャッチして自動的にデプロイしなおしてくれます(現状、トップレベルのモジュールのみ)。

7. 再度実行して、編集してみる。

では、再度実行してみましょう。その際に、先ほど追加した runModEclipse タスクを使います。

% gradlew runModEclipse

起動してブラウザから http://localhost:8080/ にアクセスすると先ほどの修正が反映されて、"Hello from vert.x with Eclipse!!" と表示されるはずです。

では、Eclise から編集してみます。 "Hello from vert.x with Eclipse!!" を "Hello from vert.x with Eclipse!!!" というふうに "!" を一つ加えてブラウザの画面をリロードしてみます。
すると、その修正が反映された画面に変わるはずです。
もし変わらない場合は、 bin/mod.json の内容を確認してみてください。修正の反映された内容になっていないかもしれません。もし反映されていないならば、それに直接編集加えるか、反映されるまで根気よく Eclipse から src/main/resources/mod.json を修正してみてください。

ここまで来たら、gradlew を起動する前に、環境変数 JAVA_OPTS にデバッグのための引数を設定すれば、Eclipse からリモートデバッグも可能です。

例:
 -Xdebug -Xrunjdwp:transport=dt_socket,address=8002,server=y,suspend=n

8. あとがき

7. で、修正が反映されると書きましたが、わたしの環境では、GroovyVerticle#start() を

package example

import org.vertx.groovy.platform.Verticle

class GroovyVerticle extends Verticle {
    def start() {
	vertx.createHttpServer().requestHandler{ request ->
   		request.response.end("<html><body><h1>Hello from vert.x!</h1></body></html>")
	}.listen(8080, "localhost")    
    }
}

のようにホスト名を指定するようにして書いた場合、編集してリロードするとそのリロードが終わらないという現象が起きています。

この現象が起きても、ブラウザの停止ボタンを押してリロードすると無事反映されます。Java で Verticle を書いた場合も同様です。

現時点(Vert.x 2.0.0-CR1)で、このようになる原因は、私は把握できていません。
分かり次第、その都度、追記していきたいとおもいます。
何か気づかれた方がいらっしゃいましたら教えてください。

Yoke のGradleテンプレートを用いた auto-redeploy 可能な Eclipse プロジェクトの作成方法

Yoke のページでは、Yoke は Vert.x のミドルウェアフレームワークと定義されています。さらに、Node.js の connect をリスペクトしたものであるとも書かれています。

この記事では、Yokeのチュートリアルを土台にして、auto-redeploy 可能な Eclipse プロジェクトを作成します。

※ Vert.x の要求によりJDK 1.7.0かそれ以降が必要です。

●関連する主なプロジェクト:
Vert.x
Yoke
Gradle

1. Yoke の Gradle テンプレートを入手し、プロジェクトの作業ディレクトリを作る

まず、Gradle テンプレートを取得し、それを利用してプロジェクトの作業ディレクトリを作成します。
とても簡単です。展開して名前を変えるだけです。

$ curl -0 http://pmlopes.github.io/yoke/yoke-gradle-template.tar.gz | tar -zx
$

$ mv yoke-gradle-template yoke-tutorial
$ cd yoke-tutorial
$

2. Verticle クラスを書く

チュートリアルに沿って src/main/java/com/jetdrone/yoke の下に、以下の内容の Java ファイルを作ります。ファイル名は HelloWorldVerticle.java とします。

package com.jetdrone.yoke;

import com.jetdrone.vertx.yoke.*;
import com.jetdrone.vertx.yoke.middleware.*;

import org.vertx.java.core.*;
import org.vertx.java.platform.*;

public class HelloWorldVerticle extends Verticle {

    @Override
    public void start() {
        Yoke yoke = new Yoke(vertx);
        yoke.use(new Handler<YokeRequest>() {
            @Override
            public void handle(YokeRequest request) {
                request.response().end("Hello World");
            }
        });
        yoke.listen(8080); 	
    }
}

3. mod.jsonファイルを編集する

src/main/resources の下に、mod.json があります。次のように修正します。

{
    "main": "com.jetdrone.yoke.HelloWorldVerticle",
    "includes": "com.jetdrone~yoke~1.0.0-beta2",
    "auto-redeploy": true
}

チュートリアルには、

"auto-redeploy": true

がありません。これを入れておくと、クラスファイルやリソースの変更を Vert.x がキャッチして、自動的にデプロイしなおしてくれます。

"main" には、「2. Verticleクラスを書く」で作成した Java クラスのフルパス名を記述します。

4. 最新の Vert.x を利用するように gradle.properties を修正する

現時点の最新は、2.0.0-CR1 なので、次のように修正しました。

# The version of Vert.x
#vertxVersion=2.0.0-beta3
vertxVersion=2.0.0-CR1

5. Eclipse 用のタスクを追加

gradle/vertx.gradle の適当な位置に、次のタスクを追加します。

task runModEclipse(dependsOn: copyMod, description: 'Run the module using all the build dependencies (not using installed vertx') << {
  setSysProps()
  def classpath = [new URL("file:./bin/")] as URL[]
  def pm = PlatformLocator.factory.createPlatformManager()
  def latch = new CountDownLatch(1)
  pm.deployModuleFromClasspath(moduleName, null, 1, classpath, new AsyncResultHandler<String>() {
      public void handle(AsyncResult<String> asyncResult) {
          if (asyncResult.succeeded()) {
              println "CTRL-C to stop server"
          } else {
              println "Failed to deploy module"
              asyncResult.cause().printStackTrace()
              latch.countDown()
          }
      }
  });
  latch.await(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}

※ このタスクでは、org.vertx.java.core.file.impl.PathAdjuster 正常に機能せずしないことが分かりました。リソースの取得が正常に動作しません。詳しくは、この記事を参照してください。


このタスクを実行すると、 runMod タスクと同様にアプリケーションが起動しますが、少し違いがあります。その違いは、Eclipse 上でソースやリソースを編集するとそのビルド結果が bin ディレクトリに出力される場合に限って、auto-redeploy が機能するように classpath を指定しているところです。(このタスクは私が試行錯誤で作ったものなのでもっと良い方法があるのかもしれません。ご存じの方がいらっしゃれば教えてください)

6. Eclipse にプロジェクトをインポートする

Gradle Integration for EclipseEclipse にインストールしていない場合は、Eclipse Marketplace などからインストールします。

Gradle Integration for Eclipse の準備が整ったら、Eclipse のインポート機能で本プロジェクトをインポートします。

※ まだ試してないけど、./gradlew eclipse でもよいみたい。

7. アプリケーションを実行しブラウザで閲覧してみる

コンソール(ターミナル)から実行します。

linux/unix系は、

% ./gradlew runModEclipse

windowsは、

% gradlew.bat runModEclipse

です。

うまく起動したら、ブラウザで http://localhost:8080 にアクセスしてみます。

Hello World と表示されたら OK です。

8. Eclipse上で、編集してみる

Eclipse 上で、HelloWorldVerticle.java を編集してみます。request.response().end("Hello World") の部分の文字列を適当に変えてみるのが手ごろでしょう。

修正して保存を行ったら、ブラウザをリロードしてみます。祟りでもない限り、修正通り表示されたはずです。

gradlew を起動する前に、環境変数 JAVA_OPTS にデバッグのための引数を設定すれば、Eclipseからリモートデバッグも可能です。

例:
 -Xdebug -Xrunjdwp:transport=dt_socket,address=8002,server=y,suspend=n

9. 補足:Yoke の Java チュートリアルの誤記

http://pmlopes.github.io/yoke/#java の以下のコードは、

Yoke yoke = new Yoke(vertx)
  .use(new Favicon())
  .use(new Static("webroot"))
  .use(new Router()
    .all("/hello", new Handler<HttpServerRequest>() {
      @Override
      public void handle(HttpServerRequest request) {
        request.response().end("Hello World!");
      }
    })).listen(3000);

次のようにする必要があります。(HttpServerRequest -> YokeRequest)

Yoke yoke = new Yoke(vertx)
  .use(new Favicon())
  .use(new Static("webroot"))
  .use(new Router()
    .all("/hello", new Handler<YokeRequest>() {
      @Override
      public void handle(YokeRequest request) {
        request.response().end("Hello World!");
      }
    })).listen(3000);

(2013/06/16日現在)

10. 参照した情報

http://uehaj.hatenablog.com/entry/2013/06/03/225117
 Vert.xに注目したきっかけ
http://vertx.io/vertx20_docs/dev_guide.html#auto-redeploy-and-see-your-changes-immediately
 auto-redeployについて知ったきっかけ
https://groups.google.com/forum/?fromgroups#!searchin/vertx/auto$20redeploy
 auto-redeployについて調べた