Tutorial

Tutorial: JSF 2 and HTML5 Server Sent Events - Part 3

 

The Stock Composite Component

Listing 7: stock.hmtl : Stock Ticket Composite Component

 

1.  <!DOCTYPE html>
2.
3.  <html xmlns="http://www.w3.org/1999/xhtml"
4.    xmlns:h="http://java.sun.com/jsf/html"
5.    xmlns:f="http://java.sun.com/jsf/core"
6.    xmlns:ui="http://java.sun.com/jsf/facelets"
7.    xmlns:ez="http://java.sun.com/jsf/composite/stockinfo"
8.    xmlns:composite="http://java.sun.com/jsf/composite">
9.
10.  <composite:interface>
11.
12.  </composite:interface>
13.
14.  <composite:implementation>
15.    <br/>
16.    <h:panelGrid columns="4">
17.       <h:outputText value="Space delimited symbol(s):" style="font-size: 18px;"/>
18.       <h:inputText id="symbol" value="#{stockTickerBean.symbols}"/>
19.       <h:commandButton id="submit" value="Get Quotes" action="#{stockTickerBean.getStockInfo}">
20.          <f:ajax execute="@this symbol"/>
21.       </h:commandButton>
22.       <h:commandButton id="clear" value="Reset" action="#{stockTickerBean.reset}">
23.          <f:ajax execute="@this symbol" render="symbol"/>
24.       </h:commandButton>
25.    </h:panelGrid>
26.
27.    <br/>
28.
29.    <div id="stockInfo" style="font-size: 16px;">
30.       <table id="stockTable" border="8" bordercolor="#000080" cellspacing="10" style="visibility:hidden">
31.          <tr>
32.             <th style="background-color:#b0c4de;text-align:left">Symbol</th><th style="background-color:#b0c4de;text-align:left">Open</th>
33.             <th style="background-color:#b0c4de;text-align:left">Trade</th><th style="background-color:#b0c4de;text-align:left">Chg</th>
34.          </tr>
35.        </table>
36.    </div>
37.
38.    <br/>
39.
40.    <ez:rss symbols="#{stockTickerBean.symbols}"/>
41.
42.    <h:outputScript name="js/app.js" target="head"/>
43.
44.  </composite:implementation>
45.
46. </html>

 

This composite component's implementation uses standard JSF components.

Line 18: The input text field that will store the entered stock symbol data to a managed bean.

Lines 19 – 21: The button that, when activated, will fire a request over Ajax to invoke a method getStockInfo on a managed bean. This method could utilize a java.util.Timer method to reach out to a stock quote service periodically, and send the event data back to the client.

Lines 29 – 36: This is the placeholder area for our dynamic table to display the stock data from the server.

Line 40: This component uses the rss composite component (more about that next).

Listing 8: Partial JavaScript Listing for Stock Ticker Component

 

1.  /**
2.   * Server Sent Event message handling function dynamically builds a table
3.   * based on the message data. 
4.   */
5.  function stockHandler(event) {
6.      var eventData = event.data.toString().replace(/^\s*/,"").replace(/\s*$/,"");
7.      var stockTable = document.getElementById("stockTable");
8.      var rowCount = stockTable.rows.length - 1;
9.      if (eventData.length == 0) {
10.          stockTable.style.visibility="hidden";
11.          clearTableRows(stockTable, rowCount);
12.          return;
13.    }
14.    var quotes = eventData.split(" ");
15.    if (quotes.length !== rowCount || quotes.length == 0) {
16.         clearTableRows(stockTable, rowCount);
17.    }
18.    if (quotes !== null) {
19.         stockTable.style.visibility="visible";
20.         var newRow, newCell;
21.         for (var i = 0; i < quotes.length; i++) {
22.              var fields = quotes[i].split(":");
23.              if (document.getElementById(fields[0]) !== 'undefined' &&
24.                   document.getElementById(fields[0]) !== null) {
25.                   document.getElementById(fields[0]).innerHTML = fields[0];
26.                   document.getElementById(fields[0]+1).innerHTML = fields[1];
27.                   document.getElementById(fields[0]+2).innerHTML = fields[2];
28.                   if (fields[3] == "UP") {
29.                       document.getElementById(fields[0]+3).innerHTML =
30.                           "<img src='resources/up_g.gif'/>";
31.                   } else if (fields[3] == "DOWN") {
32.                       document.getElementById(fields[0]+3).innerHTML =
33.                           "<img src='resources/down_r.gif'/>";
34.                   } else {
35.                       document.getElementById(fields[0]+3).innerHTML = "";
36.                   }
37.               } else {
38.                   newRow = stockTable.insertRow(stockTable.rows.length);
39.                   newCell = newRow.insertCell(newRow.cells.length);
40.                   newCell.id = fields[0];
41.                   newCell.innerHTML = fields[0];
42.                   newCell.align = "center";
43.                   newCell.width = "20%";
44.                   newCell = newRow.insertCell(newRow.cells.length);
45.                   newCell.id = fields[0]+1;
46.                   newCell.innerHTML = fields[1];
47.                   newCell.align = "center";
48.                   newCell.width = "20%";
49.                   newCell = newRow.insertCell(newRow.cells.length);
50.                   newCell.id = fields[0]+2;
51.                   newCell.innerHTML = fields[2];
52.                   newCell.align = "center";
53.                   newCell.width = "20%";
54.                   newCell = newRow.insertCell(newRow.cells.length);
55.                   newCell.id = fields[0]+3;
56.                   if (fields[3] == "UP") {
57.                       document.getElementById(fields[0]+3).innerHTML =
58.                           "<img src='resources/up_g.gif'/>";
59.                   } else if (fields[3] == "DOWN") {
60.                       document.getElementById(fields[0]+3).innerHTML =
61.                           "<img src='resources/down_r.gif'/>";
62.                   } else {
63.                       document.getElementById(fields[0]+3).innerHTML = "";
64.                   }
65.
66.                   newCell.align = "center";
67.                   newCell.width = "20%";
68.            }
69.        }
70.    }
71.}

 

