Web APIの次世代標準プロトコル「Atom Publishing Protocol」

第4回Atom Publishing Protocolを試す

第4回目は、より理解を深めていただけるよう、読者の皆様にもAtom Publishing Protocol(以下、AtomPub)を簡単に試していただけるツールや実装方法を幾つかご紹介します。

ツールの紹介(UNIXコマンドラインcURL)

最も簡単にAtomPubを試していただける手段の一つとして、UNIXコマンドラインツールである「cURL」⁠client for URLsの略でsee URL(シー・ユーアールエル)と発音します)を紹介します。このコマンドは、サーバから、あるいはサーバへデータ伝送を行うものです。対応するプロトコルはHTTP, HTTPS, FTP, TELNET, LDAP, FILE…と、盛りだくさんですが、AtomPubで利用する機能はHTTP(HTTPS)のみです。

cURLの使い方簡単にを以下に記述します。

[Usage]:curl [otions...] <url>
表1 cURL options
options説明
-X <command>HTTPメソッドの設定
例:-X POST)
--data-binary <data>POST,PUTするバイナリデータの設定
例:--data-binary @ntt.jpg[1]
-HHTTPヘッダーの設定
例:-H Content-Type:image/jpeg

※1:ファイル名の前に"@"を付けること

cURLによるサービス文書の取得

それでは、実際にcURLを利用してみましょう。AtomPubの最初のステップは、サーバがどのようなコレクションを持っているか知るためにサービス文書を得ることです。具体的にはAtomPub対応のサーバは、クライアントにとっての最初の接続点であるサービス文書URIを公開していますので、そのURIに対してGETします。

表2 サービス文書URI
サイト名サービス文書URI備考
APP Test Sitehttp://bitworking.org/projects/
apptestsite/app.cgi/service/
;service_document
【作者】Joe Gregorio(Google)
【詳細】http://bitworking.org/
projects/apptestsite/
Perl Atompub test sitehttp://teahut.sakura.ne.jp:3000/
service
【作者】井上 武(NTT 未来ねっと研究所)
【詳細】http://teahut.sakura.ne.jp/
b/2007-07-26-1.html
Mixi Station API足跡:http://mixi.jp/atom/tracks
新着:http://mixi.jp/atom/notify
フォト:http://photo.mixi.jp/atom
【作者】⁠ミクシィ)
【注意】WSSE認証が必要

今回は接続先をAtomPubの提唱者であり、RFCの著者でもあるJoe GregorioさんのAPP Test Siteとし、cURLで接続してみます。

