<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="stomp">
  <title>STOMP &amp; WebSockets on TorqueBox</title>

  <section>
    <title>Overview</title>

    <para>TorqueBox provides real-time bidirectional communication between
    applications and web-browsers using a combination of WebSockets and STOMP.
    Raw access to WebSockets is not provided. Instead, multiplexed
    communication is supported through the layering of messaging semantics on
    top. Additionally, optional integration into other messaging systems (such
    as JMS/HornetQ) are provided to enable advanced application
    architectures.</para>

    <para>TorqueBox provides support for Stomplets to allow explicit control
    and design of messaging end-points, instead of simple direct bridging to
    some other underlying messaging technology, such as a JMS broker.</para>

    <section id="websockets-overview">
      <title>What is WebSockets?</title>

      <para>WebSockets is a new specification to allow synchronous
      bidirectional communication between a client (such as a web browser) and
      a server. While simliar to TCP sockets, WebSockets is a protocol that
      operates as an upgraded HTTP connection, exchanging variable-length
      <emphasis>frames</emphasis> between the two parties, instead of a
      stream.</para>

      <para>A browser may access a WebSockets-based service using Javascript.
      Once connected, the client and server must determine the meaning of any
      data sent across the socket. The WebSockets transport itself provides no
      protocol semantics beyond data frames passing each direction. TorqueBox
      implicitly applies STOMP semantics to the WebSocket connection.</para>
    </section>

    <section>
      <title>What is STOMP?</title>

      <para>STOMP stands for Stream-Oriented Messaging Protocol. STOMP defines
      a protocol for clients and servers to communicate with messaging
      semantics. STOMP does not define any implementation details, but rather
      addresses an easy-to-implement wire protocol for messaging
      integrations.</para>

      <para>STOMP provides higher semantics on top of the WebSockets
      transport. STOMP defines a handful of frame types that are mapped onto
      WebSockets frames.</para>

      <para><itemizedlist>
          <listitem>
            <para><command>CONNECT</command></para>
          </listitem>

          <listitem>
            <para><command>SUBSCRIBE</command></para>
          </listitem>

          <listitem>
            <para><command>UNSUBSCRIBE</command></para>
          </listitem>

          <listitem>
            <para><command>SEND</command> (messages sent to the server)</para>
          </listitem>

          <listitem>
            <para><command>MESSAGE</command> (for messages send from the
            server)</para>
          </listitem>

          <listitem>
            <para><command>BEGIN</command>, <command>COMMIT</command>,
            <command>ROLLBACK</command> (transaction management)</para>
          </listitem>
        </itemizedlist></para>
    </section>

    <section>
      <title>What are Stomplets?</title>

      <para>The Stomplet specification defines a controller (in the MVC sense
      of controllers) API for working with asynchronous messaging end-points.
      Stomplets are mapped to STOMP destinations (possibly using wildcards,
      like Rails routes), coordinating clients subscribing to receive messages
      and clients sending messages.</para>

      <para>Stomplets are long-lived stateful controllers.</para>
    </section>
  </section>

  <section>
    <title>Ruby Stomplets</title>

    <para>Ruby Stomplets have a handful of methods which may be implemented to
    support all messaging actions.</para>

    <para><itemizedlist>
        <listitem>
          <para><code>configure(config)</code> Configures the Stomplet with
          its name/value configuration and context.</para>
        </listitem>

        <listitem>
          <para><code>destroy()</code> Destroys the Stomplet and releases
          resources when it is taken out of service.</para>
        </listitem>

        <listitem>
          <para><code>on_subscribe(subscriber)</code> Called when a client
          wishes to receive messages.</para>
        </listitem>

        <listitem>
          <para><code>on_unsubscribe(subscriber)</code> Called when a client
          no longer wishes to receive messages.</para>
        </listitem>

        <listitem>
          <para><code>on_message(message)</code> Called when a client has sent
          a message.</para>
        </listitem>
      </itemizedlist></para>

    <section>
      <title>Stomplet API</title>

      <section>
        <title><code>configure(config)</code></title>

        <para>The configure(config) method is called for each instance of the
        Stomplet instantiated by the container. The config parameter includes
        any name/value pairs specified in the configuration of the Stomplet
        for a given route.</para>

        <para>The configure(...) method is typically where a Stomplet would
        acquire any resources it needs to handle subscription requests and
        sent messages.</para>
      </section>

      <section>
        <title><code>destroy()</code></title>

        <para>The <code>destroy()</code> method is called for each instance of
        the Stomplet when the container undeploys its route. This method is
        typically where all resources are released and connections to
        underlying systems are terminated.</para>
      </section>

      <section>
        <title><code>on_subscribe(subscriber)</code> and
        <code>on_unsubscribe(subscriber)</code></title>

        <para>The <code>on_subscribe(subscriber)</code> method is called when
        a client wishes to receive messages from a destination matching the
        Stomplet. The same instance of the subcriber parameter is passed to
        <code>on_unsubscribe(...)</code> when the client wishes to cease
        receiving messages and cancel that subscription.</para>

        <para>The subscriber object supports a few useful methods:</para>

        <para><itemizedlist>
            <listitem>
              <para><code>destination</code> String describing the desired
              destination to receive messages from.</para>
            </listitem>

            <listitem>
              <para><code>send(message)</code> Deliver a message to the client
              through this subscription.</para>
            </listitem>

            <listitem>
              <para><code>session</code> Access to the STOMP session (see
              below).</para>
            </listitem>
          </itemizedlist></para>
      </section>

      <section>
        <title><code>on_message(message, session)</code></title>

        <para>The on_message(message) method is called with a Stomp message
        and session as the parameters whenever a client sends a message to a
        destination handled by the Stomplet. The client does not necessarily
        need to have previous subscribed to the destination in order to send
        messages to it.</para>
      </section>

      <section>
        <title>Sessions</title>

        <para>If the Stomplets are run as part of a larger application which
        involves web components (Rack, Sinatra, Rails, etc), then the user's
        session will pass between the web and STOMP components. Values
        set on the session from a web-based controller will be visible within
        the scope of the Stomplet, and vice-versa.</para>

        <para>If the Stomplets are deployed without a web component, or using
        different virtual-host configuration, a STOMP-specific session will be
        used, providing communication between all of the Stomplets but
        independent from any web sessions.</para>
      </section>
    </section>

    <section>
      <title>Example</title>

      <para><programlisting>require 'torquebox-stomp'

