標準Java APIを使用したWebサービスへのアクセス

昨今のJavaは、大規模なWebアプリケーションを構築するための技術として、すっかり定着しました。サーバサイドでの処理としては、データベースへのアクセスが大半を占めることでしょう。しかし最近では、一般公開されている多くのWebサービスを利用する機会が増えてきています。それは一部のマッシュアップサイトにとどまらず、企業システムの中でも採用されることが多くなってきています。Javaエンジニアにとって、サーバサイドマッシュアップというキーワードは、今後どんどん身近なものになります。
前回の内容は、典型的なマッシュアップの形態や、SOAPやRESTなどのWebサービスの種類についても取り上げました。今回は、実際にWebサービスにアクセスして情報を得るためのコードを紹介しようと思います。何らかのライブラリをいきなり使っても良いのですが、まずは堅く、標準Java APIのみで試してみたいと思います。といっても、とっても面倒な手順というわけではありませんので、ご安心ください。
さて、「イベント検索アプリケーション」を考えた場合、例えば、あるイベントに関連したブログエントリもイベント情報として提示できると、アプリケーションの付加価値があがりますね。そのイベントの開催情報、参加した人の感想、さらにはイベントのスピーカーの方の話なども見つけることができるかもしれません。まさにマッシュアップですね。ブログ検索のWebサービスで代表的なものといえば、株式会社テクノラティジャパンが提供しているブログ検索APIがあげられます。今回はこのサービスを実際にアクセスしてみます。
そのためには、最初にAPIキーを取得する必要があります。多くのWebサービスでは、最初に利用登録を行い、その結果発行されたキーを使ってサービスを利用するという形態がほとんどです。テクノラティ社のブログ検索APIのAPIキーを得るためには、まずメンバー登録を行います。
メンバー登録メンバー登録が済めば、以下のURLでAPIキーを得ることができます。ページ上部の「あなたのテクノラティAPIキーはこちらです:」の後が、APIキー文字列となります。
テクノラティジャパンのAPIについてAPIキーを入手できれば、ブログ検索APIをすぐに利用することができます。APIの説明は、以下のURLに解説されています。今回使用するのは、キーワード検索APIです。
テクノラティジャパン APIの使用に関して キーワード検索APIこのブログ検索APIは、RESTfulライクな形式のWebサービスですので、試しにアクセスするのは非常に簡単です。単にWebブラウザでURLを打ち込んでアクセスする、それだけです。例えば「デブサミ」というキーワードでブログを検索してみるには、以下のURLにアクセスするだけです。APIキーの部分には、先ほど入手したAPIキーをそのまま記載します。
http://api.technorati.jp/search?key=[APIキー]&query=デブサミ
以下のように、検索結果がXML形式で得られることがわかると思います。REST準拠と謳っているWebサービスであれば、このように手軽にアクセスできます。逆に、SOAP対応と謳っているWebサービスの場合は、比較的複雑な手順を踏む必要が出てきます。
さて、早速このサービスにアクセスするためのコードを説明していきましょう。今回は標準Java APIを使用しますが、次回以降より便利な実装に入れ替える予定ですので、ブログ検索のインタフェースをきっちりと決めておきましょう。コード1にインタフェースを、コード2に検索結果を格納するためのクラスを紹介します。
コード1 ブログ検索インタフェース
package jp.tkrb.event.services;
import java.util.List;
public interface TechnoratiBlogSearch {
List<Weblog> search(String keyword);
}
package jp.tkrb.event.services;
public class Weblog {
private String name;
private String url;
private String title;
private String permalink;
public Weblog(String name, String url, String title, String permalink) {
super();
this.name = name;
this.url = url;
this.title = title;
this.permalink = permalink;
}
public String getName() {
return name;
}
public String getPermalink() {
return permalink;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
@Override
public String toString() {
return name + " : " + url + " : " + title + " : " + permalink;
}
}
キーワードを渡すと、関連するブログエントリがWeblogクラスのインスタンスのコレクションとして得られる、というAPIとしました。Weblogクラスには、toString()メソッドを定義して、内容を目で見てわかりやすいようにしてあります。
いよいよWebサービスにアクセスするコードです。標準Java APIのみの使用となるので、少し長めのコードとなっています。ちなみに、import文は省略しています。
コード3 標準Java APIを使用した実装クラス
package jp.tkrb.event.services;
public class TechnoratiBlogSearchSimpleImpl implements TechnoratiBlogSearch {
private static final String TECHNORATI_URL = "http://api.technorati.jp/search?key=";
private static final String API_KEY = "72e0ca796d918081695af3014eb3b751";
public List<Weblog> search(String keyword) {
InputStream in = null;
try {
List<Weblog> weblogs = new ArrayList<Weblog>();
String encoded = URLEncoder.encode(keyword, "UTF-8");
URL technorati = new URL(TECHNORATI_URL + API_KEY + "&query=" + encoded);
URLConnection connection = technorati.openConnection();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
in = connection.getInputStream();
Document doc = builder.parse(in);
Element root = doc.getDocumentElement();
Element document = child(root, "document");
NodeList items = document.getElementsByTagName("item");
for (int i = 0; i < items.getLength(); i++) {
Element item = (Element)items.item(i);
Element weblog = child(item, "weblog");
String name = child(weblog, "name").getFirstChild().getNodeValue();
String url = child(weblog, "url").getFirstChild().getNodeValue();
String title = child(item, "title").getFirstChild().getNodeValue();
String permalink = child(item, "permalink").getFirstChild().getNodeValue();
weblogs.add(new Weblog(name, url, title, permalink));
}
return weblogs;
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
private Element child(Element parent, String tagName) {
NodeList children = parent.getElementsByTagName(tagName);
return (Element)children.item(0);
}
}
部分的に見ていきましょう。まずは、ブログ検索APIにアクセスするコードです。
String encoded = URLEncoder.encode(keyword, "UTF-8");
URL technorati = new URL(TECHNORATI_URL + API_KEY + "&query=" + encoded);
URLConnection connection = technorati.openConnection();
まず、キーワード文字列に対してUTF-8でURLエンコードをかけます。例えば「デブサミ」というキーワード文字列であれば、「%E3%83%87%E3%83%96%E3%82%B5%E3%83%9F」という変換となります。その後、APIキーを含むURL全体を構築して、それを元にURLオブジェクトを生成します。それだけですと単なる識別子なので、openConnection()メソッドを呼び出して、WebサービスにアクセスするためのURLConnectionオブジェクトを得ます。
ブログ検索の結果はXML形式となりますので、その内容を解析するためにXMLパーサーを利用することになります。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
in = connection.getInputStream();
Document doc = builder.parse(in);
上記のコードは、JavaSEに標準搭載されているJAXP APIを利用しています。具体的には、DocumentBuilderオブジェクトを取得し、URLConnectionオブジェクトから入力ストリームを得て、parse()メソッドに渡しています。この時点でWebサービスとの通信が行われ、結果のXMLがパースされてDOMが構築されます。
あとは結果の内容を解析するだけなのですが、残念ながらこの解析処理が面倒なコーディングとなります。DOMはオブジェクトのツリー構造となるため、このツリーを辿って必要な情報を探し出します。
ここで、ちょっとした便利メソッドを紹介しましょう。DOM内のある要素の子要素をタグ名を指定して取得する、というchild()メソッドが今回活躍してくれます。
private Element child(Element parent, String tagName) {
NodeList children = parent.getElementsByTagName(tagName);
return (Element)children.item(0);
}
指定された要素の子要素をgetElementsByTagName()メソッドで探します。このメソッドの結果は複数存在することが想定されているのですが、このメソッドの前提として「子要素は1つのみ」と見なし、固定的に1つ目の要素を返しています。
このchild()メソッドを使って、必要となる情報を抜き出してWeblogオブジェクトを作り出している処理が、以下のコードです。
Element root = doc.getDocumentElement();
Element document = child(root, "document");
NodeList items = document.getElementsByTagName("item");
for (int i = 0; i < items.getLength(); i++) {
Element item = (Element)items.item(i);
Element weblog = child(item, "weblog");
String name = child(weblog, "name").getFirstChild().getNodeValue();
String url = child(weblog, "url").getFirstChild().getNodeValue();
String title = child(item, "title").getFirstChild().getNodeValue();
String permalink = child(item, "permalink").getFirstChild().getNodeValue();
weblogs.add(new Weblog(name, url, title, permalink));
}
検索結果は、document要素の子要素である個々のitem要素が相当しますので、item要素をfor文でぐるぐる回しています。item要素内にある「weblog/name、weblog/url、title、permalink」要素が、それぞれ「ブログ名、ブログのURL、ブログエントリのタイトル、ブログエントリのURL」となります。これらを先ほどのchild()メソッドを利用して取得し、Weblogオブジェクトを生成してコレクションに格納しています。あとはこのコレクションをブラウザに返却してイベント情報として表示してあげれば、マッシュアップが完成するということになります。
以上で、標準Java APIだけを使ったサーバサイドマッシュアップの準備体操は終わりです。今回のブログ検索では、扱いたい情報が4つしかないため、それほど行数があるわけではありません。しかし、量が多くなるに従って、このような手動マッピングは非常に苦痛になってくることでしょう。Webサービス側の仕様変更に弱いのも気になりますね。次回はそういった弱点を克服し、さらに手軽にサーバサイドマッシュアップを行うための施策を考えてみたいと思います。




最近のコメント