[dsakano@test $ curl -x GET "http://bitworking.org/projects/apptestsite/app.cgi/service/;serv
ice_document"
<?xml version="1.0" encoding="utf-8"?>
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
  <workspace>
    <atom:title>AtomPub Test Site</atom:title>
/*中略*/
    <collection href="media/">
      <atom:title>media</atom:title>
      <accept>*/*</accept>
      <categories fixed="yes" />
    </collection>
/*中略*/
  </workspace>
</service>

レスポンスとして返ってきたサービス文書を見ると、以下のことがわかります。

  • "AtomPub Test Site" というワークスペースがあること
  • "AtomPub Test Site"の中に"media"というコレクションがあること
  • "media"のコレクションURIはhttp://bitworking.org/projects/apptestsite/app.cgi/service/mediaであること
  • "media"に対するPOSTで許可されているのは"*/*"つまり、全てのファイル形式であること

cURLによるコレクションに新メンバーを追加

どのようなコレクションがあるかわかったところで、次は"media"コレクションに新しいメンバーを追加してみましょう。これはコレクションURIに対してPOSTすることで実現します。以下の例ではクライアントからサーバへfeed-red.jpgファイルをPOSTで送っています。サーバ側で正しく処理されてコレクションに新メンバーが追加されると以下の例のようにAtomエントリがレスポンスとして返ってきます。

[dsakano@test$  curl -H Content-Type:image/jpeg --data-binary @feed-red.jpg -X POST "http://bitworking.org/projects/apptestsite/app.cgi/service/media" 
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app">
  <title />
  <link href="http://bitworking.org/news/414/" />
  <id>http://bitworking.org/news/414/</id>
  /*中略*/
  <link href="414/" rel="edit" />
  <link href="414/;media" rel="edit-media" />
  <content src="http://bitworking.org/images/dev/414-.jpe" type="image/jpeg" />
</entry>

cURLによるメンバーの更新、削除

このAtomエントリからは次の情報が得られます。

上記二つのURIを利用することでこのメンバーを操作することができます。例えば、Atomエントリを更新したいとき(写真のタイトルやコメントを更新等)はエントリーリソースに対してPUTします。先ほどPOSTした画像そのものを更新したいときは、メディアリソースに対してPUTします。作成したメンバーをコレクションから削除したいときは、エントリーリソースに対してDELETEします。実際、消されているかどうかはコレクションURIをGETして視認するとよいでしょう。

このように、cURLを利用すれば非常に簡単にAtomPubを試すことができます。これはAtomPubの設計思想であるRESTがHTTP通信を利用するという非常に簡単かつ一般的である仕組みであるために、既存のツールを使えば簡単に試すことできるのです。

AtomPubクライアント実装

前述のとおり、RESTはステートフルなHTTP通信という簡単な仕組みでできています。そのために多数のプログラミング言語において標準ライブラリだけでAtomPubのクライアントを簡単に実装することが可能です。とはいうものの、そうでない言語も幾つかあります。本節では実装にあたり、まずAtomPubの実装要件からはじめたいと思います。

AtomPubの実装要件

AtomPubはHTTPメソッドのGET、POST、PUT、DELETEを利用して、データのCRUDを実現しています。また、認証等の付加情報はHTTPのヘッダーに埋め込みます。このような点からAtomPubが実装要件は以下の3つと考えられます。

  1. HTTP(HTTPS)に対応していること
  2. HTTP(HTTPS)のヘッダーを編集できること
  3. HTTP(HTTPS)メソッドのうち、GET、POST、PUT、DELETEに対応していること

これらを満たすものはPerl、Ruby、ASP.NET、JavaScript and DHTML、Java(J2SE, J2EE)、…とあげればキリがありません。RESTはそれほど汎用的な仕組みと言えます。逆に満たさないものは、Java(J2ME)、FlashLiteとモバイルソフトウェアエンジニアにお馴染みの言語が多いようです。これらに共通しているのは3番目の要件のうちPUTとDELETEが使えないことです。これは携帯電話の限られたスペックを考慮して、環境を軽量に最適化(例えばJ2MEではJVMではなくキロバイトのメモリで動くKVMを使っている)しているため、当時あまり使われていなかった機能は極力削られているという経緯があります。

モバイルアプリ開発環境におけるAtomPub対応状況

前述のとおり、ほとんどのモバイルアプリ開発環境おいてはPUTとDELETEに対応していません。また、J2ME(DoJa)では厳重なセキュリティを考え、HTTPヘッダーの編集を許可しません。これはAtomPubだけでなくCookieが使えない原因ともなっています。これらを表3にまとめます。モバイルアプリとしてAtomPubを実装するためには、Socketクラスから自分で書くなどの工夫と労力が必要になります。

表3 携帯アプリ開発環境におけるAtom要件対応状況
環境説明標準ライブラリ利用によるヘッダーの編集標準ライブラリ利用によるPUT、DELETEの実行
J2ME(DoJa5.0)Sunmicrosystems提供の携帯端末用Javaプラットフォーム、DoCoMoが採用××
J2ME(MIDP2.0)Sunmicrosystems提供の携帯端末用Javaプラットフォーム,au,SoftBank,WILLCOMが採用×
FlashLite2.0Adobe提供の携帯端末用のFlashプラットフォーム,au,SoftBankが採用×
FlashLite3.0Adobe提供の携帯端末用のFlashプラットフォーム,DoCoMoが採用×
BREW3.1Qualcommが提供している携帯端末用C++プラットフォーム,auが採用×
AndroidGoogle提供の携帯端末用Javaプラットフォーム
.NET Compact Framework2.0Microsoft提供の携帯端末用フレームワーク、Windows Mobile5.0,6.0で採用

このように残念ながら携帯端末とAtomPubは相性がいいとは言えません。しかし携帯端末の性能は緩やかながらも向上していますし、Androidのような新しい開発環境も出現していますので、今後は携帯端末との親和性も高くなることが予想されます。

次は、実際にクライアントを作ってみましょう。今回は、AtomPubの実装要件を満たす言語の1つであるJava(J2SE)を使った実装を簡単に説明したいと思います。

J2SEによる実装

J2SEにおいてHTTP通信を行うための手段はSocketクラスをはじめ、たくさんありますが、その中でも簡単にHTTP独自の機能を使うためにはjava.net.HttpURLConnectionクラスを使います(ちなみにHTTPSにはjavax.net.ssl.HttpURLConnectionが用意されています⁠⁠。今回はこれを利用して、以下にAtomPubの通信手順を説明していきます。

  • ①.URI(URL)の設定
  • ②.コネクションのオープン
  • ③.HTTPリクエストヘッダーの設定
  • ④.POST、PUTの場合はアウトプットストリームの作成
  • ⑤.URI(URL)へ接続
  • ⑥.HTTPレスポンスコードを得る
  • ⑦.HTTPレスポンスヘッダーを得る
  • ⑧.HTTPレスポンスボディを得る(ある場合に限る)
  • ⑨.XMLのDOM化、DOMの処理
  • ⑩.①へ戻る

と見ていただいてわかるように、AtomPubはRESTベースなのでHTTP通信手順とほぼ同じです。

①.URI(URL)の設定、②.コネクションのオープン(リスト1)

まず、URLオブジェクトを得て、HttpURLConnectionオブジェクトを作ります。この時点ではURLが示すリソースに接続されていません。AtomPubでは、多くの場合URIを使うので、厳密にはリスト2のようになりますが、どちらでも構いません。

//リスト1
URL url =new URL(String);
HttpURLConnection con=(HttpURLConnection)url.openConnection();
/*リスト2
URI uri =new URI(String);
HttpURLConnection con=(HttpURLConnection)uri.toURL().openConnection();
*/

③.HTTPリクエストヘッダーの設定(リスト3)

リクエストヘッダーの設定を行います。POSTやPUTであれば、属性「Content-Type」「Content-Length」を明示する必要があります。また、仕様でOPTIONに定められているWSSEやBASIC認証に対応するには、属性「X-WSSE」「Authorization」リスト4のように追加します。

//リスト3
con.setRequestProperty("Content-Type", "image/jpeg");
/*ベーシック認証を使う場合
con.setRequestProperty("Authorization",makeBasic(ID,PASSWD);
*/
//中略
//リスト4 ベーシック認証
private String makeBasic(String id, String pass){
  String basic=id+":"+pass;
  basic = "Basic "+new String(Base64.encode(basic.getBytes())); 
  return basic;
}

④.POST、PUTの場合はアウトプットストリームの作成(リスト5)

POSTやPUTの場合、リソースに接続する前にPOSTまたはPUTするデータをHTTPリクエストボディに書き込むことでこれを実現します。具体的にはまず、コネクションにおいてPOST,PUTができるようにHTTPURLConnection.setDoOutput(boolean)で設定し、その後OutputStreamを作成します。

//リスト5
//POST(PUT)可能にします
con.setDoOutput(true);
///POST(PUT)用のOutputStreamを取得
os = con.getOutputStream();

次に、OutputstreamにPOST,PUTしたいファイルを書き込めば接続前の準備完了です。リスト6では、入力がFileの場合とStringの場合の2パターンを示しています。

//リスト6
//Fileの場合
InputStream fdata = new FileInputStream(File);
byte[] buffer = new byte[8192];
int rsize;
while ((rsize = fdata.read(buffer)) != -1) {
os.write(buffer, 0, rsize);
}

/*Stringの場合
os.write(String.getBytes());
*/

⑤.URI(URL)へ接続、⑥.HTTPレスポンスコードを得る、⑦.HTTPレスポンスヘッダーを得る(リスト7)

指定したURLへリクエストを送るためにHttpURLConnection.connect()を使います。この時点からHttpレスポンスが取得可能になります。続けてHttpレスポンスコード、Httpレスポンスヘッダーを取得します。

//リスト7
//接続
con.connect();
//レスポンスコードを得る
System.out.print("Response Code["+con.getResponseCode()+"]");
System.out.println(":"+"Response Message["+con.getResponseMessage()+"]");
//レスポンスヘッダー
Map headers = con.getHeaderFields();
Iterator it = headers.keySet().iterator();
System.out.println("Response Header");
while(it.hasNext()){
  String key=(String)it.next();
  System.out.println(" "+key+" : "+headers.get(key));
}

※AtomPubで使われている主なレスポンスコードを示します。これ以外はRFC2616を参照してください。

表4 レスポンスコード
レスポンスコード意味メソッド:補足
200OkGET,PUT,DELETE:成功時
201CreatedPOST:成功時
304Not modifiedGET:修正できないメンバー
415Unsupported Media TypePOST:サポート外のメディアタイプ

⑧.HTTPレスポンスボディを得る(ある場合に限る)(リスト8)

GET、POST、PUTでリクエストを出し、HTTPレスポンスコードが200または201ならば、HTTPレスポンスにHTTPレスポンスボディが含まれています。このレスポンスボディどうするかはサーバ側の今後の処理次第です。リスト7ではレスポンスボディに含まれるサービス文書等のXML文書を、2パターンで取り出しています。1つ目のパターンはレスポンスボディをInputStreamで取り出しDOM化しています。DOM化することで今後、プログラム内で扱いやすいデータとなります。2つ目のパターンはString化するもので単純な出力用に使う程度でしょうか。

//リスト8
// DOM化
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilde	 db = dbf.newDocumentBuilder();
Document doc = db.parse(con.getInputStream());
/* String化
BufferedReader  reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line;
System.out.println("ResponseBody");
while((line = inputreader.readLine())!= null){
    System.out.println(line);
}
*/

⑨.XMLのDOM化、DOMの処理

次は、得られたDOMを操作して、必要な情報を取り出します。ここではJava SwingのJtreeを使って視覚的に表現したいと思います(Stringで出せれば十分だという方は話が細かくなりますので、読み飛ばしましょう。⁠⁠。

図1 java swingによるクライアント実装
図1 java swingによるクライアント実装

JTreeはDefaultMutableTreeNodeを引数とするコンストラクタを持っているので、DefaultMutableTreeNodeでツリーを構築して、そのルートをJTreeのコンストラクタに渡せば簡単にツリー構造を視覚化できます。実際には、まずDOMのルートノードをDefaultMutableTreeNodeでインスタンス化し、addchild()でその子ノードを順次ツリーに追加します。子ノードにエレメントノードがあると、それを新たなルートとしてadd.child()を再帰的に繰り返します。このようにすることでJTreeへ変換することが可能です。具体的にはリスト9のようになります。

//リスト9
//rootノードの作成
Node rootTag = doc.getDocumentElement();
DefaultMutableTreeNode root = new DefaultMutableTreeNode(rootTag.getNodeName());
//rootから前順序で再帰的にdocを辿る
addChildren(rootTag, root);
//Tree作成
JTree tree = new JTree(root);
//中略
private void addChildren(Node node, DefaultMutableTreeNode branch) {
  //branchに対して子ノードをaddする
  NodeList list = node.getChildNodes();
  //子ノードのうちアトリビュートノードだけを処理する
  NamedNodeMap attributes = node.getAttributes();
  for(int i=0; i<attributes.getLength(); i++){
    DefaultMutableTreeNode att = new DefaultMutableTreeNode( attributes.item(i));
    branch.add(att);		
  }
 //子ノードのうちエレメントノードとテキストノードを処理する		
 for( int i=0; i<list.getLength(); i++ ) {
    Node child = list.item( i );
    if(/*テキストノードならば*/){
       //テキストノードの処理  
       continue;
    }
    //エレメントノードの処理
    DefaultMutableTreeNode tn = new DefaultMutableTreeNode( child.getNodeName());
    branch.add( tn );
    //追加したエレメントノードをルートとして再帰的にツリーを構成
    addChildren(child,tn);
  }	
}

これでDOMからJteeに変換することができました。これに加えてイベント処理系を利用することで、簡易なGUIが作成できると思います。一方、GUI上におけるJTreeの内容をXML化したいとき(例えば、Tree上で編集して、その内容をPUTするような状況)はJtreeに加えられた更新をイベント処理でDOMと同期させます。そしてDOMからXMLへの変換を行います。これはTransrormer.fransformを使えば簡単に実現できます。リスト10ではDOMの内容をUTF-8でStringとして取り出しています。

//リスト10
try{
ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
TransformerFactory tff = TransformerFactory.newInstance();
Transformer tr = tff.newTransformer();
tr.transform(new DOMSource(document), new StreamResult(bufferStream));
String st=bufferStream.toString("UTF-8");
}catch(Exception e){}

AtomPubクライアントの紹介

最後に、既存のAtomPubクライアントを幾つか紹介したいと思います。

APP Test Client
本稿で使わせてもらったJoeGregorioさんのAPP Test Siteのクライアントバージョンです。
the Ape
IETFのAtomPubワーキングループで議長を務めているTimBrayさんのクライアントです。ブラウザから使えます。
Atomic
AtomicはAtomPubを利用するためのFireFox用プラグインです。REST型のアプリケーションを開発するためのJava軽量フレームワーク"Restlet"を利用したAtomPubクライアント&サーバープロジェクト"atomojo"の一環で作成されました。このプラグインはまだ未実装な機能も多いですが、作者のAlex Milowskiさんが勢威製作中のようなので今後に期待です。
MobileAtomJ2ME
MobileAtomはJ2MEによる携帯アプリのAtomPubクライアントの実装です。2004年ごろのAtomPubの仕様をもとに作成されたものなので、HTTPメソッドのPUTとDELETEに対応するために、RFCでは認められていないSOAPを一部利用しています。しかし、ソースそのものは実装するときに参考になります。
Atomic
ウィザシステムの丸本さんのクライアントソフトです。和製ソフトでは一番有名かもしれません。

まとめ

最終回はAtomPubを試すと銘うって、ツール、実装方法、その他クライアントソフトを紹介しました。AtomPubは結局のところHTTP通信であるために、どのような試し方をしても理解や応用が容易であることがおわかりいただけたと思います。仮にRFC片手に自分で実装するとしても標準ライブラリの組み合わせだけでほぼ完結していまいます(RFCを理解する労力は翻訳によって軽減されるでしょう⁠⁠。このようにAtomPubは実装面においても非常に簡便であると言えます。これはAtomPubの設計思想が簡単・一般的であるRESTからブレなかったことに起因します。

また、AtomPubはRFC化されて間もないプロトコルです。今後は、ご紹介したツールをはじめRFC準拠の様々なクライアントが登場してくると予想されます。それらをウオッチすることでAtomPubがどのようなアレンジを加えられて市場に出現するか、どのように広まるか等を観察するのも面白いかもしれません。

最後に、この連載をきっかけにAtomPubの理解を深めていただけたり、少しでも何か使ってみよう・作ってみようと思っていただけたなら幸いです。

おすすめ記事

記事・ニュース一覧