class SimpleBroadcastStomplet

  def initialize()
    super
  end

  def configure(stomplet_config)
    super
  end

  def on_message(stomp_message, session)
    @subscribers.each do |subscriber|
      subscriber.send( stomp_message )
    end
  end

  def on_subscribe(subscriber)
    @subscribers &lt;&lt; subscriber
  end
 
  def on_unsubscribe(subscriber)
    @subscribers.delete( subscriber )
  end

end</programlisting></para>
    </section>
  </section>

  <section>
    <title>JMS Integration</title>

    <para>TorqueBox provides useful classes upon which you can build your own
    application's Stomplets. The most useful of these is
    <classname>TorqueBox::Stomp::JmsStomplet</classname>, which handles a
    large portion of bridging between STOMP and JMS, while allowing the
    flexibility to adapt the integration to match your particular
    needs.</para>

    <para>The primary assistance it provides is through two methods:</para>

    <para><itemizedlist>
        <listitem>
          <para>subscribe_to( subscriber, jms_destination, jms_selector=nil
          )</para>
        </listitem>

        <listitem>
          <para>send_to( jms_destination_name, stomp_message, headers={}
          )</para>
        </listitem>
      </itemizedlist>Your own Stomplet may use these methods to handle the
    heavy-lifting after translating between STOMP destinations and JMS
    destinations.</para>

    <para>When using <methodname>send_to(...)</methodname>, the
    <parameter>stomp_message</parameter> parameter may be a complete
    <classname>StompMessage</classname>, or simply a string, which will be
    converted into a message. Any headers specified will override any headers
    provided through the <classname>StompMessage</classname>.</para>

    <para><example>
        <title>Example JMS Stomplet Bridge</title>

        <para><programlisting>require 'torquebox-stomp'

class BridgeStomplet &lt; TorqueBox::Stomp::JmsStomplet

  def initialize()
    super
  end

  def configure(stomplet_config)
    super
   
    @destination_type = stomplet_config['type']
    @destination_name = stomplet_config['destination']
  end

  def on_message(stomp_message, session)
    send_to( destination_for( @destination_name, @destination_type ), stomp_message )
  end

  def on_subscribe(subscriber)
    subscribe_to( subscriber, destination_for( @destination_name, @destination_type ) )
  end

