Jerseyの設定1(web.xmlとかApplicationクラスとか)

Jerseyの設定がバージョンによって違うが、webにある情報が色々混在していたりバージョンが不明だったりするので整理。
DeltaSpikeといいJerseyといい、結局は公式ドキュメントを読み込まないといけなくなる。英語力無いのが辛いところ。
Chapter 4. Application Deployment and Runtime Environments

環境

  • Jersey2.22.1
  • Java8
  • Tomcat8
  • Eclipse Luna(4.4.2)

テスト用プロジェクト

EclipseでJersey(JAX-RS)を始める - Qiita
こちらを参考にプロジェクトを作成。

Servlet2.5

自動作成されるpom.xmlやweb.xmlは、Servlet2.5(以上)の設定になっている様子。
Servlet2.5はTomcat6になるので今更使うことはないが軽く整理。
Servlet3.xは次回

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
     see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>mypackage.test</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/webapi/*</url-pattern>
  </servlet-mapping>
</web-app>

プロジェクト作成して自動生成されたweb.xmlサーブレットとしてJerseyを動かす設定になっている。

もしサーブレットフィルタとして動かす場合は、filter関連のタグを使う。

  <filter>
    <filter-name>Jersey Web Application</filter-name>
    <filter-class>org.glassfish.jersey.filter.ServletContainer</filter-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>mypackage.test</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </filter>
  <filter-mapping>
    <filter-name>Jersey Web Application</filter-name>
    <url-pattern>/webapi/*</url-pattern>
  </filter-mapping>

<url-pattern>/webapi/*</url-pattern>はWebリソースを提供するパスを指定する。
この場合、Eclipseプロジェクトがtestとして作っているので、各リソースへは「http://localhost:8080/test/webapi/・・・」というURLになる。

param-nameで指定しているjersey.config.server.provider.packagesはリソースクラスの読み込み先を指定する。
param-valueで指定したパッケージ内のクラスがスキャンされる。デフォルトではサブパッケージもスキャン対象。コンマ区切りで複数指定可。

サブパッケージをスキャン対象にしたくない場合は、recursiveをfalseにする。

    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>mypackage.test</param-value>
    </init-param>
    <init-param>
      <param-name>jersey.config.server.provider.scanning.recursive</param-name>
      <param-value>false</param-value>
    </init-param>

リソースクラスを個別に指定することもでき、その場合はjersey.config.server.provider.classnamesを使う。

<init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>mypackage.test.MyResource</param-value>
</init-param>

こちらもコンマ区切りで複数クラス指定可。

packagesとclassnamesは両方指定可能のようなので、通常はpackagesを使用して特殊なものだけclassnamesを使う、という使い方になるはず。
(普通はpackagesだけで収まるようにすると思うけど)

pom.xml

dependenciesだけ抜き出し

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
            <!-- artifactId>jersey-container-servlet</artifactId -->
        </dependency>
        <!-- uncomment this to get JSON support
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
        </dependency>
        -->
    </dependencies>

コメントアウトされている通り、servlet2.x に適合させる必要がなければjersey-container-servletを使う。 つまりjersey-container-servlet-coreはServlet2.x(2.5)用の依存指定になる。
余談:moxyの方はJerseyデフォルトのJSONバインドモジュール。JSONリクエストをPOJOで受け取りたいとか、POJOを戻り値にしたい(JSON変換されてレスポンス)、という時に使う。

javax.ws.rs.core.Application

ここまではリソースクラスの読み込み先などをweb.xmlで設定していたが、実はこれは簡易版(?)で本来はjavax.ws.rs.core.Applicationを継承したクラスを作成して、そのクラスで設定をするものらしい。
たぶんApplicationクラスを作成するのはJAX-RSの仕様で、Jerseyを使うのであればorg.glassfish.jersey.servlet.ServletContainerがそのあたりを吸収してweb.xmlで書けるようにしてくれている、のかな?

とりあえずApplicationクラスを作成。

public class MyApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(MyResource.class);
        return s;
    }
}

リソースクラスのclassを設定する。つまり上述のweb.xmlでclassnamesを使っているようなもの。
これは面倒なので、JerseyではApplicationを拡張(extends)したResourceConfigクラスが用意されている。

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        packages("mypackage.test;mypackage.test2");
        property(ServerProperties.PROVIDER_SCANNING_RECURSIVE, false);
    }
}

ResourceConfigだとパッケージ指定ができて、セミコロン区切りで複数指定もできる。
Jersey独自設定はResourceConfigを使うことになるので、Jersey使うのであればResourceConfigを最初から使っておけばいい。
PROVIDER_SCANNING_RECURSIVEは名前で分かる通り、上記web.xmlで指定していたjersey.config.server.provider.scanning.recursiveに該当する。

Application(or ResourceConfig)を使う場合、web.xmlはApplicationを読み込むだけの設定になる。

  <servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>javax.ws.rs.Application</param-name>
      <param-value>mypackage.test.MyApplication</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/webapi/*</url-pattern>
  </servlet-mapping>

公式ドキュメントにもわざわざ注釈があるが、param-nameに指定する値は「javax.ws.rs.Application」である。
作成したApplicationクラスの親クラスは「javax.ws.rs.core.Application」なのでcore 1つ分違う。ややこしい。
web.xmlをコピペした人はハマらない、注意深い人ほどハマる、たぶん。

Servlet2.5の方が意外と長くなったので、Servlet3.0の方は分けて書く。