2014年6月24日火曜日

気象庁のオープンデータを取得する(購読確認)

気象庁のオープンデータについて調査してこいといわれ
簡単に取得するまでの流れとJavaでのSubscriber構築についてメモする

Javaのサンプルソース全然ないんだよね…


■気象庁のサイト
気象庁防災情報XMLフォーマット 情報提供ページ


■手順
  1. 上記サイトにある申請フォームを使って登録申請
  2. 気象庁からAlert Hub経由で”購読確認”がGETリクエストで送られてくる
  3. 予め構築したSubscriverでリクエストを返す
  4. 登録終了後、同Subscriverに随時更新フィード(xml)がPOSTリクエストで送られてくる
  5. 更新フィードから欲しい情報を取得する
大まかにいうとこんな感じ。
PubSubHubbubっていう、フィードをリアルタイムで配信(プッシュ)するプロトコルを使っているシステム。

なんにせよSubscriver(購読側)を構築してサーバにあげてリクエストを受けられる状態にしておかないといけないわけなんだけど、”購読確認”と”更新フィード受信”っていう全然違う機能を同じURLで受け付けなきゃいけないのがちょっと厄介。


■実装
まずGETとPOSTの受け口を作る。そんで購読確認を返せるようにするんだけど、更新フィード受信後の処理はおいときます。

rubyなら
http://www.mk-mode.com/octopress/2013/11/20/rails-implement-pubsubhubbub-subscriber/

PHPなら
http://washitake.com/blog/archives/117

この辺りのサンプルが分かり易い。

しかしJavaはシンプルなやつがなくて。一応サンプルあるんだけど古いし分かりづらい。。
てことで作ってみたやつ↓

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/Subscriber")
public class Subscriber extends HttpServlet {

 enum MessageStatus {
  ERROR,
  OK
 };

 public Subscriber() {
  super();
 }

 /**
  * 購読確認
  * GETリクエストを取得し、ステータスコード200とhub.challengeを返す
  */
 protected void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  String hubmode = null, hubchallenge = null;
  MessageStatus stsMessage = MessageStatus.ERROR;

  if (request != null) {
   if (request.getParameter("hub.mode") != null) {
    hubmode = request.getParameter("hub.mode");
   }
   if (request.getParameter("hub.challenge") != null) {
    hubchallenge = request.getParameter("hub.challenge");
   }

   if (hubmode != null && hubchallenge != null) {
    if (hubmode.equals("subscribe") || hubmode.equals("unsubscribe")) {
     stsMessage = MessageStatus.OK;
    }
   }
  }

  // ヘッダーのContent-Typeをtext/plainに設定
  response.setHeader("Content-Type", "text/plain");

  switch (stsMessage) {
  case OK:
   // OKの場合ステータスコード200と、hubchallengeの値をそのまま返す
   response.setStatus(HttpServletResponse.SC_OK);
   ServletOutputStream sos = response.getOutputStream();
   sos.print(hubchallenge);
   sos.close();
   break;
  default:
   response.sendError(HttpServletResponse.SC_NOT_FOUND, "404 not found");
   break;
  }

 }

 /**
  * 更新フィード受信
  * POSTリクエストで更新フィードからデータを取得する
  */
 protected void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  // 取得xml毎の処理
 }

}

要するに肝は
GET リクエスト時、HEADER の Content-Type に text/plain を設定し、GET リクエスト受信時に取得した「チャレンジコード(hub.challenge の値)」をそのまま、ステータスコード 200 とともに返却しないといけない。(チャレンジコードの末尾に改行があってはならない)
ってところです。

ちなみにGETリクエストのパラメータは以下。verify_token は今回無視。
hub.mode … subscribe(購読登録) か unsubscribe(登録解除)
hub.topic … フィードの URL
hub.challenge … Hub 側指定の認証用のランダムな文字列
hub.verify_token … 購読者側指定の認証用のランダムな文字列(但し、Hub 登録時に指定した場合のみ)
hub.lease_seconds … Hub 再登録までの時間(但し、Hub 登録時に指定した場合のみ。上記の例では使用していない)
■テスト
とりあえず購読確認のテストをしてみる。
購読確認が問題なくできれば、あとはどんどん更新フィードを送ってくれる。

テストはGoogle がテスト用に公開している Hub を利用する。
https://pubsubhubbub.appspot.com/

ここのSubscribeページでフォームの内容を入れてDo it!ボタン押す
Callback URL:用意した Subscriber の URL を入力する。
Topic URL:フィード発行側の URL を入力する。
Verify Type:は、デフォルトのまま。
Mode:Subscribeを選択する。
Verify Token:認証用文字列を入力する。(利用したい場合のみ)
HMAC secret:デフォルト(空白)のまま。
Lease seconds:デフォルト(空白)のまま。(指定すれば、購読意思確認(Hub から GET リクエストが届く)間隔を変更できる。デフォルトは「5日」)
Topic URLは、適当にブログなんかで更新フィード作ってatom.xmlのURLを貼るのが楽。

次に、同ページの【Subscriber Diagnostics】のほうにURLを入力してGet infoボタンを押す

すると購読確認の結果が表示される。
statusverifiedならOK

あとは任意にPOSTのほうの実装をやればよいと

2 件のコメント:

  1. 拡散したいので、できればソーシャルボタン欲しいです。

    返信削除
    返信
    1. 共有からいけませんか?あとWEB版なら小さいけど各ソーシャルボタンついてるはずです!

      削除