前回までは、GETで取得、POSTで新規作成、PUTで更新だったので、今回はDELETEで削除をサポートしたい。
DELETE /message/10 - 10番のメッセージを削除する。
DELETE /resources/message/10 HTTP/1.1 Host: server:8080
これに対応するサーバー側のJavaのプログラムは、以下の様に記述した。
@Path("message")
public class MessageResource {
...
@DELETE
@Path("{id}")
public void deleteMessage(@PathParam("id") long id) {
this.dao.delete(id);
}
...
}
テストとして、curlコマンドでテストしてみる。
コマンド実行
# curl -X DELETE http.../message/7
7番のメッセージが削除される。
やはり、ここまでは問題無い。
しかし、やはり、HTML + Prototype.jsによるWebインターフェイスを作成し、Ajax.Requestクラスを使用してDELETEを試みたが、なぜかDELETEされずにPOSTされる。(追記 2010/3/7: PUT同様、ブラウザがDELETEをサポートしていないせいか?)
...
new Ajax.Request( resourceUrl, {
asynchronous: false,
method: "delete", /* POSTされてしまう */
onSuccess: function( transport ) { alert( "delete success" ); },
});
...
これも、前回と同じアプローチで対応する。
QueryParameterの_methodとしてdeleteを指定し、Java側でそれをハンドリングする。
いろいろ面倒なので、メソッドはgetとする。
JavaScriptでの送信先:
...
new Ajax.Request( ".../message?_method=delete", {
asynchronous: false,
method: "get",
onSuccess: function( transport ) { alert( "update success" ); },
});
...
Java側での処理
@Path("message")
public class MessageResource {
@QueryParam("_method")
private String _method;
...
@GET
@Path("{id}")
public Message getMessage(@PathParam("id") long id) {
/*追加分*/
if ("DELETE".equalsIgnoreCase(_method)) {
this.deleteMessage(id); /*削除へルーティング*/
return null;
}
/*追加分終わり*/
return this.dao.findById(id);
}
...
}
とりあえず、うまく削除される。
ここまでで、追加、更新、削除、検索が可能となった。
| 処理 | HTTP Method | URL | 内容 |
|---|---|---|---|
| 検索 | GET | /message | 全件検索 |
| GET | /message/{id} | {id}で指定されたメッセージを検索 | |
| 追加 | POST | /message | 新規メッセージの追加 |
| 更新 | PUT | /message | POSTされるデータ内に指定されたIDのメッセージを更新 |
| POST | /message?_method=put | (Prototype.js用) | |
| PUT | /message/{id} | {id}で指定されたメッセージをPOSTされたメッセージで更新 | |
| POST | /message/{id}?_method=put | (Prototype.js用) | |
| 削除 | DELETE | /message/{id} | {id}で指定されたメッセージを削除 |
| GET | /message/{id}?_method=delete | (Prototype.js用) |
この後、この機能を呼び出すユーザーインターフェイスを整備することになるだろう。
また、アクセス制御に関しても考慮する必要があるだろう。誰でも追加、更新、削除できてしまうのは現実的ではない。
前回までは、GETで取得、POSTで新規作成、だったので、今回はPUTで更新をサポートしたい。
PUT /message - 特定のメッセージを更新する。
PUT /message/10 - 10番のメッセージを更新する。
2つのやり方を考える。
一つは、更新対象のIDをXML内に指定する方法。
例えば、IDが5番のデータを更新する場合、以下のリクエストを送る。
PUT /resources/message HTTP/1.1
Host: server:8080
Content-Type: application/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
<id>5</id>
<comment>テストデータです。</comment>
<name>yuki yamazaki</name>
</message>
もう一つは、更新対象のIDをURLとして指定する方法。
例えば、IDが5番のデータを更新する場合、以下のリクエストを送る。XML内のIDは無視する。
PUT /resources/message/5 HTTP/1.1
Host: server:8080
Content-Type: application/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
<comment>テストデータです。</comment>
<name>yuki yamazaki</name>
</message>
これに対応するサーバー側のJavaのプログラムは、以下の様に記述した。
RESTでのGETは「リソースの取得」だった。
次は、POSTを使用した「リソースの新規作成」を実装してみたいと思う。
新規に作成したいデータを次のようなHTTPで送信すればよい。
POST /resources/message HTTP/1.1
Host: server:8080
Accept: text/javascript, text/html, application/xml, text/xml, */*
Content-Type: application/xml; charset=UTF-8
<message>
<comment>テストデータです。</comment>
<name>yuki yamazaki</name>
</message>
サーバー側のJAX-RSで受ける側を作成するには、次のコードとなる。
@Path("message")
public class MessageResource {
//永続化の仕組み(RESTには直接関係ない)
private MemoryDAO dao;
public MessageResource() {
this.dao = MemoryDAO.getInstance();
}
....
@POST
public Message createMessage( Message message ) {
return this.dao.create( message );
}
}
非常に簡単である。
POSTの意味づけは、つまりPOSTは「新規作成である」という対応はメソッド内に処理によって行われる。いかようにも作れるが、RESTのデザインに合わせて実装すればよい。
サーバー側よりもむしろテストデータの送り方に手間取った。
一つは、Linuxで使用できるcurlコマンドラインを使用した。
JavaEE6から、JAX-RPCを経た、JAX-WSとJAX-RSが使用できる。
JAX-RS(Java API for RESTful Web services)はJSR311として標準化されている。前回調べたJAXB(JSR222) と JAX-RSのコンビネーションは美しい。
REST(Representational State Transfer)はHTTPを使用して、リソース操作を行うための(極めて可搬的であり庶民的に感じる)デザインの1種だが、非常に直感的に理解しやすい。RESTの厳密な定義は、いろいろ議論できるようだが、まずは、実用上使いやすくコンセプトを共有しやすい状態を維持するのが重要ではないかと思う。つまり、ある程度自由で直感的な発想から、よりよいやり方を共有し合うという態度で接したい。
例えばメッセージ(message)をリソースとすると・・・
GET /message/all - メッセージの一覧を取得する。
GET /message/10 - 10番のメッセージを取得する。
GET /message/list?count=5 - 最近のメッセージ、5個のリストを取得する。
POST /message - メッセージを新規作成する。
PUT /message - 特定のメッセージを更新する。
PUT /message/10 - 10番のメッセージを更新する。
DELETE /message/10 - 10番のメッセージを削除する。
と言うような操作をURLにマップしておく。
JavaEE6で標準化されたアノテーションは、次のものがありPOJOで使用できる。
javax.ws.rs.Path
jaxax.ws.rs.GET
jaxax.ws.rs.POST
javax.ws.rs.PUT
javax.ws.rs.DELETE
javax.ws.rs.PathParam
etc...
全メッセージを取得する場合、次のようなPOJOとなる。
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("message")
public class MessageResource {
private MemoryDAO dao;
public MessageResource() {
this.dao = MemoryDAO.getInstance();
}
@GET
@Path("all")
public List<Message> listAllMessages() {
return this.dao.findAll();
}
}
JavaSE6からJAXB2.0が標準で使えるようである。
javax.xml.bind パッケージとして含まれている。
POJOからXMLをマーシャリング(marshaling - 整列)、その逆をアンマーシャリング(unmarshalling 無秩序化)という。
マーシャリング(POJO->XML)の例を上げる。
クラスにXmlRootElementをアノテーションする。
import java.util.Calendar;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Message {
private long id;
private Date postDate;
private String name;
private String comment;
//constructor,getter,setter,etc...
}
これを、XMLにマーシャリングするには、次のコードを使用する。
import java.util.Date;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Test {
public static void main( String[] args ) throws Exception {
//アノテーションされたPOJOをインスタンス化
Message message = new Message( 1, new Date(), "yamazaki", "Hello World" );
//マーシャラーを取得。
Marshaller marshaller = JAXBContext.newInstance( Message.class ).createMarshaller();
//標準出力へマーシャリング
marshaller.marshal( message, System.out );
}
}
結果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><message><comment>Hello World</comment><id>1</id><name>yamazaki</name><postDate>2010-02-18T00:46:47.366+09:00</postDate></message>
一行でコンパクトに表示される。改行を入れたい場合は、マーシャラーに次のプロパティをセットする。
...
//マーシャラーを取得。
Marshaller marshaller = JAXBContext.newInstance( Message.class ).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
...
すると、見やすくなる。
結果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
<comment>Hello World</comment>
<id>1</id>
<name>yamazaki</name>
<postDate>2010-02-18T00:52:24.009+09:00</postDate>
</message>
素晴らしい。
妻と娘たちから、バレンタインデーということで贈り物をもらった。
手作りのチョコクッキーで、ジャムが添えてあった。
すごくおいしくいただいたし、ラッピングも工夫が凝らしてあって、すごく美しかった。
私たちの身の回りには、大量生産品、既製品が溢れているが、手作りのものがそれに勝ることがあるんだという、当たり前のことに、改めて気づかされる。
こういった、当たり前の事実が、元気の元になる。
私たちが、元気になるには、そこから発想すべきなのだと思う。
人を元気にするようなものを作るには、どうしたらよいのか?
それは、買ってきて済ませることができるほど、単純ではないと思う。
そこが、きっと大事なことなんだと思う。
現在、コンピューターで映像を視聴する場合、主にAdobe Flash、Microsoft WMV、Apple Quick Time、Oggなどが使われているのだろう。
インターネットではYoutubeで使用されているFlashが、Linuxを含むすべてのプラットフォームで視聴でき守備範囲が広い。しかし、FlashはApple(Macromedia)のプロプライエタリな技術製品である。かなり広範囲で使用されているが、例えばAppleのiPhone, iPadのSafariがそれをサポートしないなど、ベンダー間での軋轢がちらほら見える。
AdobeのCTOがFlash擁護 「HTML5があればFlashは不要」論に反論(ITMedia):
http://www.itmedia.co.jp/news/articles/1002/03/news057.html
FlashはWindowsと同様、非常に多くのユーザーに使用されているがプロプライエタリでありオープンではないため「スタンダード」とは呼ばれず「よく使われている技術」の一つである。
私がWindowsを使う状況の一つは、映画や動画、音楽を視聴するためだが、それは、配布されているコンテンツのフォーマットがWMVやQuick TimeやMP3だからであり、それ以外のコンピューティングは主にLinuxを使用する。Linuxはメディアのハンドリングが弱いが、それはコンテンツのフォーマットがプロプライエタリであり、Linuxのコミュニティは基本的にそういった独占的な技術を使わない。
最近、Linuxを使いながら、机の脇にiPhoneを置いて作業をするようになったが、音楽を同期できないという致命的な点(つまりiTunesが必要で、MacかWindowsが必要だということだが)を除くと、案外心地よい環境である。最近発表されたiPadはどのような環境と経験を提供するのは定かではないが、おもしろい状況が生まれるような気がする。つまり、現在、あらゆる「知的活動」や「欲望」をすべて1つのコンピューターに詰め込もうとしているが、これをいくつかに分けたようなユーザー経験のデザインはおもしろそうなのである。これは、また、別の機会に考えたいと思う。
クラウドコンピューティングという言葉が流通している。
これを、単なるバズワードとして片付けたり、よくできたキャッチコピーとして感心して見せることもできるが、いずれにしても、その言葉がリアリティをもってイメージできる状態にはなっているのは間違いないと思う。
私にとって、この言葉の技術的な裏づけを調べるのはあまり面白い思索ではない。むしろ、この言葉が、ある程度説得力を持ち、現実味を帯びている現象のほうが興味がある。
クラウドのイメージは、「電波」や「放送」に似ている。つまり、テレビやラジオ、および新聞などのメディアを置き換えるかのような印象を与える。テレビをコンピューティング環境に置き換え可能かどうかをイメージしてみることは面白い思考シミュレーションだ。
たとえば、テレビは、スイッチを入れるとたくさんの情報を得ることができるし、楽しめる。このメディアはスポンサーシップに支えらながら、少なくとも数十年、私たちの情報源となり、娯楽を提供してきてくれた。
イギリスでは、受信料を支払わなければ、テレビを見れないらしいのだが、日本の民放は「無料」で、特に面倒な操作を必要とせず、ニュースや映画、バラエティー番組にアクセスでき、日常的にそれを利用してきた。テレビはまさにクラウド的だといえなくも無い。
いうまでも無く、現在のコンピューターの機能性と処理能力はテレビを上回っているし、拡張性が高いし、何より重要な機能は、「コンテンツを作成し、開発し、発信できる」点だ。この機能は個人の創造性を発揮でき、きわめて大きな利点だ。
考えてみるとテレビと放送のシステムは誰でも使える良くできたテクノロジーだと思うし、優れた番組もたくさんある。が、テレビがいかに高機能になったとしても、この「コンテンツ作成能力」や「プログラム開発実行機能」をもてなければ、双方向性は生まれないだろう。そして、一方向の少数の組織が発信する情報の流れだけでは、もはや私たちは満足できなくなってきていると思う。人間は良くも悪くも欲望する生き物だ。そして、人間は進化し変化でき、状況に適応する。
そして、もし、クラウドがクラウドとして機能するためには、こうした端末から発信させるコンテンツを受け入れることのできる能力が求められ、必然的にオープンであり、カスタマイザブルであり、必然的にスケーラブルで無くてはならない。
インターネット+コンピューターテクノロジーが、電波+テレビを包含するには、まだ時間がかかるとは思うが、おそらくいつかそうなると思う。
テレビのように簡単に情報にアクセスできるコンピューター端末やアプリケーション、および、見る価値のあるコンテンツが増えることで、電波に変わるクラウドが実現することになると思う。
そのとき、私たちは今より充実した生活が営めるようになるだろうか?
双方向性からくるオープン性を損なわない限り、楽観的だ。
なんと、青森県のねぶたが、原宿、表参道をねり歩いたとのことである。
http://www.asahi.com/national/update/0123/TKY201001230260.html
http://www.47news.jp/CN/201001/CN2010012301000580.html
http://mainichi.jp/area/tokyo/news/20100110ddlk13040140000c.html
今年開通予定の青森までの新幹線開通を記念してとのこと。
偶然見かけた人などは、さぞ、驚いたのではないだろうか?
日本でもっとも「おしゃれ」な表参道を、日本でもっとも田舎でうまれた「じょっぱりの美意識」が堂々とねり歩く様は、さぞ、痛快であったろう。