The Trade Monitor has a client that allows dynamic subscriptions to trade summary information for a stock symbol. The data is random buys and sells generated on the server by a timer. The example has GWT & Dojo Cometd integration on the client and Spring Bayeux integration on the server. The GWT part of the client handles display and client interaction, but calls out to external JavaScript to let Dojo Cometd handle Bayeux publish/subscribe on the client. The Spring by Example Web Module is used for it's Bayeux support to configure the Bayeux Trade Monitor Service.
Please see the Spring Bayeux GWT Chat Webapp example for a brief explanation of Comet and Bayeux, and also to see a simpler Bayeux service example.
![]()  | Note | 
|---|---|
Currently this example only runs on Jetty since it uses the Bayeux implementation provided by Jetty. Tomcat also has Comet support and it should be easy to port a Bayeux implementation to it if one doens't already exist. There isn't currently a standard API to suspend and resume HTTP requests, but the Servlet 3.0 Draft Specification is standardizing this. Hopefully it will be finalized relatively soon and most major servlet engines will have a Servlet 3.0 implementation. Jetty already has a pre-release 7.0 version implementing the Servlet 3.0 specification as it currently is.  | 
                This is basically identical to the Spring Bayeux GWT Chat Webapp's 
                web.xml.  The Spring JS ResourceServlet is defined to serve JavaScript files 
                like Dojo and the Dojox library.  The SpringContinuationCometdServlet handles 
                Bayeux publish and subscribe requests and is mapped to '/cometd/*'.
            
                    
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" 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">
    <display-name>monitor</display-name>
    
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/web-application-context.xml
        </param-value>
    </context-param>
    <filter>
        <filter-name>encoding-filter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- Serves static resource content from .jar files such as spring-faces.jar -->
    <servlet>
        <servlet-name>resources</servlet-name>
        <servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>cometd</servlet-name>
        <servlet-class>org.springbyexample.cometd.continuation.SpringContinuationCometdServlet</servlet-class>
        <init-param>
            <param-name>asyncDeliver</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
        
    <servlet>
        <servlet-name>trade-monitor</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value></param-value>
		</init-param>
		<load-on-startup>2</load-on-startup>
    </servlet>
    
    <!-- Map all /resources requests to the Resource Servlet for handling -->
    <servlet-mapping>
        <servlet-name>resources</servlet-name>
        <url-pattern>/resources/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>cometd</servlet-name>
        <url-pattern>/cometd/*</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>trade-monitor</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
    
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>
                    
                
                The context:component-scan registers the bayeux server TradeMonitorService and 
                the bayeux bean configures the Bayeux instance used by the Bayeux services and servlet.
            
                    
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="org.springbyexample.web.cometd" />
    
    <bean id="bayeux" 
          class="org.springbyexample.cometd.continuation.SpringContinuationBayeux"
          p:timeout="300000"
          p:interval="0"
          p:maxInterval="10000"
          p:multiFrameInterval="2000"
          p:logLevel="0"
          p:directDeliver="true">
    </bean>
</beans>
                    
                The Trade Monitor Bayeux Service is just a test class that randomly generates different buys and sells for any registered symbols, keeps track of the summary information for each symbol, and publishes the summary information to anyone subscribed to the symbols channel.
                The TradeMonitorService is annotated with @Component so it is 
                registered as a bean by the context:component-scan in the bayeux-context.xml.
                It's constructor is marked with @Autowired the Bayeux created in the XML configuration file 
                will be injected into the constructor.  There is also an initialization method with @PostConstruct on it 
                that means it should be called during Spring's initialization lifecycle.  The init() adds the 
                starting prices and ranges for symbols.  It also initializes the timer to broadcast the server list used for 
                the menu and also to publish the randomly generated trades.
            
Example 1. TradeMonitorService Initialization
Excerpt from src/main/java/org/springbyexample/web/cometd/monitor/TradeMonitorService.java
                    
@Component
public class TradeMonitorService extends BayeuxService {
    
    ...
    /**
     * Constructor
     */
    @Autowired
    public TradeMonitorService(Bayeux bayeux) {
        super(bayeux, "monitor");        
    }
    
    /**
     * Init sending monitor test messages.
     */
    @PostConstruct
    protected void init() {
        Bayeux bayeux = getBayeux();
        final Channel serversChannel = bayeux.getChannel("/monitor/servers", true);
        final Client client = getClient();
        addChannel(NYSE_GATEWAY, ATT_SYMBOL, "AT&T", 24, 20, 25);
        addChannel(NYSE_GATEWAY, GM_SYMBOL, "General Motors", 5, 5, 9);
        addChannel(NYSE_GATEWAY, IBM_SYMBOL, IBM_SYMBOL, 80, 80, 100);
        addChannel(NYSE_GATEWAY, MS_SYMBOL, "Morgan Stanley", 17, 10, 26);
        addChannel(NYSE_GATEWAY, NYX_SYMBOL, "NYSE Euronext", 27, 23, 32);
        addChannel(NYSE_GATEWAY, PG_SYMBOL, "Proctor & Gamble", 63, 57, 64);
        addChannel(NASDAQ_GATEWAY, JAVA_SYMBOL, "Sun Microsystems, Inc.", 4, 4, 12);
        addChannel(NASDAQ_GATEWAY, ORCL_SYMBOL, "Oracle Corporation", 17, 16, 23);
        addChannel(NASDAQ_GATEWAY, GOOG_SYMBOL, "Google Inc.", 331, 330, 510);
        addChannel(NASDAQ_GATEWAY, MSFT_SYMBOL, "Microsoft Corporation", 21, 21, 28);
        addChannel(NASDAQ_GATEWAY, YHOO_SYMBOL, "Yahoo! Inc.", 12, 12, 20);
        // start now, publish every 10 seconds
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                publishServerList(serversChannel, client);  
            }
        }, 0, 5000); 
        
        // start now, publish every second
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                publishTrades(client);  
            }
        }, 0, 200);
    }
    ...
}
                    
                
                Trades are randomly generated, occasionally skipping some symbols, with a volume of 500-1000 and a price 
                movement of up to a dollar.  The trade also randomly generates whether or not it is a buy or a sell.
                The SummaryInfo will change the buy to a sell or a sell to a buy if the symbol 
                has passed it's range set during initialization.
            
Example 2. TradeMonitorService Publish Trades
Excerpt from src/main/java/org/springbyexample/web/cometd/monitor/TradeMonitorService.java
                    
protected void publishTrades(Client client) {
    Random random = new Random();
    for (Channel channel : hTrades.keySet()) {
        SummaryInfo summary = hTrades.get(channel);
        // randomly skip a symbols trade if it's not IBM, P&G, or Google
        if (IBM_SYMBOL.equals(summary.getSymbol()) || 
            PG_SYMBOL.equals(summary.getSymbol()) ||
            GOOG_SYMBOL.equals(summary.getSymbol()) ||
            random.nextInt(5) < 4) {
            int volume = 500 + random.nextInt(500);
            double price = random.nextDouble();
            
            boolean buy = random.nextBoolean();
            summary.increment(volume, price, buy);  
            
            channel.publish(client, summary.getTradeSummaryMap(), null);  
        }
    }
}