LoginSignup
15
17

More than 5 years have passed since last update.

【2018/12月更新】Jetty9でつくる組み込みwebサーバーとservletのTips

Last updated at Posted at 2017-04-24

ご存知Jetty9は軽量で高機能なwebサーバー、servletコンテナです。

Jetty9をJavaアプリ組み込みで使うときに、良く使う設定TIPSと、servletまで含めたサンプルコードを掲載します。

動作するソースコード一式(maven project)は https://github.com/riversun/jetty9-quick-start にあげています。

Jetty9でHTTPS(SSL/TLS)する記事は https://qiita.com/riversun/items/2909019123b28471ea79 にまとめました。

Jetty9 組み込み設定Tips (v.9.4.12で動作確認)

●Servletを追加する

"/api" というパスで受け付けるExampleServlet.javaというサーブレットを追加する

// ServletContextHandlerはサーブレットをハンドリングする
ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);

// サーブレットを追加
servletHandler.addServlet(new ServletHolder(new ExampleServlet()), "/api");

●フォームのアップロードサイズを指定する

大きなサイズのデータをアップロードしようとすると、java.lang.IllegalStateException: Form too largeをお見舞いされることがあります。
そういうときには、setMaxFormContentSizeで必要なサイズに指定します。


// フォームのアップロードサイズを指定
servletHandler.setMaxFormContentSize(1024 * 1024 * 1024);

●静的コンテンツ(static file)の置き場所を指定する

setResourceBaseでresource baseを指定します。

// ResourceHandlerは(ざっくりいうと)静的コンテンツをハンドリングする
final ResourceHandler resourceHandler = new ResourceHandler();

// 静的コンテンツの置き場所を指定
resourceHandler.setResourceBase(System.getProperty("user.dir") + "/htdocs");

●静的コンテンツのファイル一覧をみせたくない

Apache等古くから伝わるwebサーバー伝統のおせっかい機能です。
ファイル一覧(リスティング)表示をしたくない場合はこうします

// 静的コンテンツのファイル一覧(リスティング)表示しない
resourceHandler.setDirectoriesListed(false);

●デフォルトのhtmlをindex.htmlから変更したい

setWelcomeFilesで変更可能です

// 初期表示するファイルを指定
resourceHandler.setWelcomeFiles(new String[] { "index.html" });

●キャッシュを無効にする。キャッシュさせない。

HTMLやJavaScript開発者を悩ます静的コンテンツのキャッシュをオフする方法です。
setCacheControlによって、焼け石に水のF5連打から解放されます。

// キャッシュさせない
resourceHandler.setCacheControl("no-store,no-cache,must-revalidate");

●サーバーのバージョン情報をヘッダー等で送出させない

jettyのバージョン番号等情報がHTTPヘッダや、デフォルトのエラー画面等に表示したくない場合に使います
HttpConfig#setSendServerVersionで設定可能です。

2つの方法を示します
(やりたいことはシンプルなのに、コードがやや長くなります。ここは、jetty9リファクタでjetty8より複雑化しました)

【方法その1】
http connectorを自前でつくり、httpConfig をセットする

// デフォルトコンストラクタでサーバーを初期化する
final Server jettyServer = new Server();
jettyServer.setHandler(handlerList);

final int PORT = 8080;

// httpの設定クラス
final HttpConfiguration httpConfig = new HttpConfiguration();

// サーバーのバージョン情報をヘッダにのせない
httpConfig.setSendServerVersion(false);
final HttpConnectionFactory httpConnFactory = new HttpConnectionFactory(httpConfig);
final ServerConnector httpConnector = new ServerConnector(jettyServer, httpConnFactory);
httpConnector.setPort(PORT);
jettyServer.setConnectors(new Connector[] { httpConnector });

【方法その2】
(既存の)http connectorをひっぱりだしてきて、httpConfig をセットする

final int PORT = 8080;

final Server jettyServer = new Server(PORT );

for (Connector conn : jettyServer.getConnectors()) {
  for (ConnectionFactory connFactory : conn.getConnectionFactories()) {
    if (connFactory instanceof HttpConnectionFactory) {
      ((HttpConnectionFactory) connFactory).getHttpConfiguration().setSendServerVersion(false);
    }
  }
}

