Push me to the limit
Innerhalb der TwitterWebSocketApplication wird die eigene WebSocket-Implementierung durch das Überschreiben der createSocket()-Methode registriert. Die vom Browser ankommende Payload, also die gewünschten Twitter Trends, werden an die monitorTwitterStream()-Methode des WebSocket-Clients übergeben. Die TwitterStreamWebSocket-Klasse verwendet für den Zugriff auf das Twitter API das Open-Source-Projekt twitter4j [10]:
Listing 6: Eigene WebSocket-Implementierung
public class TwitterStreamWebSocket extends BaseServerWebSocket implements twitter4j.StatusListener { // GlassFish WebSocket API public TwitterStreamWebSocket(NetworkHandler handler, WebSocketListener[] listeners) { super(handler, listeners); // connect to Twitter twitterStream = new TwitterStreamFactory().getInstance("user", "password"); } // Eigene API: // (re) launch the twitter stream.. public void monitorTwitterStream(String[] track) { // clean ups... twitterStream.shutdown(); // (re) init twitterStream.setStatusListener(this); FilterQuery fq = new FilterQuery(); fq.track(track); try { twitterStream.filter(fq); } catch (TwitterException e) { e.printStackTrace(); } } // twitter4j APIs public void onStatus(Status status) { try { // push the data to the WebSocket client this.send(status.getText()); } catch (IOException e) { throw new RuntimeException(e); } } ....
Die Klasse erweitert das Glassfish API lediglich um Funktionalität des twitter4j-Projekts. Die Methode monitorTwitterStream() stellt die Verbindung zum Twitter-Stream her. Sobald neue Nachrichten auf dem Server eintreffen, wird seitens twitter4j die onStatus()-Methode aufgerufen. Diese nutzt die send()-Methode des WebSocket API, um den Text der Twitter-Nachricht zum Browser zu schicken. Zum Deployment der Anwendung kann ein Servlet als Treiber genutzt werden:
Listing 7: Servlet als Treiber der WebSocket-Anwendung
public class WebSocketsServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { WebSocketEngine.getEngine().register(new TwitterWebSocketApplication()); } }
Die TwitterWebSocketApplication wird innerhalb der init()-Methode mit der WebSocket Engine des Glassfish registriert. Das WebSocket API selbst ist unabhängig von dem Servlet API.
Andere Container, andere APIs
Der Jetty-Container verfügt bereits seit Version 7 über WebSocket-Support. Im Gegensatz zu Glassfish ist das Jetty-spezifische API jedoch stark vom Servlet API abhängig. Für die Programmierung einer WebSocket-Anwendung muss ein spezielles Servlet erweitert werden:
Listing 8: Jetty WebSocket Servlet
public class TwitterWebSocketServlet extends WebSocketServlet { protected WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { return new TwitterWebSocket(); } ... }
Ähnlich wie bei Glassfish muss auch hier eine WebSocket-„Client“-Klasse programmiert werden. Durch die Verdrahtung mit dem Servlet API unterscheidet sich das Jetty API deutlich von dem des Glassfish-Containers. Einen etwas anderen Weg schlägt Resin 4.x ein [11]. Zwar kann ein gewöhnliches Servlet genutzt werden, aber innerhalb der service()-Methode kommt ein proprietärer WebSocketListener ins Spiel:
Listing 9: WebSocket mit dem Resin-Container
public class TwitterServlet extends GenericServlet { public void service(ServletRequest req, ServletResponse res) throws IOException, ServletException { WebSocketServletRequest wsReq = (WebSocketServletRequest) req; WebSocketListener handler = new TwitterStreamHandler(); wsReq.startWebSocket(handler); } }
Das verdeutlicht ein großes Problem: Sämtliche Server-APIs sind derzeit inkompatibel, was die Realisierung von portablen Java-EE-Produkten schwierig macht.
Das Atmosphere-Framework
Statt auf ein einheitliches API zu warten, empfiehlt es sich, einen Blick auf das Atmosphere-Framework [12] zu werfen. Es bietet ein einheitliches Comet und WebSocket API für verschiedene Java-EE-Server. Ein Entwickler schreibt sein Programm gegen das Atmosphere API, und das Framework stellt zur Laufzeit sicher, dass die korrekte Comet- oder WebSocket-Implementierung genutzt wird. Standardmäßig schickt das Framework alle ankommenden Nachrichten zu sämtlichen verbundenen WebSocket-Clients, sodass ein einfacher Chat nur JavaScript- und HTML-Code benötigt [13]. Soll das Verhalten angepasst werden, muss der WebSocketAtmosphereHandler überschrieben werden:
Listing 10: WebSocket API des Atmosphere-Frameworks
public class TwitterWebSocketHandler extends WebSocketAtmosphereHandler { @Override public void onStateChange( AtmosphereResourceEventevent) throws IOException { .... } }
Innerhalb der onStateChange()-Methode kann nach bestimmten Kriterien entschieden werden, an welchen Browserclient die Nachricht geschickt wird. Atmosphere überzeugt ebenfalls durch die nahtlose REST-Integration auf Basis von Jersey. Neben einem einheitlichem Server-API für Comet/WebSocket bietet das Projekt ein jQuery Plug-in: Das Plug-in stellt fest, ob Server und Client WebSocket unterstützen. Ist das nicht der Fall, wird versucht, stattdessen HTTP-Streaming oder Long Polling zu nutzen.
Ausblick und Fazit
WebSocket stellt einen interessanten Standard dar, dessen Protokoll- und Client-API durch etablierte Organisationen spezifiziert wird. Derzeit gibt es noch keine finale Version des Protokolls, allerdings sind die Arbeiten an dem Client-API schon nahezu abgeschlossen. Das Hauptproblem ist jedoch das vollständige Fehlen eines einheitlichen (Java-)Server-API.
Atmosphere stellt zwar einen Wrapper für diverse Container bereit, allerdings fehlt auf Dauer eine vollwertige Java-EE-Integration. Auf der JavaOne 2010 hat der Java-EE-Spezifikationsleiter Roberto Chinnici bereits angedeutet, dass er WebSocket-Support innerhalb von Java EE 7 sieht. Diese Aussage unterstreicht vor allem eines: WebSocket wird mehr und mehr an Bedeutung bei der Realisierung von Real-Time-Webapplikationen gewinnen.
Hinterlasse einen Kommentar