2019年6月24日

Jetty

Jetty 跟 Tomcat 一樣,都支援標準 Servlet 及 JavaEE 規範,但 Jetty 架構比 Tomcat 簡單,可以獨立運作,也可以用 embedded 的方式嵌入 project 中。目前 Jetty 支援 Servlet Spec 3.1 及 JSP 2.3,有部分規範是以模組的方式支援,可藉由設定的方式 enable/disable。

在專案配置 Jetty 只需要以 Maven POM 引用 jetty,就可以用嵌入的方式啟動 Jetty。

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

也可以直接下載 Jetty,以 standalone 方式啟動,下載 Jetty 有兩種,第一種是標準的完整的 Jetty,裡面包含了 modules 以及一個 demo server 配置。另一種是 jetty-home,是最小的 package,提供給進階使用者使用。

要啟動 Jetty 可執行下載的 $JETTY_HOME/bin 目錄中的 ./jetty.sh start,只要用 ./jetty.sh stop 停止 Jetty。

如果要以 console 模式啟動 jetty,可切換到 $JETTY_HOME 目錄,然後直接以 java 執行

java -jar start.jar

預設都是提供 http://localhost:8080 的服務,因為預設沒有部署任何 webapp,所以連接該網址後會得到 404 Error。

Jetty 裡面有個 Demo Base 配置範例,可以切換到該目錄,啟動範例

cd demo-base/
java -jar ../start.jar

可用以下指令,檢查 demo base 裡面的 modules 及 config

cd demo-base/
java -jar ../start.jar --list-modules

java -jar ../start.jar --list-config

Configuration

  • jetty.home
    定義 Jetty 主程式、lib、default modules與 default XML files 的目錄,這是不會修改異動的部分
  • jetty.base
    定義了 Jetty Server、configurtion、logs與 webapps 目錄

這兩個 properties 可透過 command line 設定,或是由環境變數 $JETTY_HOME, $JETTY_BASE 決定

以下是建立一個新的 jetty.base 目錄,然後 enable HTTP connector 與 web app deployer modules,並複製並部署 demo webapp

# JETTY_HOME 設定為下載 Jetty Distribution 解壓縮後的目錄
JETTY_HOME=~/java/jetty
JETTY_BASE=/tmp/mybase
mkdir $JETTY_BASE
cd $JETTY_BASE

直接啟動,會得到 error

$ java -jar $JETTY_HOME/start.jar
WARNING: Nothing to start, exiting ...
$ java -jar $JETTY_HOME/start.jar --create-startd
MKDIR : ${jetty.base}/start.d
INFO  : Base directory was modified

$ java -jar $JETTY_HOME/start.jar --add-to-start=http,deploy
INFO  : webapp          transitively enabled, ini template available with --add-to-start=webapp
INFO  : server          transitively enabled, ini template available with --add-to-start=server
INFO  : security        transitively enabled
INFO  : servlet         transitively enabled
INFO  : http            initialized in ${jetty.base}/start.d/http.ini
INFO  : threadpool      transitively enabled, ini template available with --add-to-start=threadpool
INFO  : deploy          initialized in ${jetty.base}/start.d/deploy.ini
MKDIR : ${jetty.base}/webapps
INFO  : Base directory was modified


$ cp $JETTY_HOME/demo-base/webapps/async-rest.war webapps/ROOT.war
$ java -jar $JETTY_HOME/start.jar

在 command line 可直接修改 http port

java -jar $JETTY_HOME/start.jar jetty.http.port=8081

但這只是暫時的設定,實際上設定值會用以下順序決定

  1. start.d/http.ini 該檔案是 http module 的設定參數
  2. modules/httpd.mod 定義料 http module 並指定使用 etc/jetty-http.xml 設定檔
  3. jetty.http.port 屬性,是由 /etc/jetty-http.xml 指定使用該屬性

後續再增加 SSL 及 http2

$ java -jar $JETTY_HOME/start.jar --add-to-start=https,http2

$ java -jar $JETTY_HOME/start.jar

啟動後,可使用 https://localhost:8443/


可用 --help 查詢所有 command line 指令

java -jar $JETTY_HOME/start.jar --help

deployment

部署 Jetty Webapp 有幾種方式,而且都可以在不停止 Jetty 的條件下,直接部署該 webapp

  1. 一個目錄 (ex: example/) 裡面有 WEB-INF 目錄,以及 web.xml,將目錄放到 $JETTY_BASE 的 webapps 目錄中,就可以部署該 webapp
  2. example.war
  3. 一個獨立的 XML 檔案,裡面定義 webapp 的相關資訊。可參考 demo-base/test.xml 為範例

在剛剛的 /tmp/mybase 新的 $JETTY_BASE 目錄中,如果要部署一個 example webapp,裡面有 JSP 網頁,必須先增加 jsp module support

$ java -jar $JETTY_HOME/start.jar --add-to-start=jsp

