4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java】Eclipse2025を使ってJakartaEEで簡単なJAX-RSプロジェクトを作成してみる

4
Posted at

今回は、Jakarta EE 10 + Maven + JAX-RS(Jersey) を使って
とてもシンプルな RESTful CRUD(GET / POST / PUT / DELETE) サンプルを作る手順を
記載します。

イメージ

簡単ですがこんなサンプル画面を作っていきます。
image.png

image.png

全体構成

JakartaEERestful
 └─ src
    └─ main
       ├─ java
       │  └─ com.example
       │     ├─ config
       │     │  └─ RestApplication.java
       │     ├─ model
       │     │  └─ Item.java
       │     ├─ resource
       │     │  └─ ItemResource.java
       │     └─ service
       │        └─ ItemService.java
       └─ webapp
          └─ WEB-INF
             └─ web.xml(※省略可)

JakartaEE公式サイト

JakartaEE公式サイトでプロジェクトを作成していきます。
JakartaEEのプロジェクト.png

ライブラリのインストール

Jakarta Restful WS API

Jersey Conatiner Servlet

Jersey Inject HK2

Jersey Media JSON Jackson

Parayaのインストール

image.png

image.png

image.png
https://payara.fish/downloads/payara-platform-community-edition/

pom.xmlの全体コードは下記です。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>JakartaEERestful</artifactId>
    <version>0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>JakartaEERestful</name>
    <description>
        This is a very simple Jakarta EE application generated by the official Eclipse Starter.
    </description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.report.sourceEncoding>UTF-8</project.report.sourceEncoding>
        <maven.compiler.release>21</maven.compiler.release>
        <jakartaee-api.version>10.0.0</jakartaee-api.version>
        <compiler-plugin.version>3.13.0</compiler-plugin.version>
        <war-plugin.version>3.4.0</war-plugin.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>${jakartaee-api.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/jakarta.ws.rs/jakarta.ws.rs-api -->
		<dependency>
		    <groupId>jakarta.ws.rs</groupId>
		    <artifactId>jakarta.ws.rs-api</artifactId>
		    <version>4.0.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-servlet -->
		<dependency>
		    <groupId>org.glassfish.jersey.containers</groupId>
		    <artifactId>jersey-container-servlet</artifactId>
		    <version>4.0.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.inject/jersey-hk2 -->
		<dependency>
		    <groupId>org.glassfish.jersey.inject</groupId>
		    <artifactId>jersey-hk2</artifactId>
		    <version>4.0.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->
		<dependency>
		    <groupId>org.glassfish.jersey.media</groupId>
		    <artifactId>jersey-media-json-jackson</artifactId>
		    <version>4.0.0</version>
		</dependency>
    </dependencies>

    <build>
        <finalName>JakartaEERestful</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler-plugin.version}</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>${war-plugin.version}</version>
            </plugin>

        </plugins>
    </build>
</project>

GlassFishのインストール

2. JAX-RS アプリケーション設定

RestApplication.java

RestApplication.java
package com.example.config;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/api")
public class RestApplication extends Application {
    // 設定は不要(自動スキャン)
}

👉 これで

http://localhost:8080/JakartaEERestful/api/...

が REST のベースパスになります。

  1. エンティティ(DTO)クラス
Item.java
package com.example.model;

public class Item {

    private Long id;
    private String name;

    public Item() {
    }

    public Item(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

JacksonJSON変換)は getter/setter があればOK
■アノテーションは不要です。

4. サービス層(擬似DB)

ItemService.java
package com.example.service;

import com.example.model.Item;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

public class ItemService {

    private static final Map<Long, Item> store = new HashMap<>();
    private static final AtomicLong idGenerator = new AtomicLong(1);

    public List<Item> findAll() {
        return new ArrayList<>(store.values());
    }

    public Item findById(Long id) {
        return store.get(id);
    }

    public Item create(Item item) {
        Long id = idGenerator.getAndIncrement();
        item.setId(id);
        store.put(id, item);
        return item;
    }

    public Item update(Long id, Item item) {
        if (!store.containsKey(id)) {
            return null;
        }
        item.setId(id);
        store.put(id, item);
        return item;
    }

