Minimal Embedded Tomcat

f:id:Naotsugu:20150320232437p:plain

Tomcat を組み込みで扱う最小限のサンプルです。

最小限の組み込み Tomcat

main から最低限の設定をして組み込みの Tomcat を起動します。

package embed;

import org.apache.catalina.Context;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.startup.Tomcat;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;

public class Main {
    
    public static void main(String[] args) throws Exception {

        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        File base = new File("src/main/webapp/");
        Context context = tomcat.addContext("/app", base.getAbsolutePath());

        Tomcat.addServlet(context, "default", new DefaultServlet()).addMapping("/");

        Tomcat.addServlet(context, "hello", new HttpServlet() {
            protected void service(HttpServletRequest req, HttpServletResponse resp)
                    throws ServletException, IOException {
                Writer w = resp.getWriter();
                w.write("Hello, World!");
                w.flush();
            }
        }).addMapping("/hello");

        tomcat.start();
        Desktop.getDesktop().browse(new URI("http://localhost:8080/app/hello"));
        tomcat.getServer().await();
    }

}

addContext でコンテクストパスを設定しています。 "src/main/webapp/" をルートとして指定しているので、ここに静的ファイルを置けます。

静的ファイルを扱うためにDefaultServletを登録しています。

同じように Hello, World! を返すだけのサーブレットを匿名クラスで登録しています。

main 抜けてすぐに終了しないようにtomcat.getServer().await();しています。

build.gradle

build.gradle は以下のようになります。

apply plugin: 'java'
apply plugin: 'application'

sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '1.0'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.apache.tomcat.embed:tomcat-embed-core:8.0.20'
    compile 'org.apache.tomcat.embed:tomcat-embed-logging-juli:8.0.20'
}

mainClassName = "embed.Main"

tomcat-embed-core とロガー用に tomcat-embed-logging-juli を依存に追加し、mainClassName でメインクラス指定しています。

以下のように実行します。

gradle run

f:id:Naotsugu:20150320232618p:plain

Jersey で JAX-RS に対応させる

build.gradle に以下の依存を追加します。

compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.17'

簡単なリソースクラスを作成します。

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("helloworld")
public class HelloWorldResource {
    
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String sayHello() {
        return "Hello from Tomcat Embedded with Jersey!";
    }
}

REST リクエストの受け口となるサーブレットを作成します。

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import javax.ws.rs.ApplicationPath;

public class RestServlet extends ServletContainer {

    public RestServlet() {
        super(new RsConfig());
    }

    @ApplicationPath("rest")
    public static class RsConfig extends ResourceConfig {
        public RsConfig() {
            packages(getClass().getPackage().getName());
        }
    }
}

このサーブレットTomcat.addServlet()Tomcat に登録するだけです。

HK2 で DI する

jersey は HK2 に依存しているので、このままで DI も利用できます。

簡単なサービスを作成します。

public class HelloService {

    public String greeting() {
        return "Hello";
    }
}

サービスをリソースから呼ぶようにします。

@Path("helloworld")
public class HelloWorldResource {
    
    @Inject HelloService service;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String sayHello() {
        return service.greeting() + " from Tomcat Embedded with Jersey!";
    }
}

ResourceConfig にバインドの設定を追加します。

    public RsConfig() {
        packages(getClass().getPackage().getName());
        register(new AbstractBinder() {
            @Override
            protected void configure() {
                bindAsContract(HelloService.class);
            }
        });
    }

f:id:Naotsugu:20150321182405p:plain