RESTful 開発環境をJersey+Tomcat+Eclipseで構築する

# はじめに
RESTful API を実装しようと思い、調べましたが、古い情報が多かったので
少し新しい情報として書き留めておきます。
# 目的
* RESTful APIの開発環境を簡単に構築したい
* 開発ツールは Eclipse を使う
* JAX-RSの仕様の実装として Jersey を使う
* WEBサーバーは Tomcat を使う

J2EE のサーバーである必要は無く、Spring等のフレームワークも使いません。
とりあえずプロジェクトのテンプレートとして使える事を目的にしています。

# Eclipse で Jersey を使う
RESTful をサービスを提供する為に Java EE では JAX-RS という仕様があります。
その実装には　Apache CXF　等いくつかありますが、その中で Jersey(GlassFish版) を使うことにしました。
使用例が多く、バージョンの更新も活発に行われているのが理由です。
Eclipseは Pleiades を使います。

https://willbrains.jp/

# 作成するプロジェクトの概要
実行するとブラウザが立ち上がり以下の様なWEBページが表示されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/ea4126cb-d788-4113-e83b-ace2e5940ff1.jpeg" width="75%">

リンクをクリックして「Got it!」が表示されれば環境構築が正常に出来ていることが確認できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/49747dbe-e6b2-a82d-b36b-84d03827e668.jpeg" width="75%">

後は、サンプルプログラムを消して、自分のプログラムを書いていけば良いと思います。

## プロジェクトの作成
では、手順を解説していきます。
maven の **jersey-quickstart-webapp** アーティファクトを使います。
これは、Jersey の環境と簡単なサンプルを内包しています。

まずプロジェクトを新規で作ります。
ここでは「mavenプロジェクト」で作成します。
その中で、アーキタイプを選択します。アーキタイプはプロジェクトの雛型です。
沢山表示されるので、フィルターで、「glassfish.jersey」と入力すれば絞れます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/1d8ee443-42f1-3df8-b7dd-4fa2fec9f098.jpeg" width="75%">

「jersey-quickstart-webapp」を選択します。
最新バージョンしか表示されていませんが、チェックを外すと過去のバージョンも選択できます。

:::note info
後で詳しく述べますが、バージョンの選択は重要です。
少なくとも、ここで選択したのは Tomcat10 以上を使う前提です。
:::

入力画面に進みます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/b2456381-b8b0-7ff1-64b2-7189a02da56a.jpeg" width="75%">

| 入力項目 | 対応するEclipseの項目 |
|---------|----------------------|
|グループId |パッケージ名|
|アーティファクトId |プロジェクト名 |

という関係です。
名前は何でも良いですが、アーティファクトId は、Eclipse のプロジェクト名にもなるので、それを踏まえて付けて下さい。
「完了」でプロジェクトが作成されます。
※最後に、コンソールに「Y」を入力するのを忘れずに


## 実行してみる
「サーバーで実行」を選択し、Eclipseにインストール済の Tomcat を選択します。
ここでは Tomcat10 以上を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/8c9d7378-2de2-785f-1238-9386f3c41a59.jpeg" width="50%">

:::note warn
Jersey3.x以上は Tomcat9 以下では動きません。
:::

プロジェクトの概要で説明した画面が表示されれば完了です。

# 追加で設定しておきたい事

* Servlet3.0 以上に設定する
* Applicationサブクラスを追加する
* JSON を有効にする

## Servlet3.0 以上にする
pom.xml 中のコメントの記載通りに、既存の Artifact の記述を削除して、代わりにコメントを外して有効にしましょう。

```xml:pom.xml
<!-- artifactId>jersey-container-servlet-core</artifactId -->
<!-- use the following artifactId if you don't need servlet 2.x compatibility -->
<artifactId>jersey-container-servlet</artifactId>
```
今時、**servlet2.x** を使う事などあり得ません。
また、これを行わないと、**Applicationサブクラス** が働きません。

## Applicationサブクラスを追加

補足ですが、JAX-RSの仕様では
1. **Applicationサブクラス**
2. **リソースクラス**

の２つがあり、Applicationサブクラスがあれば web.xml は必要ありません。
Applicationサブクラスは<span>@</span>ApplicationPathアノテーションを使って基底の URI を決める事が出来ます。
例えば、MyApplication.java を src/main/java 配下のどこかに作成します。

```java:MyApplicatio.java
@ApplicationPath("/webapi")
public class MyApplication extends Application {
    public Set<Class<?>> getClasses() {
        Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(MyResource.class); // 作成したリソースクラス
        return s;
    }
}
```
もしくは Jersey 限定で良ければ、もう少し簡単に書けます。

```java:MyApplication.java
@ApplicationPath("/webapi")
public class MyApplication extends ResourceConfig {
    public MyApplication() {
        packages("MyPackage"); // 作成したパッケージ名
    }
}
```
多くの解説では作成したら不要だから web.xml は削除しろと書いてある場合が多いですが、**Servlet3.0 用に構築** しないと動きません。
web.xml を削除して、同様に動くか確認しましょう。

なお、web.xml とApplicationサブクラスを併用する事もできます。URI が重複しなければ問題ありません。