end</programlisting></para>
      </example></para>

    <section>
      <title>Destination and Message compatibility</title>

      <para>When using the <classname>JmsStomplet</classname> to bridge STOMP
      destinations to JMS destination, normal message-encoding occurs. This
      allows your application to send a message to a JMS destination using
      <classname>TorqueBox::Messaging</classname> interfaces as normal. The
      <classname>JmsStomplet</classname> will appropriately decode the
      messages received from the JMS destination. Likewise, any messages sent
      by the <classname>JmsStomplet</classname> will be appropriate encoded in
      order to be consumable by other non-STOMP
      <classname>MessageProcessors</classname>.</para>
    </section>
  </section>

  <section>
    <title>Deployment descriptors</title>

    <para>To deploy Stomplets with your application, a <code>stomp</code>
    section is added to your application's <filename>torquebox.yml</filename>
    or <filename>torquebox.rb</filename> descriptor. The section should
    contain named sections for each Stomplet your application needs to deploy.
    Each Stomplet is bound to a route, which works similar to Rails request
    routing, but matches against STOMP destinations instead of web URLs.
    Additionally, it specifies the class of the implementation, along with
    optional configuration in the form of name/value pairs of strings.</para>

    <para>STOMP supports the notion of virtuals, just as with web container.
    By default, if your application specifies a virtual host for the web
    portion of the configuration, the same value will be used for the STOMP
    container. The host may be overridden, though, by specifying a
    <parameter>host:</parameter> parameter within the
    <parameter>stomp:</parameter> block.</para>

    <para>To configure stomplets using the YAML syntax: <informalexample>
        <para><programlisting>stomp:
  host: somehost.com
  stomplets:
    stomplet.one:
      route: '/queues/:queue_name'
      class: StompletOne
    foo.stomplet:
      route: '/bridge/foo'
      class: BridgeStomplet
      config:
        type: queue
        destination: /jms-queues/foo
    bar.stomplet:
      route: '/bridge/bar'
      class: BridgeStomplet
      config:
        type: topic
        destination: /jms-topics/bar</programlisting></para>
      </informalexample> To configure stomplets via the DSL: <informalexample>
        <para><programlisting>TorqueBox.configure do
  ...
  stomp do
    host'somehost.com'
  end

  stomplet StompletOne do
    route '/queues/:queue_name'
  end

  stomplet BridgeStomplet do
    name 'foo.stomplet' # required if &gt;1 stomplets use the same class, optional otherwise
    route '/bridge/foo'
    config do
      type 'queue'
      destination '/jms-queues/foo'
    end
  end

  stomplet BridgeStomplet do
    name 'bar.stomplet' # required if &gt;1 stomplets use the same class, optional otherwise
    route '/bridge/bar'
    config :type =&gt; 'topic', :destination =&gt; '/jms-topics/bar'
  end
end</programlisting></para>
      </informalexample></para>
  </section>

  <section>
    <title>Javascript Client</title>

    <para>TorqueBox makes use of the Stilts framework and to implement the
    WebSockets and STOMP stack. TorqueBox includes the Javascript client
    provided by the Stilts distribution in the
    <filename>share/javascripts/</filename> directory. The client is derived
    from work by Jeff Mesnil.</para>

    <section>
      <title>Using the Javascript client</title>

      <para>The Javascript STOMP client isolates your application from the
      underlying WebSocket protocol. The Javascript client works purely in
      terms of STOMP semantics. The client is based around callbacks.</para>

      <section>
        <title>Instantiating a client</title>

        <para>The client is created using the
        <methodname>client(...)</methodname> method of the Javascript
        <classname>Stomp</classname> class. This method takes one parameter,
        being the URL of the server to connect to. Creating the client does
        not connect it.</para>

        <para>By default, the STOMP server runs on port 8675 and supports
        virtual hosts. To connect, a URL scheme of <code>ws://</code> should
        be used.</para>

        <para><informalexample>
            <programlisting>client = Stomp.client( "ws://localhost:8675" )</programlisting>
          </informalexample></para>
      </section>

      <section>
        <title>Connecting the client</title>

        <para>To connect the client to the STOMP server, use the connect(...)
        method, which takes three arguments: username, password and a callback
        function which will be invoked once the connection has been
        established.</para>

        <para>Currently the username and password parameters are ignored.
        Typical applications will authenticate the user through traditional
        web-based logins, storing an authorization token in the session, which
        is then available to each Stomplet to positively identify the
        connected client.</para>

        <para><informalexample>
            <programlisting>client.connect( "username", "password", function() {
    // executed once successfully connected
} );</programlisting>
          </informalexample>Once connected, the callback function will be
        invoked. Other client methods should be used from within this method
        or other callbacks.</para>
      </section>

      <section>
        <title>Sending a message</title>

        <para>Messages may be sent to any destination supported by your
        Stomplets, even without prior subscription to the same
        destination.</para>

        <para>The client's <methodname>send(...)</methodname> method is used
        to deliver a payload with headers to the destination.</para>

        <para><informalexample>
            <programlisting>client.send( "/some/destination", { header1: 'Header 1' }, "this is the payload" );</programlisting>
          </informalexample>Messages sent this way will by processed by the
        <methodname>on_message(...)</methodname> method of the Stomplet bound
        to the destination.</para>
      </section>

      <section>
        <title>Subscribing to destinations</title>

        <para>A client can subscribe to STOMP destinations using the
        subscribe(...) method, passing the destination and a message-handling
        callback function as parameters. Any message delivered to the client
        on the destination will invoke the function with the message as the
        argument. The message provides access to the body and hash of headers,
        along with an <methodname>ack()</methodname> method to acknowledge
        receipt, if required.</para>

        <para><informalexample>
            <programlisting>client.subscribe( "/some/destination", function(message) {
  // message.body 
  // message.headers['header1']
} );</programlisting>
          </informalexample></para>
      </section>

      <section>
        <title>Working with transactions</title>

        <para>The STOMP protocol defines transactional semantics, and several
        transactions may concurrently be in use at the same time.</para>

        <formalpara>
          <title>Starting a transaction</title>

          <para>The <methodname>begin()</methodname> method is used to start a
          transaction. It returns a transaction identifier which may be used
          to associate other activities with the transaction.</para>
        </formalpara>

        <formalpara>
          <title>Committing a transaction</title>

          <para>To commit the work performed within the scope of a particular
          transaction, the <methodname>commit(...)</methodname> method is
          used, passing the transaction identifier provided by
          <methodname>begin()</methodname> as the only parameter.</para>
        </formalpara>

        <formalpara>
          <title>Aborting a transaction</title>

          <para>To cancel the work performed within the scope of a particular
          transaction, the <methodname>abort(...)</methodname> method is used,
          passing the transaction identifier provided by
          <methodname>begin()</methodname> as the only parameter.</para>
        </formalpara>

        <formalpara>
          <title>Sending messages within a transaction</title>

          <para>To send a message within a transaction, a
          <parameter>transaction</parameter> header should be added, with its
          value being the transaction identifier returned by a previous call
          to <methodname>begin()</methodname>.</para>
        </formalpara>

        <informalexample>
          <programlisting>tx = client.begin();

