Archive for 2012年11月12日
WebSocket Twitter タイムライン・取得サンプル
WebSocket と Twitter4J を使った、Twitter のタイムライン取得 WebSocket サンプルアプリケーションのサンプルコードを公開致します。
2013 年 5 月 13 日追記:本ソースコードは、WebSocket の仕様が完全に FIX する前に記載したコードのため、既に記載している内容のコードでは動かなくなっています。新しい WebSocket の API では @WebSocketEndpoint アノテーションの代わりに @ServerEndpoint アノテーション等を使用します。 詳しくは、javax.websocketパッケージ、javax.websocket.serverをご参照ください。 |
Java EE 7 の WebSocket の概要は下記をご参照ください。
下記は、2012 年 11 月現在、 JSR-356 の Java API for WebSocket の標準仕様で開発・検討中の API 、また Twitter4J 2.2.6 を利用し実装したサンプルコードです。正式リリース時には下記で記載した内容(アノテーションや引数等)が変更される可能性もありますので、ご注意ください。
本、プログラムは現在開発中の GlassFish の開発ビルド GlassFish v4 b58 で動作しています (既に最新の b61では @WebSocketEndpoint のコンテキスト・ルートの設定方法が変更されています。ただし b61 は少し挙動が不安定なため b58 でデモを実施しています)。
WebSocket はクライアントとサーバ間で双方向・全二重が可能な HTTP のアップグレード・プロトコルを使用して通信を行います。
一度、WebSocket の接続が確立した後は、下記それぞれのライフサイクルに応じた処理をアノテーションを付加してかんたんに実装する事ができます。
- 接続確立時
- メッセージ受信時
- エラー発生時
- 接続切断時
下記に、今回の Twitter のタイムラインを取得するサンプルのコードを記載します。@WebSocketEndpoint アノテーションをクラスに付加し、引数に接続する URL のコンテキスト・ルートを指定します。これにより、この例では、ws://WEBSOCKET-SERVER/APP_NAME/twitter でアクセスされたリクエストに対して処理を行います。
@WebSocketOpen, @WebSocketClose のアノテーションが付加されたメソッドではそれぞれ下記の処理を行います。
- @WebSocketOpen:クライアントが接続をしてきた際に、リモート・エンドポイント(クライアント)の情報(Session)を配列に挿入
- @WebSocketClose:クライアントが切断された際に、配列からリモート・エンドポイント(クライアント)の情報(Session)を削除
package jp.co.oracle; import javax.net.websocket.Session; import javax.net.websocket.annotations.WebSocketClose; import javax.net.websocket.annotations.WebSocketEndpoint; import javax.net.websocket.annotations.WebSocketOpen; @WebSocketEndpoint(path = "/twitter") public class TwitterWebSocket { @WebSocketOpen public void initOpen(Session session) { System.out.println("client had accessed" + session.getRemote().toString()); TwitterClientSingleton.peers.add(session); } @WebSocketClose public void closeWebSocket(Session session) { System.out.println("client had cut the connect" + session.getRemote().toString()); TwitterClientSingleton.peers.remove(session); } }
次に、Twitter のメッセージを受信した際に、接続されている全てのクライアントに対してメッセージを配信するコードを下記に示します。下記は、Twitter4J の Streaming API を使用し、またシングルトン EJB として実装しています。本クラスはアプリケーションの起動時にアプリケーション・サーバが自動的に初期化するように @Startup のアノテーションを付加しています。また、シングルトン EJB が EJB コンテナによって初期化された後に @PostConstruct を付加したメソッド initTwitterStream() が呼び出されます。この initTwitterStream() メソッド中では Twitter4J の Stream API を利用して、Twitter の Stream をオープンしています。(ここでは簡単の為、検索キーワードとして “java” を静的に埋め込んでいます)
また、Twitter4J では StatusAdapter#onStatus() のメソッドで、フィルターにマッチしたメッセージを受信する事ができますので、そのメソッド内で全接続クライアントに対してシーケンシャルでメッセージを配信しています(もちろん JSON 形式などに変換して配信する事も可能ですが、簡単のため単純な文字列で送信しています)。
(※ 仮に、サーバ側で(チャット等のように)クライアント側から何らかのメッセージを受け取りたい場合は、@WebSocketMessage を付加したメソッドを実装していただく事でクライアントから情報を受信する事もできます。)
package jp.co.oracle; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.annotation.PostConstruct; import javax.ejb.Singleton; import javax.ejb.Startup; import javax.net.websocket.Session; import twitter4j.FilterQuery; import twitter4j.Status; import twitter4j.StatusAdapter; import twitter4j.TwitterStream; import twitter4j.TwitterStreamFactory; import twitter4j.User; @Startup @Singleton public class TwitterClientSingleton extends StatusAdapter { private static TwitterStream twStream = null; public static Set<Session> peers = null; static { peers = Collections.synchronizedSet(new HashSet()); } @PostConstruct public void initTwitterStream() { //Twitter Stream の初期化 twStream = TwitterStreamFactory.getSingleton(); FilterQuery filter = new FilterQuery(); filter.track(new String[]{"#dev459"}); twStream.addListener(this); twStream.filter(filter); } @Override public void onStatus(Status status) { //Twitter のフィルターに引っかかった場合 User user = status.getUser(); // if (status.getUser().getLang().equals("ja")) { String resStr = "@" + user.getScreenName() + " : " + status.getText(); System.out.println(resStr); try { for (Session peer : peers) { peer.getRemote().sendString(resStr); } } catch (IOException ioe) { ioe.printStackTrace(); } // } } }
最後に、View の部分を下記に記載します。View の部分は、今回の場合はとても簡単なサンプルですので、単なる HTML でも JSF でも、何で実装しても問題ないかと思います。下記の例では私は JSF を使って(XHTML として)実装しています。この例では、サーバからメッセージを受信した際に、onMessage () が呼び出され、結果としてテーブルの先頭行に着信メッセージを追加していっています。
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Twitter TimeLine Sample</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Twitter TimeLine Sample</title> <style type="text/css"> table,td,th { width: 700px; border-collapse: collapse; border: 1px black solid; } </style> <script language="javascript" type="text/javascript"> var wsUri = "ws://localhost:8080/TwitterTimeLine/twitter"; var websocket = new WebSocket(wsUri); websocket.onopen = function(evt) { onOpen(evt) }; websocket.onmessage = function(evt) { onMessage(evt) }; websocket.onerror = function(evt) { onError(evt) }; var numberOfMessage; function init() { numberOfMessage = 0; } function onOpen(evt) { ; } function onMessage(evt) { writeToScreen(evt.data); numberOfMessage++; } function onError(evt) { writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); } function writeToScreen(messages) { var table = document.getElementById("TBL"); var row = table.insertRow(0); var cell1 = row.insertCell(0); var textNode = document.createTextNode(messages); var z = numberOfMessage%2; if(z==1){ cell1.style.backgroundColor="#ADD8E6" ; } cell1.appendChild(textNode); } window.addEventListener("load", init, false); </script> </h:head> <h:body> <h2>Twitter タイムライン<BR/>WebSocket サンプル・アプリケーション!!</h2> <TABLE BORDER="1" ID="TBL"> </TABLE> </h:body> </html>
このサンプルを実行するためには、事前に Twitter のコンシュマー・キーや、アクセス・トークン等を事前に入手しておく必要があります。Twitter より入手した情報をWEB-INF/classes ディレクトリ配下に twitter4j.properties ファイルを作成し、下記のように記載してください。
# To change this template, choose Tools | Templates # and open the template in the editor. debug=false oauth.consumerKey=********************* oauth.consumerSecret=**************************************** oauth.accessToken=********-**************************************** oauth.accessTokenSecret=****** |
また、本プログラムを実行するためには、下記のライブラリが必要です。
- Twitter4J コアライブラリ: twitter4j-core-2.2.6.jar
- Twitter4J Streaming ライブラリ: twitter4j-stream-2.2.6.jar
- WebSocket : tyrus-servlet.jar (GlassFish-INSTALL/glassfish/modules 配下に存在)
- EJB : (GlassFish-INSTALL/glassfish/modules 配下に存在)
- CDI : (GlassFish-INSTALL/glassfish/modules 配下に存在)
念のため、NetBeans プロジェクトのディレクトリ構成も公開します。
最後に、
上記のサンプルコードをご参照頂き、シングルトン EJB と WebSocket を利用する事でとても簡単にそして効率的に、バックエンド・リソース(応用として:DBのカラム変更チェック等)のモニタリング・アプリケーションが作成できる事がお分かりいただけるかと思います。
実行例:
今までは同じ情報を参照(例えば DB カラム)するために、参照したいクライアントの数だけ、HTTP リクエストを送信し、SQL のクエリを実行していましたが、WebSocket を利用すると1つのモニタリングアプリをサーバ側に作成する事で、効率良く、そして最小のデータサイズでリアルタイムに情報を送受信できるようになります。
WebSocket は Java EE 7 で新規追加される技術の中でも特に注目すべき技術です。
WebSocket にご興味のある方は是非、開発中の GlassFish v4 と NetBeans を使って試してみてください。
JavaOne 報告会 & JJUG CCC
11 月 9 日の JavaOne 報告会と 11 月 10 日の JJUG CCC と2日続けて、Java に関するセミナーを開催致しました。JavaOne 報告会では Java EE 7 に関連するシンプル化された技術(JSON, Batch, JMS 2.0, EL 3.0)情報をお届けし、JJUG CCC では、今の Java のトレンドと、Java EE プラットフォームにおける HTML 5 対応と題し、JSF 2.2, WebSocket そして研究開発中のプロジェクトである、Project Avatar についてご紹介致しました。
下記にそれぞれで発表した内容を公開致しましたので、どうぞご参照ください。
(※ ファイルアップ時にフォント情報が壊れてしまっており、私がプレゼン時に使用したフォントと異なる点につきまして、どうかご了承ください。)
11 月 9 日 JavaOne 報告会でご紹介した内容(JSON, Batch, JMS 2.0 , EL 3.0):
11 月 10 日 JJUG CCC でご紹介した内容 (Java のトレンド)
11 月 10 日 JJUG CCC でご紹介した内容 (HTML 5 : JSF 2.2, WebSocket, Avatar)