JavalinをTomcatで動かす方法（2024/10時点）

通常、JavalinはJettyサーバーと密接に統合されているため、Tomcatで直接動作させるのが難しいとされています。しかし、Javalinのサーブレット機能を活用することで、Tomcatコンテナ上でもJavalinを動作させることが可能です。この方法を使って、Tomcat上でJavalinアプリケーションを実行する手順をまとめました。

## 必要な環境と前提条件
- Java 17
- Apache Tomcat 10.1
- Maven

# プロジェクト設定

まず、`pom.xml`でJavalinと必要な依存関係を定義します。重要な点として、`Javalin`から`Jetty`の依存を排除し、TomcatでサポートされているサーブレットAPIを使用します。

## `pom.xml`

```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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.demo</groupId>
    <artifactId>TomcatJavalin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>io.javalin</groupId>
            <artifactId>javalin</artifactId>
            <version>6.3.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-server</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.eclipse.jetty.websocket</groupId>
                    <artifactId>websocket-server</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.1.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.5</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.7</version>
        </dependency> 
    </dependencies>

    <build>
        <finalName>tabi2auto</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.1</version>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <release>17</release>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
```

# サーブレット設定

Tomcat上で動作するように、WEB-INFの中に`web.xml`を追加しておきます。

## `web.xml`

```xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <display-name>Javalin on Tomcat</display-name>

</web-app>
```

# `TomcatJavalin.java` - メインのJavalinアプリケーションクラスを継承して作成

`TomcatJavalin`クラスは、Javalinの機能を継承して作成しています。Javalinはjettyの依存関係があるため、Tomcat上で動作させるには、以下のように依存関係を取り除いた状態にカスタムが必要です。

```java
package com.example.demo;

import java.util.function.Consumer;

import io.javalin.Javalin;
import io.javalin.config.JavalinConfig;

public class TomcatJavalin extends Javalin {

    public TomcatJavalin(final JavalinConfig config) {

        super(config);
    }


    public static TomcatJavalin create() {

        return TomcatJavalin.create(config -> {
        });
    }


    public static TomcatJavalin create(final Consumer <JavalinConfig> config) {

        final var cfg = new JavalinConfig();
        JavalinConfig.applyUserConfig(cfg, config); // mutates app.config and app (adds http-handlers)
        return new TomcatJavalin(cfg);
    }
}
```


# `TomcatJavalinServlet.java` - Javalinのサーブレット実装

`TomcatJavalinServlet`は、`HttpServlet`を継承し、`init`メソッドでJavalinのインスタンスを設定します。

```java
package com.example.demo;

import io.javalin.http.servlet.JavalinServlet;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet (urlPatterns = "/api/*", name = "TomcatJavalinServlet")
public class TomcatJavalinServlet extends HttpServlet {

    private JavalinServlet javalinServlet;

    @Override
    public void init() {

        final var app = TomcatJavalin.create();
        app.get("/api/hello", ctx -> ctx.result("Hello from Javalin!"));
        this.javalinServlet = (JavalinServlet) app.javalinServlet();
    }


    @Override
    protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {

        this.javalinServlet.service(req, resp);

    }
}
```

# 最後に

この設定で、JavalinはTomcatサーブレットコンテナで動作するようになりました。Jettyに依存せずにTomcatで実行するには、依存関係の除外や`JavalinServlet`の使用が重要です。

サンプルリポジトリはこちら: [javalin-sample](https://github.com/blue-islands/javalin-sample)

このサンプルには基本的なセットアップ、javalinとTomcatの連携が含まれていますので、ぜひ参照してみてください。

## めっちゃ古いけど、参考にした記事

https://javalin.io/2018/11/15/javalin-embedded-example.html