Jettyの使い方1

2019年5月15日

jettyについての全投稿は/tag/jettyにあるので参照されたい

Jettyは、そのパッケージ名がorg.mortbay.jettyの時代から使用しているのだが(現在はorg.eclipse.jetty)、バージョンがあがり使い方がかなり違ってきてしまったので、現時点で最新のJetty9の使い方をまとめてみる。

ここでは、2018/2時点で最新の9.4.8を使うが、この表にある通り、Java8が必須となる。

なお、ここではアプリに組み込んで(Embedded)使用することを前提にしているので、見るべきマニュアルは、Embedding Jettyになるのだが、最初の方の例は大して実用性は無い。途中の例から試してみる。

組み込みJettyで実現したいこととしては、任意のコンテキストパスを作り、それぞれのパスに異なるリソースやサーブレットを割り当てることである。特に後から設定ファイルで動作を変更したいなどということはない。

最も簡単な例

最も簡単な例を作成してみると、以下になる。


import org.eclipse.jetty.server.*; import org.eclipse.jetty.server.handler.*; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class WebServerTest { public static void main(String[] args) throws Exception { Server server = new Server(8080); ContextHandler context = new ContextHandler("/"); context.setHandler(new HelloHandler("Root Hello")); ContextHandler contextFR = new ContextHandler("/fr"); contextFR.setHandler(new HelloHandler("Bonjoir")); ContextHandler contextIT = new ContextHandler("/it"); contextIT.setHandler(new HelloHandler("Bongiorno")); ContextHandler contextV = new ContextHandler("/"); contextV.setVirtualHosts(new String[] { "127.0.0.2" }); contextV.setHandler(new HelloHandler("Virtual Hello")); ContextHandlerCollection contexts = new ContextHandlerCollection(); contexts.setHandlers(new Handler[] { contextFR, contextIT, contextV, context }); server.setHandler(contexts); server.start(); server.dumpStdErr(); server.join(); } public static class HelloHandler extends AbstractHandler { final String greeting; public HelloHandler(String greeting) { this.greeting = greeting; } public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html; charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter out = response.getWriter(); out.println("<h1>" + greeting + "</h1>"); baseRequest.setHandled(true); } } }

ContextHandlerにコンテキストパスを指定すると、そのURLが呼ばれたときに所定のハンドラが呼び出される。そして、コンテキストパスは、例えば”/fr”を指定した場合、”/fr”でも”/fr/abc”でも同じハンドラが呼び出される。

そして、パスが一致するものが無い場合には、コンテキストパス”/”がすべて拾うようだ。つまり、”/abc”は”/”が呼び出される。

AbstractHandler#handleの引数

AbstractHandler#handleの引数の内容を調べる。

    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
      System.out.println("target:" + target);
      System.out.println("method:" + baseRequest.getMethod());

例えば、”/it”について、targetの値を表示させると、

  • “/it”を指定した場合、”/”
  • “/it/abc”を指定した場合、”/abc”

とコンテキストパスを除いた部分が示される。これを使用すれば、コンテキストパスの設定によらずに呼び出された相対パスを得ることができるというわけ。

メソッドはbaseRequest.getMethod()を見ればわかる。

requestは、通常のサーブレットと同じく、URL全体が示されてしまう。

また、”/it”を呼び出すと、自動で”/it/”に修正されてしまうのだが、これをやめさせるのは、例えば

    contextIT.setAllowNullPathInfo(true);

とする。

リソースを得る

特定のフォルダ以下を特定のコンテキストパス以下で見れるようにする。これにはResourceHandlerを使う。例えば、”/fr”以下に”.”フォルダ以下が現れるようにしてみる。

    ContextHandler contextFR = new ContextHandler("/fr");
    ResourceHandler resourceHandler = new ResourceHandler();
    resourceHandler.setDirectoriesListed(true);
    resourceHandler.setWelcomeFiles(new String[]{ "index.html" });
    resourceHandler.setResourceBase(".");
    contextFR.setHandler(resourceHandler);

コンテキストパスが重複している場合はどうなるのか?

ContextHandlerの登録順序によって、どのハンドラが使用されるかが決まるらしい。例えば、”/”を扱うハンドラ、”/fr”を扱うハンドラがあるとすると、”/fr”を先に、”/”を後にしないと、両方とも呼び出されてしまうようだ(確実なテストはしていない)。

したがって、より広い範囲をカバーするものは、後に登録しなければいけないと思われる。