example 目錄結構包含一個目錄,兩個檔案

example/
    index.jsp
    WEB-INF/
        web.xml

web.xml 就是很單純的標準 webapp

<?xml version="1.0" encoding="UTF-8"?>
<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">
</web-app>

啟動 jetty 後,將 example 複製到 /tmp/mybase/webapps 裡面,可看到 example hot deploy 的 log

$ java -jar $JETTY_HOME/start.jar
.....

oejsh.ContextHandler:Scanner-0: Started o.e.j.w.WebAppContext@7a5904a3{ex,/ex,file:///private/tmp/mybase/webapps/example/,AVAILABLE}{/example}

另外要注意的是,在 webapps 裡面如果有 ROOT 目錄,或是 ROOT.war,這個是 jetty server 的 default web context application。也就是 http://localhost:8080/ 連線取得的網頁。

Configuration

如何設定 jetty

POJO configuration
直些撰寫 java code 設定 jetty。或是使用 etc/jetty.xml,這是 main jetty XML config。或是利用 IoC framework (spring) 初始化 jetty objects as Spring beans
Jetty Start Configuration Files

jetty distribution 使用以下設定檔,透過 start.jar 初始化 jetty

  • ini files

    jetty 利用 command line 讀取 $JETTY_BASE/start.ini and/or $JETTY_BASE/start.d/*.ini files,產生 command line arguments

    • --module=name 啟用 module
    • name=value 用在參數化的 Jetty IoC XML
    • XML files: Jetty IoC(Spring) XML 格式
    • 標準 property file,包含附加的 start properties
    • 其他 start.jar options (java -jar start.jar --help)
    • 一些 JVM options ,以 --exec 整合,例如 -Xbootclasspath
  • mod files

    $JETTY_HOME/modules/*.mod files,以 --module=name 啟用,每個 mod 都定義了

    • module dependencies for ordering and activation
    • 需增加的 libraries
    • module 增加的 command line 參數
    • 啟用 module 需要的檔案
    • template ini,可用 --add-to-start=name 啟用
  • XML files

    Jetty IoC XML format 或是 Spring IoC format,通常放在 $JETTY_HOME/etc/ ,附加的 XML 放在 $JETTY_BASE/etc/


以下是這些設定檔的關係圖


在 Jetty 需要設定什麼?

  • Server

    核心設定檔為 /etc/jetty.xml,可加上其他 server configurations:

    • ThreadPool
      server instance 提供了一個 ThreadPool instance,這是 jetty server components 使用的預設 Executor service,可在 start.ini 或 start.d/server.ini 調整 max/min size

    • Handlers
      jetty 只能有一個 Handler instance 處理 incomping HTTP request,預設 handler tree 設定在 etc/jetty.xml,包含了 a context Handler Collection 及 Default Handler。

      Context Handler Collection 由 context path 選擇 handler,也就是 deployed Context Handler 及 Web Application Contexts

      Default Handler 無法處理的 request 會產生 404 page,可增加其他 handlers(ex: jetty-rewrite.xml, jetty-requestlog.xml) 或增加 hot deploy handlers (ex: jetty-deploy.xml)

    • Server Attributes
      server 會儲存 attribute map of strings,給 components 使用,如果 value objects 實作了 LifeCycle interface,就會 started/stopped with the server,通常 server attributes 是儲存 server-wide default values

    • Server fields
      可設定在 start.ini 或 start.d/server.init,控制 HTTP responses 的 dates, versions

    • Connectors
      接收 HTTP 或其他 protocol 的 connections

    • Services 儲存 service objects,通常會以 LifeCycle beans 存在,例如 Login Services 及 DataSources。以 server level 設定,在 webapp 使用。

  • Connector

    network endpoint,接收某個 protocol 的 connection,標準 protocol 為 http.ini, https.ini, jetty-http2.ini

    • Port
      jetty.http.port (jetty.ssl.port),預設為 8080 (8443)
    • Host
      jetty.host 預設為 0.0.0.0

    • Idle Timeout
      在 connector 動作前,可 idle 多少 ms,否則就 close connection

    • HTTP Configuration
      包含 http, https, http2

    • SSL Context Factory
      TLS connector type (https, http2) 設定 keystore 及 truststore

    jetty 9 是以單一 ServerConnector type,他是 NIO based,並以 Connection Factories 處理多個 protocols

  • Contexts

    • contextPath
      URL prefix,例如 /foo 可處理 /foo, /foo/index.html ... 這些 URL。 / 稱為 root context

    • virtualHost
      context 可設定多個 virtual hosts,virtual host 不需要設定 network parameters。virtual host 代表 IP 的 name service alias

    • classPath
      context 可有自訂的 classpath,該 context 內執行的 handler,有增加該 classpath 的 thread context classloader。標準 webapp 會增加 WEB-INF/lib 及 WEB-INF/classes 這兩個目錄到 classpath

    • attributes
      ex: javax.servlet.context.tempdir 用在 webapp 使用 File instance 的 temp dir

    • resourceBase
      包含 static resource for the context,圖片或 html


  • Context Configuration by API

    在 embedded server,是呼叫 ContextHandler API 進行 context 設定

package org.eclipse.jetty.embedded;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;

public class OneContext
{
    public static void main( String[] args ) throws Exception
    {
        Server server = new Server( 8080 );

        // Add a single handler on context "/hello"
        ContextHandler context = new ContextHandler();
        context.setContextPath( "/hello" );
        context.setHandler( new HelloHandler() );

        // Can be accessed using http://localhost:8080/hello

        server.setHandler( context );

        // Start the server
        server.start();
        server.join();
    }
}
  • Context Configuration by IoC XML

    以下 XML 設定,提供 jetty distribution 的 javadoc context

<?xml version="1.0"  encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC
    "-//Mort Bay Consulting//DTD Configure//EN"
    "http://www.eclipse.org/jetty/configure_9_3.dtd">

<!--
  Configure a custom context for serving javadoc as static resources
-->

<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
  <Set name="contextPath">/javadoc</Set>
  <Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/javadoc/</Set>
  <Set name="handler">
    <New class="org.eclipse.jetty.server.handler.ResourceHandler">
      <Set name="welcomeFiles">
        <Array type="String">
          <Item>index.html</Item>
        </Array>
      </Set>
      <Set name="cacheControl">max-age=3600,public</Set>
    </New>
  </Set>
</Configure>
  • Configuring Web Applications

    jetty 用以下方式處理 WAR application

    • classpath 包含 WEB-INF/lib, WEB-INF/classes
    • WEB-INF/web.xml 定義 init parameters, filters, servlets, listeners, security constraints, welcome files, resources
    • annotations 處理 WEB-INF/lib 是定義的 filters, servlets, listeners
    • (optional) WEB-INF/jetty-web.xml 定義 Jetty IoC config
  • Setting the Context Path

    在 WEB-INF/jetty-web.xml 可設定 context path

    <?xml version="1.0"  encoding="UTF-8"?>
    <!DOCTYPE Configure PUBLIC
    "-//Mort Bay Consulting//DTD Configure//EN"
    "http://www.eclipse.org/jetty/configure_9_3.dtd">
    
    <Configure class="org.eclipse.jetty.webapp.WebAppContext">
        <Set name="contextPath">/contextpath</Set>
    </Configure>

    也可以直接在 $JETTY_HOME/webapps/test.xml,就定義了一個 webapp,test.xml 包含 war 的位置,及 contextPath

    <?xml version="1.0"  encoding="UTF-8"?>
    <!DOCTYPE Configure PUBLIC
        "-//Mort Bay Consulting//DTD Configure//EN"
        "http://www.eclipse.org/jetty/configure_9_3.dtd">
    
    <Configure class="org.eclipse.jetty.webapp.WebAppContext">
      <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test.war</Set>
      <Set name="contextPath">/test</Set>
    </Configure>
  • Setting an Authentication Realm

    標準 realm name 設定在 web.xml,例如以下是宣告使用 BASIC authentication,並使用 "Test Realm"

    <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>Test Realm</realm-name>
    </login-config>

    "Test Realm" 設定在 $JETTY_BASE/etc/test-realm.xml,並傳入 start.ini 及 start.d/server.ini

    以下設定 LoginService

    <?xml version="1.0"?>
    <!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_3.dtd">
    <Configure id="Server" class="org.eclipse.jetty.server.Server">
        <!-- =========================================================== -->
        <!-- Configure Authentication Login Service                      -->
        <!-- Realms may be configured for the entire server here, or     -->
        <!-- they can be configured for a specific web app in a context  -->
        <!-- configuration (see $(jetty.home)/webapps/test.xml for an    -->
        <!-- example).                                                   -->
        <!-- =========================================================== -->
        <Call name="addBean">
          <Arg>
            <New class="org.eclipse.jetty.security.HashLoginService">
              <Set name="name">Test Realm</Set>
              <Set name="config"><Property name="jetty.demo.realm" default="etc/realm.properties"/></Set>
              <Set name="hotReload">false</Set>
            </New>
          </Arg>
        </Call>
    
        <Get class="org.eclipse.jetty.util.log.Log" name="rootLogger">
          <Call name="warn"><Arg>demo test-realm is deployed. DO NOT USE IN PRODUCTION!</Arg></Call>
        </Get>
    </Configure>

隱藏 Server 資訊

在 start.ini 將 server module 裡面的 jetty.httpConfig.sendServerVersion 設定改為 false

--module=server
## Whether to send the Server: header
jetty.httpConfig.sendServerVersion=false

References

The Definitive Reference

CentOS 7 安装jetty

Deploying Web Applications in Jetty

system service

Setup SSL in Jetty

沒有留言:

張貼留言