はじめに
前回までにKubernetes上でWildFlyを動かし簡単なRESTサービスを動かしてみた。今回はRESTの理解を深めるために、情報の登録と削除(取り出し)を行うRESTサービスを作成してみる。
コインロッカーのように物(情報)をロッカーに預けて鍵(ID)を受け取り、取り出すときはその鍵(ID)を提示し中身(情報)を取り出す、以下のようなサービスを作成する。

プログラミング
概要
このRESTサービスは以下の3つのクラスファイルから構成します。
- Locker.java 情報を表すエンティティ。鍵もエンティティに持つ。
- LockerManager.java 情報をデータベースに保存したり削除したり操作する。
- RestApi.java 利用者からのPOSTとDELETEを受け付ける。

ちなみに、Locker_(アンダーバー).javaはeclipseにより自動で作成されるファイルで、JPAメタモデルと呼ばれる。何が嬉しいかというと、タイプセーフになる。Lockerクラス内で定義した変数名がクエリー上も文字列としてではなく、”型”として認識されるので、タイプミスなどによる型の不一致を容易に見つけられるようになる、という点である。
Locker.java
ロッカーで取り扱う情報の中身の定義をこのクラスファイルで行う。
アノテーションを使うことによって、この一つのクラスファイルが3役となり、POJOとデータベースとJSONの挙動や変数名などを制御できる。JSON Bindingのアノテーションについてはこちらを参照。
@Entity
public class Locker {
@Id // 主キー
private String id; // ロッカーの鍵
@NotNull
private String familyName; // 姓、名、アドレスなどなど(略)
@NotNull
@JsonbDateFormat(value = "yyyy/MM/dd HH:mm:ss") // JSONで返す日時の書式を指定できる
private LocalDateTime expirationDate; // 有効期限
idの生成はapache.commons.lang3を利用し、コンストラクタ時に以下のように英数文字をランダムに発生させている。
public Locker(String familyName, ....) {
this.familyName = familyName;
this.id = RandomStringUtils.randomAlphanumeric( xxx );
}
LockerManager.java
POST時には情報をデータベースに保存し、DELETE時にはデータベースから情報を削除します。
@Transactional(Transactional.TxType.REQUIRES_NEW)
public Locker post(String familyName, …) {
Locker entity = null;
try {
entity = new Locker(familyName, ...); // 情報と鍵の生成
repo.persist(entity); // DBに保存
} catch (Exception e) {
entity = null;
}
return entity;
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
public Locker delete(String id) {
Optional<Locker> entity = repo.getEntity(Locker.class, id);
if (entity.isPresent()) {
repo.remove(entity.get()); // DBから削除
return entity.get();
}
return null;
}
RestApi.java
まず情報を預けるPOSTの方です。情報はBase64でコード化されてもらうはずなので、デコードしてからエンティティを新規作成します。作成、DBに保存できたら、レスポンスボディにいれてあげてJSONを返してあげます。
@POST
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response post(@FormParam("familyName") ...) {
Locker entity = null;
try {
String fname = new String(Base64.getDecoder().decode(familyName), StandardCharsets.UTF_8);
entity = mgr.post(fname, ...); // DBに保存
} catch (Exception e) {
}
if (entity == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
} else {
return Response.status(Response.Status.CREATED).entity(JSONB.toJson(entity)).build();
}
}
次に送られた鍵に合う情報を取得するRESTです。DBから情報を取得したらDBからは情報を削除します。
@DELETE
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response delete(@HeaderParam("Authorization") String authorization) {
// ロッカーの鍵を取得
String id = authorization.substring("Bearer".length()).trim();
// ロッカーから情報を取り出し削除する
Locker entity = mgr.delete(id);
if (entity == null) {
return Response.status(Response.Status.NOT_FOUND).build();
} else {
return Response.ok(JSONB.toJson(entity)).build();
}
}
有効期限の情報を加えて判断したり、EJBタイマーで不要レコードを削除したり、RESTのフィルタクラスでリクエストを制御したりなど手を加えたい箇所はありますが、RESTを使った情報の操作とデータベースとの連携が少し理解が深まりました。