WebSockets im Überblick

Push me to the limit

Matthias Weßendorf

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(
      AtmosphereResourceEvent event)
      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.

Matthias Weßendorf ist PMC Chair von Apache MyFaces. Beruflich beschäftigt er sich mit Themen rund um das „Next Generation Web“. Matthias bloggt regelmäßig auf http://matthiaswessendorf.wordpress.com (Twitter: @mwessendorf).
Geschrieben von
Matthias Weßendorf
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.