    public boolean delete(Long id) {
        return store.remove(id) != null;
    }
}

5. REST リソース(CRUD 本体)

ItemResource.java
package com.example.resource;

import com.example.model.Item;
import com.example.service.ItemService;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import java.util.List;

@Path("/items")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ItemResource {

    private final ItemService service = new ItemService();

    // GET /items
    @GET
    public List<Item> getAll() {
        return service.findAll();
    }

    // GET /items/{id}
    @GET
    @Path("/{id}")
    public Response getById(@PathParam("id") Long id) {
        Item item = service.findById(id);
        if (item == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(item).build();
    }

    // POST /items
    @POST
    public Response create(Item item) {
        Item created = service.create(item);
        return Response.status(Response.Status.CREATED)
                       .entity(created)
                       .build();
    }

    // PUT /items/{id}
    @PUT
    @Path("/{id}")
    public Response update(@PathParam("id") Long id, Item item) {
        Item updated = service.update(id, item);
        if (updated == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(updated).build();
    }

    // DELETE /items/{id}
    @DELETE
    @Path("/{id}")
    public Response delete(@PathParam("id") Long id) {
        boolean deleted = service.delete(id);
        if (!deleted) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.noContent().build();
    }
}

① 依存関係について
Jakarta EE Web プロファイルを使う場合:
GlassFish / Payara を使うなら Jersey は同梱する。

6. HTMLファイル作成

Jakarta EE の場合、HTML はここに置くのが基本です。

src/main/webapp/index.html

アクセスURL:

http://localhost:8080/JakartaEERestful/index.html
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>JAX-RS CRUD Sample</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 40px;
        }
        table {
            border-collapse: collapse;
            width: 400px;
        }
        th, td {
            border: 1px solid #ccc;
            padding: 6px;
            text-align: center;
        }
        th {
            background-color: #f0f0f0;
        }
        input {
            margin: 4px;
        }
        button {
            margin: 4px;
        }
    </style>
</head>
<body>

<h1>Item CRUD (JAX-RS)</h1>

<h2>新規作成 / 更新</h2>
ID(更新時のみ):
<input type="number" id="itemId">
<br>
名前:
<input type="text" id="itemName">
<br>
<button onclick="createItem()">作成 (POST)</button>
<button onclick="updateItem()">更新 (PUT)</button>

<h2>一覧</h2>
<button onclick="loadItems()">再読み込み (GET)</button>

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody id="itemTableBody"></tbody>
</table>

<script>
const API_BASE = "/JakartaEERestful/api/items";

// GET
async function loadItems() {
    const response = await fetch(API_BASE);
    const items = await response.json();

    const tbody = document.getElementById("itemTableBody");
    tbody.innerHTML = "";

    items.forEach(item => {
        const tr = document.createElement("tr");

        tr.innerHTML = `
            <td>${item.id}</td>
            <td>${item.name}</td>
            <td>
                <button onclick="editItem(${item.id}, '${item.name}')">編集</button>
                <button onclick="deleteItem(${item.id})">削除</button>
            </td>
        `;
        tbody.appendChild(tr);
    });
}

// POST
async function createItem() {
    const name = document.getElementById("itemName").value;

    await fetch(API_BASE, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name })
    });

    clearForm();
    loadItems();
}

// PUT
async function updateItem() {
    const id = document.getElementById("itemId").value;
    const name = document.getElementById("itemName").value;

    if (!id) {
        alert("IDを入力してください");
        return;
    }

    await fetch(`${API_BASE}/${id}`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name })
    });

    clearForm();
    loadItems();
}

// DELETE
async function deleteItem(id) {
    await fetch(`${API_BASE}/${id}`, {
        method: "DELETE"
    });

    loadItems();
}

// フォームに反映
function editItem(id, name) {
    document.getElementById("itemId").value = id;
    document.getElementById("itemName").value = name;
}

// フォームクリア
function clearForm() {
    document.getElementById("itemId").value = "";
    document.getElementById("itemName").value = "";
}

// 初期表示
loadItems();
</script>

</body>
</html>

warファイルを作成

コンソールコマンド画面で下記のコマンドを入力します。

mvn clean package

正常終了すれば target/ フォルダJakartaEERestful.war が作られるはずです。
image.png

Payaraを起動してwarファイルをデプロイ

Admin Console から WAR をデプロイする手順

1.Admin Console にログイン

ブラウザで http://localhost:4848 を開きます。
ユーザー名とパスワードを求められた場合は、Payara domain 作成時に設定したものを入力します。
(例)

項目
ユーザ名 admin
パスワード なし

2.Applications メニューを開く
左側のメニューからApplicationsをクリックします。
image.png

右側に「Installed Applications」のリストが表示されます👇
image.png

Deploy ボタンを押します。
image.png

新しい画面(Deploy Application/Module)が開きます
image.png

WAR ファイルを選択
・「Packaged File to be Uploaded」欄で Choose File / Browse ボタンを押します。
Maven で生成したtarget/JakartaEERestful.warを選択
・デプロイするアプリケーション名の確認します。
・「Application Name」欄に自動で JakartaEERestful と入っているとおもいます。

image.png

OKを押します。
image.png

しばらく待つとデプロイが完了します。

デプロイ完了の確認

「Applications」リストに JakartaEERestful が追加されているのを確認します。
image.png

デプロイ後の動作確認

下記のURLにアクセスします。

http://localhost:8080/JakartaEERestful/

下記の画面が表示されているかと思います。
image.png

サイト

JAX-RSで高性能RESTful APIを作る!実践的な実装方法と9つのベストプラクティス

Jersey

トラブルシューティング

指定されたパスが見つかりません。

Payara[domain1]を削除して実行したら下記のエラーが出た。 
Unable to start server due following issues: java.io.FileNotFoundException: 
C:\Payara\payara6\glassfish\domains\domain1\config\domain.xml (指定されたパスが見つかりません。)

これは Payara のサーバドメインdomain1を削除したため、サーバの設定ファイルが無くなった ことを意味しています。
Payara(GlassFish 由来)は、サーバドメインごとに domain.xml という設定ファイルを持っています。これが無いとサーバは起動できません。

どう直すか
1️⃣ 新しいドメインを作成する

コマンドラインから作るのが安全です。

cd C:\Payara\payara6\bin
asadmin create-domain --adminport 4848 <ドメイン名>

2️⃣ サーバを起動

新しいドメインを作ったら:

asadmin start-domain <ドメイン名>

3️⃣ IDE での再設定

NetBeans/Eclipse でサーバ設定が消えている可能性があります

サーバ一覧に新しい domain1 を追加して、プロジェクトのデプロイ先として指定してください

ドメインの削除に失敗

CLI130: Could not create domain, domain1 Command create-domain failed.

原因と対策
1️⃣ 既存の domain1 がまだ残っている

Payara は同名のドメインを作れません。

なので手動で残っているドメインを削除する必要があります。

削除後、下記のコマンドを実行します。

asadmin create-domain --adminport 4848 <ドメイン名>
4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?