## JSON を有効にする
現在主流の RESTful API では、受け渡しの形式が JSON です。
デフォルトは、jettison が使われる設定ですが、jackson を使うのが一般的です。
maven で追加しましょう。

```xml:pom.xml
<!-- uncomment this to get JSON support
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-binding</artifactId>
</dependency>
-->
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<!-- kackson or jettison use regist -->
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->
```
従来は、さらに regist しないと有効になりませんでしたが、現状は自動でロードされるようです。
念のために記載しておきます。

```java:MyApplication.java
public class MyApplication extends ResourceConfig {
    public MyApplication() {
        packages("app.hoge"); // リソースクラスを含むパッケージ名
        register(JacksonFeature.class); // Jackson を有効にする
    }
}
```
サンプルソースを書き換えてテストします。
```java:MyResource.java
@GET
@Produces(MediaType.APPLICATION_JSON) // ("application/json")でも同じ
public HashMap<String,String> getIt() {
  	HashMap<String, String> map = new HashMap<>(); 
 	map.put("Got","it!");
    return map;
}
```
Java のクラスを解釈して変換してくれるので楽です。

以上で終わりですが、以降は、うまく動かなかった場合の対処を書いておきます。

# トラブルシューティング

最初は、いろいろトラブルが生じたので、記載しておきます。

* Eclipse のコンパイルエラー
* Tomcat のバージョンとの関係
* Tomcat9 で動かしたい場合

## Eclipse のコンパイルエラー
下が実際のエラーの例

![jersey5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3834038/a304c6e4-ece6-828b-8a32-0392e566f791.jpeg)

多くは、「コンソール」窓(ビュー)の横にある「問題」（もしくは「マーカー」と表示）のエラーの説明で解る事が多いです。

* プロジェクトに✖印が付いて他にエラー箇所が無い
* JSPに✖が付いている
* web.xmlに✖が付いている

### プロジェクトだけに✖印が付いている場合
プロジェクトを一度「クリーン」してみる。
「Java プロジェクト・ファセットのバージョンと一致しません。」のようなエラーなら、ターゲットとなる Tomcatの実行環境の Java のバージョンがコンパイルの Java のバージョンと一致していない。
プロジェクトの設定で同じにする必要があります。

### JSPに✖もしくは web.xmlの✖が付いている場合
Eclipse の検証機能が正しく働いていない可能性があります。
古い Eclipse を使っていたら最新版にしてみるのが良いです。(実際、2021版を2024年版にしたらエラーが消えた)
これらのエラーは無視しても実行できますが、気になるなら、Eclipse の「設定」「検証」で「JSP構文バリデターのビルド」を外す事で対処できるようです。

## Tomcat のバージョンとの関係

Tomcat10 以上が無い場合(Eclipseのバージョンが古い)は追加で Tomcat をインストールする事も出来ます。
その際 JREは次の表に従って下さい。

https://tomcat.apache.org/whichversion.html 

ここにあるように、Java11 以上で動かす必要があります。
ただ、Tomcat10 を Java11 で動かすと下記の問題が見られました。

JSPの画面は表示されるが、リンクをクリックすると、

```
重大: Web アプリケーション [/jersey40] のサーブレット [Jersey Web Application] の load() メソッドは例外を投げました。 [土 8月 10 18:04:20 JST 2024]
```
Tomcat10 を Java17 以上で動かすと問題ありませんでした。
**Jersey4.0 以上では、Tomcat10 は Java17 以上**で動かすのが無難です。

## Tomcat9 で動かしたい場合
どうしても、実環境が Tomcat9 しか用意出来ない場合、Jersey のバージョンは 2.x で動かすことになります。
この場合、最初のプロジェクトの新規作成で、アーティファクトは古いバージョンを選ぶ必要があります。
例えば、jersey 2.5 を選びます。
この場合もプロジェクトが正常にビルドできない場合があります。

### pom.xml でエラーになる場合
中で記述されている Maven のバージョンが原因です。動くバージョンに書き換える必要があります。例えば、

```xml:pom.xml
     <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.4.0</version>
     </plugin>
```

### Tomcat9で実行時にエラーが出る場合
Tomcat9 を Java11 以上で動かしていると起こります。
理由は **JAXBException** というライブラリが標準ライブラリから除外されたためのようで、
pom.xml で追加するか、Tomcat9 を Java8 で動かすかのどちらかで対処できます。

:::note info
前者でこのサンプルは動きますが、別のエラーがログに出ているので後者のほうが確実です。
:::

### MyResource.java でコンパイルエラーが出る場合
import **jakarta**.ws.rs.Get のように **jakarta** が使われるのは Tomcat10 以上を前提にしています。
Tomcat9 では **javax**.wa.rs.Get のように書き換えます。
Tomcat10 から J2EE 関係は **jakarta** に変更されている為です。
もともと Tomcat は J2EEサーバーではありませんが、Jersey は J2EE の一部の API を使っているのでこの変更が影響します。

# まとめ
トラブルを避ける為に Eclipse はなるべく最新版を使いましょう。
Jersey3.x 以上を使いたければ、Tomcat10 以上を使う必要があります。
案外、落とし穴がありますので、Jersey の基本的な設定を覚えておくと以降の開発が楽になります。