Jetty 9.0.3でWebSocket
Jetty 9 – Updated WebSocket API | Jetty & Cometd Blogs
を基にWebSocketサンプルを作成したものの、微妙にAPIのパッケージ名が変わっていたりして混乱したので、ちょっとメモしてみる。
とりあえずpom.xml(maven v2.0.9で動作確認)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>sample</groupId> <artifactId>WebSocketSample</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>WebSocketSample</name> <dependencies> <dependency> <groupId>servletapi</groupId> <artifactId>servlet-api</artifactId> <version>2.4-20040521</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-server</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-servlet</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-common</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-api</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-client</artifactId> <version>9.0.3.v20130506</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> <encoding>UTF-8</encoding> <debug>true</debug> <optimize>false</optimize> <fork>true</fork> </configuration> </plugin> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.0.3.v20130506</version> <configuration> <contextPath>/</contextPath> <scanIntervalSeconds>10</scanIntervalSeconds> <connectors> </connectors> </configuration> <dependencies> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-server</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-servlet</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-common</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-api</artifactId> <version>9.0.3.v20130506</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-client</artifactId> <version>9.0.3.v20130506</version> </dependency> </dependencies> </plugin> </plugins> <finalName>WebSocketSample</finalName> </build> </project>
jetty-maven-pluginを使ってWebSocketを使用するため、プラグインでも依存性を指定してます(webで検索するとjetty-maven-pluginではWebSocketを使用できないっていう情報が引っかかりますが、誤り)。
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app id="TaskList" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <display-name>WebSocketSample</display-name> <servlet-name>WebSocketSample</servlet-name> <servlet-class>sample.servlet.WebSocketServletImpl</servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>WebSocketSample</servlet-name> <url-pattern>/WebSocketSample/*</url-pattern> </servlet-mapping> </web-app>
sample.servlet.WebSocketServletImplクラス
package sample.servlet; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; public class WebSocketServletImpl extends WebSocketServlet{ /** * generated by Eclipse. */ private static final long serialVersionUID = 8202951174317989818L; @Override public void configure(WebSocketServletFactory factory) { //Listenerクラスとして、sample.WebSocketSampleを指定 factory.register(sample.WebSocketSample.class); } }
Listenerとして動くsample.WebSocketSampleクラス。
package sample; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; @WebSocket public class WebSocketSample { private Session session; @OnWebSocketConnect public void onConnect(Session session) { this.session = session; WebSocketBroadcaster.getInstance().join(this); } @OnWebSocketMessage public void onText(String message) { WebSocketBroadcaster.getInstance().sendToAll(message); } @OnWebSocketClose public void onClose(int statusCode, String reason) { WebSocketBroadcaster.getInstance().bye(this); } public Session getSession(){ return this.session; } }
POJOにアノテーションを付けるタイプです。ここのサンプルとはアノテーションのパッケージ名が違うので注意。
最後に、Listenerを呼び出すサンプルクラスsample.WebSocketBroadcasterクラス。
package sample; import java.util.ArrayList; import java.util.List; /** * 接続中の全てのClientに対してメッセージを送信するサンプルクラスです。 * */ public class WebSocketBroadcaster { private static WebSocketBroadcaster INSTANCE = new WebSocketBroadcaster(); private List<WebSocketSample> clients = new ArrayList<WebSocketSample>(); private WebSocketBroadcaster(){ } protected static WebSocketBroadcaster getInstance(){ return INSTANCE; } /** * クライアントを追加します * */ protected void join(WebSocketSample socket){ clients.add(socket); } /** * クライアントを削除します * */ protected void bye(WebSocketSample socket){ clients.remove(socket); } /** * すべてのユーザへメッセージを送信します。 * */ protected void sendToAll(String message){ for(WebSocketSample member: clients){ member.getSession().getRemote().sendStringByFuture(message); } } }
それにしても、jetty9.0と9.0.3でパッケージ名が結構違う・・・
あとはプロジェクトホームからコマンドラインで
mvn clean package jetty:run
と実行すればjettyプラグインが起動するはず。
jettyが起動したら、Chrome拡張のSimple WebSocket Clientを使って、
ws://localhost:8080/WebSocketSample
への接続を行った後にメッセージを送信すると、そのメッセージが戻ってくることが確認できるはずです。数分放って置くとコネクションタイムアウトになってクライアント側からもサーバ側からも接続が切れていることすらわからなくなるので注意が必要ですが。
タイムアウトを防ぐには、ポーリングっぽく定期的にクライアント-サーバ間でメッセージをやり取りするしか無いのかな・・・Ajaxのポーリングと違って、既存のコネクションを使いまわすのでそんなに負荷はかからないはずですが、何となく本末転倒感。