ご存知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サーバーを作る!」をモチーフにこちらの記事にまとめています。