G::blog

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

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 が正常に動作するようになりました。