Jerseyに触ってみる

せっかくの夏休みなんで、JerseyでRESTfulなWebサービスとか作る練習とかしてみようといろいろ調べ中。

Webアプリケーションの作成

とりあえずは
Jersey, Jetty and Maven: HelloWorld

に従ってEclipse上でアプリケーションを作成。こういう時にpom.xmlを写せばそれだけで実行できるってのはやはり便利だ。maven様様。

GETメソッドでリソースを取得

RestTest

http://localhost:8080/jersey-helloworld-webapp/helloworld

へGETメソッドを使ってアクセスすると、URLの"helloworld"リソースに紐付けられたHelloWorldクラスのgetMessageメソッドが呼ばれ、その戻り値である文字列

"Hello World"

が戻る。まあ何の工夫もないけど、ここまでは良し。

よく考えると

上の例はGETメソッドだからいいけど、URIの"helloworld"リソースに対してDELETEメソッドとか使う場合を考えると破綻するよな。"helloworld"リソース(に紐付けられたHelloWorldクラス)を削除???インスタンスならともかくクラスを削除とかできないし。
URIのリソースはあくまでJavaの特定のインスタンスに対して紐付けるものであり、上記のようにクラスに対して紐付けてしまうのは微妙っぽい。

要は、あるメッセージ(インスタンス)を表すリソースとして、下記の例のようにメッセージID文字列を指定するようにしたい。

http://localhost:8080/message/${メッセージID文字列}

で、上記URIに対してPUTメソッドを実行するとメッセージの内容が上書きされ、DELETEメソッドを実行するとそのメッセージが削除されるようにする。

どうやってURIインスタンスを紐付ける?

こういう時は公式ページを見るに限る。
Jersey 1.3 User Guide - Example 2.2. Specifying URI path parameter
要は、URIパスにパラメータ(今回の場合、特定のインスタンスを示すID文字列)を含ませることで解決。

  • パスを指定する@Pathアノテーションの引数として、以下のように"{}"で囲んだ変数名を使用する。

@Path("/message/{id}")

例:

@Path("/message/{id}")
public class HelloWorld {

//中略

    @GET
    @Produces("text/plain")
    public String getMessage(@PathParam("id") String id ) {
        //hogehoge
    }

オリジナルのHelloWorldクラスを改変したソース全体はこんな感じ(元のサンプルからパッケージ名を変更したため、web.xmlでパッケージ名を指定している箇所を修正する必要があるので注意)。

package com.sample;
import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;
 
@Path("/message/{id}")
public class HelloWorld {
	
    private static Map<String, Message> messages = new HashMap<String, Message> ();

    @GET
    @Produces("text/plain")
    public String getMessage(@PathParam("id") String id ) {
        return messages.get(id)==null?null:messages.get(id).toString();
    }
    
    @PUT
    @Produces("text/plain")
    public String setMessage(@PathParam("id") String id, String text) {
    	Message message = messages.get(id);
    	if(message == null){
    		message = new Message(id);
    		messages.put(id, message);
    	}
    	message.setText(text);
    	return message.toString();
    }
    @DELETE
    @Produces("text/plain")
    public String delMessage(@PathParam("id") String id) {
    	Message message = messages.get(id);
    	messages.remove(id);
    	return message.toString() + " was deleted!";
    }
}

/**
 * メッセージを表すクラスです。インスタンスを作成するためには任意のidが必要。
 * */
class Message{
	
	private String id;
	private String text = null;
	public Message(String id){
		this.id = id;
	}
	
	public String getId(){
		return this.id;
	}
	
	public void setText(String text){
		this.text = text;
	}
	
	public String getText(){
		return this.text == null?"":this.text;
	}
	
	public String toString(){
		return "id: " + this.id + " text: " + this.text;
	}
}


このコードを元にwarファイルをビルドし、Webコンテナにデプロイする(細かいことはMavenが勝手にやってくれるけど)

下記のURIに対して、GET、PUT、DELETEメソッドでアクセスすると・・・

http://localhost:8080/jersey-helloworld-webapp/message/hoge

一回目に上記のURIに対してGETメソッドを実行
→id="hoge"であるメッセージが存在しないため、レスポンスコード204が戻る(本来、404を戻すべきだが・・・・とりあえず)

データとして"ああああ"と入れてPUTメソッドを実行。
→id="hoge"のMessageインスタンスが作成される。
戻り値として、新規作成されたMessageインスタンスの文字列表現"id: hoge text: ああああ"が戻る。

再度、上記のURIに対してGETメソッドを実行
→id="hoge"のMessageインスタンスが、Mapから取得される。
戻り値として、Messageインスタンスの文字列表現"id: hoge text: ああああ"が戻る。

上記のURIに対してDELETEメソッドを実行
→id="hoge"のMessageインスタンスが、Mapから削除される。

再度、上記のURIに対してGETメソッドを実行
→id="hoge"であるメッセージが存在しないため、レスポンスコード204が戻る(本来、404を戻すべきだが・・・・とりあえず)