The stockHandler function receives the event data, that is the stock quote information and performs some JavaScript magic to dynamically build and display the stock table.

The RSS Composite Component

The rss composite component displays a link, that when activated, will dynamically display stock company news relevant to the stock symbols that were entered.

Listing 9: rss.xhtml : RSS Composite Component

1.  <!DOCTYPE html>
2. 
3.  <html xmlns="http://www.w3.org/1999/xhtml"
4.        xmlns:h="http://java.sun.com/jsf/html"
5.        xmlns:f="http://java.sun.com/jsf/core"
6.        xmlns:ui="http://java.sun.com/jsf/facelets"
7.        xmlns:composite="http://java.sun.com/jsf/composite">
8.
9.    <composite:interface>
10.       <composite:attribute name="symbols" required="true"/>
11.   </composite:interface>
12.
13.   <composite:implementation>
14.   <br/>
15.   <h:panelGrid columns="4">
16.       <h:commandLink id="submit" value="Get Stock News" action="#{rssBean.getStockNews}">
17.          <f:setPropertyActionListener value="#{cc.attributes.symbols}" target="#{rssBean.symbols}"/>
18.          <f:ajax execute="@this"/>
19.       </h:commandLink>
20.    </h:panelGrid>

21.   <br/>
22.
23.    <div id="rssInfo" style="font-size: 16px; border:2px solid blue; height : 150px; overflow : auto; visibility:hidden"/>
24.
25.    <h:outputScript name="js/app.js" target="head"/>
26.
27.
28.  </composite:implementation>
29.
30. </html>

 

Line 10: This component defines an attribute for accepting stock symbols. The stock symbols are used to get the rss information.

Lines 16 – 19: The link, that when activated, will propagate the stock symbols to a managed bean and invoke the method getStockNews on the managed bean over Ajax.

Line 23: A div placeholder for displaying the returned rss information from the server.

Listing 10: Partial JavaScript Listing for RSS Composite Component

1.  function rssHandler(event) {
2.      var eventData = event.data.toString().replace(/^\s*/,"").replace(/\s*$/,"");
3.      var rssInfo = document.getElementById("rssInfo");
4.      if (eventData.length == 0) {
5.          rssInfo.style.visibility="hidden";
6.      } else {
7.          rssInfo.style.visibility="visible";
8.      }
9.      rssInfo.innerHTML="";
10.    rssInfo.innerHTML = eventData; 
11. }

 

The rssHandler callback function writes the rss event data into the div that was created in Error: Reference source not found.

Server Implementation Considerations

In the browser that has SSE support. section, we mentioned some of the requirements for sending SSE event data to the client. Server implementations need to maintain an open connection to stream event data to the client. For Java implementations, a Servlet utilizing the Servlet 3.0 Asynchronous API is one choice. For other implementations, NodeJS or PHP could be used as well. Server implementation details are not mentioned, since the main focus of this article is the JSF user interface and components.

Conclusion

This article has been all about creating JSF composite components that make up the user interface for a stock quote application that uses HTML5 Server Sent Events. JSF 2 introduced the world to composite components, and together with JavaScript, provides a component author with a multitude of options for creating dynamic components. HTML5 Server Sent Events are the “second class citizen” to HTML5 WebSockets, but it does offer an HTTP solution to event streaming without switching to another protocol.

 

Roger Kitain is a Principal Software Engineer at Oracle where he has spent most of his time in the web technology space since 1997. Roger has been involved with the JavaServer Faces technology since its inception, and has co-led the JavaServer Faces Specification since JSF 1.2. He has presented at many conferences including JAX, JSF Summit, JavaOne, Dexoxx, Jazoon, Ajax Experience, Ajax World.  

 

This article first appeared in Java Tech Journal : Web Frameworks. For more of that issue, click here

 

Pages

Roger Kitain
Roger Kitain

What do you think?

JAX Magazine - 2014 - 05 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions