1. 概要

Undertowは、JBossの非常に軽量で高性能なWebサーバーです。 NIOでブロッキングAPIと非ブロッキングAPIの両方をサポートします。

Javaと記述されているため、組み込みモードの任意のJVMベースのアプリケーションで使用できます。JBossの WildFly サーバーでさえ、サーバーのパフォーマンスを向上させるために内部でUndertowを使用します。

このチュートリアルでは、Undertowの機能とその使用方法を示します。

2. なぜアンダートウ?

  • 軽量: Undertow は、1MB未満と非常に軽量です。 埋め込みモードでは、実行時に4MBのヒープスペースのみを使用します
  • サーブレット3.1:サーブレット3.1を完全にサポートします
  • Web Socket:Web Socket機能をサポートします( JSR-356 を含む)
  • 持続的接続:デフォルトでは、 Undertow には、 keep-alive 応答ヘッダーを追加することにより、HTTP持続的接続が含まれています。 接続の詳細を再利用することにより、永続的な接続をサポートするクライアントがパフォーマンスを最適化するのに役立ちます

3. Undertowの使用

簡単なWebサーバーを作成して、Undertowの使用を開始しましょう。

3.1. Mavenの依存関係

Undertow を使用するには、pom.xmlに次の依存関係を追加する必要があります。

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-servlet</artifactId>
    <version>1.4.18.Final</version>
</dependency>

実行可能なjarを作成するには、maven-shade-pluginも追加する必要があります。 そのため、以下の構成も追加する必要があります。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Undertow の最新バージョンは、 CentralMavenリポジトリで入手できます。

3.2. シンプルサーバー

以下のコードスニペットを使用すると、Undertowの BuilderAPIを使用して簡単なWebサーバーを作成できます。

public class SimpleServer {
    public static void main(String[] args) {
        Undertow server = Undertow.builder().addHttpListener(8080, 
          "localhost").setHandler(exchange -> {
            exchange.getResponseHeaders()
            .put(Headers.CONTENT_TYPE, "text/plain");
          exchange.getResponseSender().send("Hello Baeldung");
        }).build();
        server.start();
    }
}

ここでは、 Builder APIを使用して、8080ポートをこのサーバーにバインドしました。 また、ハンドラーを使用するためにラムダ式を使用していることに注意してください。

以下のコードスニペットを使用して、ラムダ式を使用せずに同じことを行うこともできます。

Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
  .setHandler(new HttpHandler() {
      @Override
      public void handleRequest(HttpServerExchange exchange) 
        throws Exception {
          exchange.getResponseHeaders().put(
            Headers.CONTENT_TYPE, "text/plain");
          exchange.getResponseSender().send("Hello Baeldung");
      }
  }).build();

ここで注意すべき重要なことは、 HttpHandlerAPIの使用法です。 私たちのニーズに基づいてUndertowアプリケーションをカスタマイズするための最も重要なプラグインです。

この場合、 Content-Type:text /plain応答ヘッダーを各要求に追加するカスタマイズされたハンドラーを追加しました。

同様に、各応答でデフォルトのテキストを返したい場合は、以下のコードスニペットを使用できます。

exchange.getResponseSender()
  .send("Hello Baeldung");

3.3. 安全なアクセス

ほとんどの場合、すべてのユーザーがサーバーにアクセスすることを許可していません。 通常、有効な資格情報を持つユーザーがアクセスできます。Undertowでも同じメカニズムを実装できます。

これを実装するには、すべてのリクエストに対してユーザーの信頼性をチェックするIDマネージャーを作成する必要があります。

これには、UndertowのIdentityManagerを使用できます。

public class CustomIdentityManager implements IdentityManager {
    private Map<String, char[]> users;

    // standard constructors
    
    @Override
    public Account verify(Account account) {
        return account;
    }
 
    @Override
    public Account verify(Credential credential) {
        return null;
    }
 
    @Override
    public Account verify(String id, Credential credential) {
        Account account = getAccount(id);
        if (account != null && verifyCredential(account, credential)) {
            return account;
        }
        return null;
    }
}

IDマネージャーを作成したら、ユーザーの資格情報を保持するレルムを作成する必要があります。

private static HttpHandler addSecurity(
  HttpHandler toWrap, 
  IdentityManager identityManager) {
 
    HttpHandler handler = toWrap;
    handler = new AuthenticationCallHandler(handler);
    handler = new AuthenticationConstraintHandler(handler);
    List<AuthenticationMechanism> mechanisms = Collections.singletonList(
      new BasicAuthenticationMechanism("Baeldung_Realm"));
    handler = new AuthenticationMechanismsHandler(handler, mechanisms);
    handler = new SecurityInitialHandler(
      AuthenticationMode.PRO_ACTIVE, identityManager, handler);
    return handler;
}

ここでは、AuthenticationModePRO_ACTIVEとして使用しました。これは、このサーバーに着信するすべての要求が、定義された認証メカニズムに渡され、認証を熱心に実行することを意味します。

AuthenticationModeCONSTRAINT_DRIVENとして定義すると、それらの要求のみが、認証を義務付ける制約がトリガーされる定義済みの認証メカニズムを通過します。

ここで、起動する前に、このレルムとIDマネージャーをサーバーにマップする必要があります。

public static void main(String[] args) {
    Map<String, char[]> users = new HashMap<>(2);
    users.put("root", "password".toCharArray());
    users.put("admin", "password".toCharArray());

    IdentityManager idm = new CustomIdentityManager(users);

    Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
      .setHandler(addSecurity(e -> setExchange(e), idm)).build();

    server.start();
}

private static void setExchange(HttpServerExchange exchange) {
    SecurityContext context = exchange.getSecurityContext();
    exchange.getResponseSender().send("Hello " + 
      context.getAuthenticatedAccount().getPrincipal().getName(),
      IoCallback.END_EXCHANGE);
}

ここでは、資格情報を持つ2つのユーザーインスタンスを作成しました。 サーバーが起動したら、サーバーにアクセスするには、これら2つの資格情報のいずれかを使用する必要があります。

3.4. Webソケット

UnderTowのWebSocketHttpExchangeAPIを使用してWebソケット交換チャネルを作成するのは簡単です。

たとえば、以下のコードスニペットを使用して、パスbaeldungAppでソケット通信チャネルを開くことができます。

public static void main(String[] args) {
    Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
      .setHandler(path().addPrefixPath("/baeldungApp", websocket(
        (exchange, channel) -> {
          channel.getReceiveSetter().set(getListener());
          channel.resumeReceives();
      })).addPrefixPath("/", resource(new ClassPathResourceManager(
        SocketServer.class.getClassLoader(),
        SocketServer.class.getPackage())).addWelcomeFiles("index.html")))
        .build();

    server.start();
}

private static AbstractReceiveListener getListener() {
    return new AbstractReceiveListener() {
        @Override
        protected void onFullTextMessage(WebSocketChannel channel, 
          BufferedTextMessage message) {
            String messageData = message.getData();
            for (WebSocketChannel session : channel.getPeerConnections()) {
                WebSockets.sendText(messageData, session, null);
            }
        }
    };
}

index.html という名前のHTMLページを作成し、JavaScriptの WebSocketAPIを使用してこのチャネルに接続できます。

3.5. ファイルサーバー

Undertow を使用すると、ディレクトリコンテンツを表示し、ディレクトリからファイルを直接提供できるファイルサーバーを作成することもできます。

public static void main( String[] args ) {
    Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
        .setHandler(resource(new PathResourceManager(
          Paths.get(System.getProperty("user.home")), 100 ))
        .setDirectoryListingEnabled( true ))
        .build();
    server.start();
}

ディレクトリの内容を表示するためにUIコンテンツを作成する必要はありません。 すぐに使用できるUndertowは、この表示機能のページを提供します。

4. SpringBootプラグイン

TomcatJettyの他に、 Spring Boot は、組み込みサーブレットコンテナとしてUnderTowをサポートしています。 Undertow を使用するには、 pom.xmlに次の依存関係を追加する必要があります:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
    <version>1.5.6.RELEASE</version>
</dependency>

Spring Boot Undertowプラグインの最新バージョンは、 CentralMavenRepositoryで入手できます。

5. 結論

この記事では、 Undertow と、それを使用してさまざまなタイプのサーバーを作成する方法について学習しました。

いつものように、完全なソースコードはGitHub利用できます。