JBoss Undertowの紹介

JBoss Undertowの概要

1. 概要

Undertow is an extremely lightweight and high-performance web server from JBoss.NIOでブロッキングAPIと非ブロッキングAPIの両方をサポートします。

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

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

2. Undertowを選ぶ理由

  • 軽量:Undertowは1MB未満と非常に軽量です。 埋め込みモードでは、実行時に4MBのヒープスペースしか使用しません

  • サーブレット3.1:Servlet 3.1を完全にサポート

  • Web Socket:Web Socket機能(JSR-356を含む)をサポートします

  • 持続的接続:デフォルトでは、Undertowには、keep-alive応答ヘッダーを追加することでHTTP持続的接続が含まれます。 永続的な接続をサポートするクライアントが、接続の詳細を再利用してパフォーマンスを最適化するのに役立ちます

3. Undertowの使用

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

3.1. メーベン依存

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


    io.undertow
    undertow-servlet
    1.4.18.Final

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


    org.apache.maven.plugins
    maven-shade-plugin
    
        
            package
            
                shade
            
        
    

Undertowの最新バージョンはCentral Maven Repositoryで利用できます。

3.2. シンプルなサーバー

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

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 example");
        }).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 example");
      }
  }).build();

ここで注意すべき重要なことは、HttpHandlerAPIの使用法です。 It’s the most important plugin to customize an Undertow application based on our needs.

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

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

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

3.3. 安全なアクセス

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

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

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

public class CustomIdentityManager implements IdentityManager {
    private Map 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 mechanisms = Collections.singletonList(
      new BasicAuthenticationMechanism("example_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 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’s WebSocketHttpExchangeAPIを使用してWebソケット交換チャネルを作成するのは簡単です。

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

public static void main(String[] args) {
    Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
      .setHandler(path().addPrefixPath("/exampleApp", 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:に次の依存関係を追加する必要があります


    org.springframework.boot
    spring-boot-starter-undertow
    1.5.6.RELEASE

Spring Boot Undertow pluginの最新バージョンはCentral Maven Repositoryで利用できます。

5. 結論

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

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