Servlet on Jetty9 サンプルコード全文

上で説明したコードを含むJSONを返すCORS(クロスドメイン)アクセス対応Web API風servletサンプルです。
Mavenのdependencyをセットして、コピペ、実行して http://localhost:8080/api?message=hello のようにアクセスします。

package com.example.jetty;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Jetty9 Quick Start Example
 */
public class ServletApp
{
    public static void main(String[] args) {

        // ServletContextHandlerはサーブレットをハンドリングする
        ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);

        // フォームのアップロードサイズを指定
        servletHandler.setMaxFormContentSize(1024 * 1024 * 1024);

        // サーブレットを追加
        servletHandler.addServlet(new ServletHolder(new ExampleServlet()), "/api");

        // ResourceHandlerは(ざっくりいうと)静的コンテンツをハンドリングする
        final ResourceHandler resourceHandler = new ResourceHandler();

        // 静的コンテンツの置き場所を指定
        resourceHandler.setResourceBase(System.getProperty("user.dir") + "/htdocs");

        // 静的コンテンツのファイル一覧(リスティング)表示しない
        resourceHandler.setDirectoriesListed(false);

        // 初期表示するファイルを指定
        resourceHandler.setWelcomeFiles(new String[] { "index.html" });

        // キャッシュさせない
        resourceHandler.setCacheControl("no-store,no-cache,must-revalidate");

        HandlerList handlerList = new HandlerList();

        // resourceHandlerが先にくるように指定する(逆にすると静的コンテンツは永遠によばれない。。)
        handlerList.addHandler(resourceHandler);
        handlerList.addHandler(servletHandler);

        // デフォルトコンストラクタでサーバーを初期化する
        final Server jettyServer = new Server();
        jettyServer.setHandler(handlerList);

        final int PORT = 8080;

        // httpの設定クラス
        final HttpConfiguration httpConfig = new HttpConfiguration();

        // サーバーのバージョン情報をヘッダにのせない
        httpConfig.setSendServerVersion(false);
        final HttpConnectionFactory httpConnFactory = new HttpConnectionFactory(httpConfig);
        final ServerConnector httpConnector = new ServerConnector(jettyServer, httpConnFactory);
        httpConnector.setPort(PORT);
        jettyServer.setConnectors(new Connector[] { httpConnector });

        try {
            jettyServer.start();
            jettyServer.join();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @SuppressWarnings("serial")
    public static class ExampleServlet extends HttpServlet {

        final ObjectMapper mObjectMapper = new ObjectMapper();

        final class Result {
            public boolean success;
            public String message;
        }

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

            // queryパラメータを取得
            final String paramMessage = req.getParameter("message");

            // レスポンス格納用POJO(あとでJSONに変換する)
            final Result result = new Result();
            result.success = true;
            result.message = "You say '" + paramMessage + "'";

            // CORS(Cross-Origin Resource Sharing)を有効にする
            resp.addHeader("Access-Control-Allow-Origin", "*");
            resp.addHeader("Access-Control-Allow-Headers", "Content-Type");

            // JSONを返すのContent-TypeをJSONにする
            final String CONTENT_TYPE = "application/json; charset=UTF-8";
            resp.setContentType(CONTENT_TYPE);

            final PrintWriter out = resp.getWriter();
            // JacksonでPOJOをJSONに変換する
            final String json = mObjectMapper.writeValueAsString(result);

            // レスポンスを生成
            out.println(json);
            out.close();

        }

        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Sorry, POST is not supported");
        }

        @Override
        protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Sorry, PUT is not supported");
        }

        @Override
        protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Sorry, DELETE is not supported");
        }

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

            // For PreFlight Access
            // CORS(Cross-Origin Resource Sharing)を有効にする
            resp.addHeader("Access-Control-Allow-Origin", "*");
            resp.addHeader("Access-Control-Allow-Headers", "Content-Type");

        }

    }


}

Maven

<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.12.v20180830</version>
</dependency>

<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>9.4.12.v20180830</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8.1</version>
</dependency>

ソースコード

ソースコードはこちらです
https://github.com/riversun/jetty9-quick-start

おわりに

Jettyの詳しい動作については、「20行でHTTPSサーバーを作る!」をモチーフにこちらの記事にまとめています。

15
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
17