client.send( "/some/destination", { transaction: tx }, "this is a transactional message" );

if ( everyoneIsHappy ) {
  client.commit( tx ); 
} else {
  client.abort( tx );
}</programlisting>
        </informalexample>
      </section>
    </section>

    <section>
      <title>Rack middleware to provide the Javascript client</title>

      <para>The torquebox-stomp gem includes a simple Rack middleware to make
      it easy to serve the Javascript client with your application, without
      having to copy it into your application's source tree. The
      <classname>TorqueBox::Stomp::StompJavascriptClientProvider</classname>
      middleware matches requests to <filename>/stilts-stomp.js</filename> and
      serves the Javascript client.<informalexample>
          <para><programlisting>require 'torquebox-stomp'

app = lambda { |env| 
  # your app here
}

use TorqueBox::Stomp::StompJavascriptClientProvider
run app</programlisting></para>
        </informalexample></para>
    </section>

    <section>
      <title>Injecting the endpoint URL</title>

      <para>In your application, it's useful to be able to know exactly the
      STOMP server's endpoint URL, without having to hard-code that into your
      application. Since JBoss AS allows changing ports for services,
      especially when running multiple nodes on the same machine, determining
      the URL at runtime is helpful.</para>

      <para>You may inject stomp-endpoint into your application code, and it
      will provide the the URL for connections to the STOMP endpoint on that
      node.</para>

      <para><informalexample>
          <para><programlisting>my_url = fetch( 'stomp-endpoint' )</programlisting></para>
        </informalexample>The URL may be used with the middleware-provided
      Javascript client to conveniently and reliably connect back to the STOMP
      endpoint, regardless of the current port being used.</para>
    </section>
  </section>

  <section>
    <title>Other Clients (without WebSockets)</title>

    <para>The Stilts distribution also includes JRuby-based clients and Java
    clients appropriate for communicating with the TorqueBox STOMP service.
    While STOMP is offered over WebSockets, the same service, on the same port
    (8675) provides bare STOMP also, for clients not requiring a WebSockets
    transport. The JRuby and Java clients can seamlessly communicate with the
    TorqueBox STOMP server using either TCP/IP, or WebSockets as the
    underlying transport.</para>
  </section>

  <section>
    <title>Further information</title>

    <para>TorqueBox uses the Stilts project to provide the WebSockets and
    STOMP stack. The Stilts project also defines the Stomplet API. Additional
    clients are available directly from the Stilts project. Rest assured,
    Stilts is written by the same people who write TorqueBox.</para>

    <para><uri>http://stilts.projectodd.org/</uri></para>
  </section>
</chapter>
