Tutorial

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

 

The Composite Components

Recall that JSF composite components consist of two areas:

  • interface section
  • implementation section

The interface section is where you define the usage contract for the component. It is where you define the attributes that a page author would use with your component. The implementation section is the code that produces the component. It may consist of HTML markup, JavaScript and/or other JSF components. Let's take a look at the composite components that are used in this application.

The SSE Composite Component

The Server Sent Events (SSE) composite component is used to establish a connection to a server endpoint. As mentioned earlier, this component has two attributes. Let's see how this component is implemented.

Listing 3: sse.xhtml SSE Composite Component

 

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:composite="http://java.sun.com/jsf/composite">
<head>

<title>Server Sent Event Composite Component</title>

</head>

<body>

<composite:interface>
   <composite:attribute name="url" required="true"/>
   <composite:attribute name="events" required="false"/>
</composite:interface>

<composite:implementation>
   <h:outputScript name="js/jsf-sse.js" target="head" />
   <script type="text/javascript">
        JSF.sse.connect("#{cc.attrs.url}", #{cc.attrs.events});
   </script>
</composite:implementation>

</body>

</html>

 

Let's take a look at the relevant sections of this component.

Lines 4 – 7: This is just our namespace declarations. Line 7 is declaring a composite namespace that will be used to define the relevant sections of our composite component.

Lines 16 – 19: This is where we define the interface or usage contract for our composite component. On line 17 we define a required url attribute whose value will be the server endpoint. On line 18 we define the events attribute whose value will contain event type : event handler name mappings as shown earlier.

Lines 21 – 26: This is the implementation of the composite component. JavaScript and JSF work together nicely to create dynamic components. On line 22, we are loading a small JavaScript library that contains functions for performing SSE operations for JSF. Notice that we're using a JSF component <h:outputScript> to specify the target location of the JavaScript file. On line 24 we are using a function from the JSF SSE JavaScript library to establish a connection to the server endpoint using the url and events attribute values. 

So, the idea is that you can drop one of these components in your JSF view to connect to a server endpoint. Let's take a look at the JavaScript library that contains the functions for performing SSE actions.

Listing 4: jsf-sse.js The JSF SSE JavaScript Library

 

1.  if (!JSF) {
2.  
3.      /**
4.       * The top level global namespace for JavaServer Faces Server Sent Event
5.       * functionality. 
6.       * @name JSF
7.       * @namespace
8.       */
9.      var JSF = {};
10.
11.     /**
12.      * The namespace for Server Sent Event functionality. 
13.      * @name JSF.sse
14.      * @namespace
15.      * @exec
16.      */
17.     JSF.sse = function() {
18.
19.         var eventSource = null;
20. 
21.         var getEventSource = function getEventSource(url) {
22.             url = 'http://' + document.location.host + url;
23.             eventSource = new EventSource(url);
24.         };
25.
26.         return {
27.
28.             /**
29.              * Connect to a server end point. 
30.              * <p><b>Usage:</b></p>
31.              * <pre><code>
32.              * JSF.sse.connect(url, {events: 'stock:stockHandler time:clockHandler'});
33.              * ... 
34.              * function stockHandler(event) {
35.              * ... 
36.              * }
37.              * </pre></code>
38.              *
39.              * @member JSF.sse
40.              * @param url The URL of a valid server end point that will deliver messages. 
41.              * @param eventOptions The set of events consisting of event name:handler name pairs
42.              *   associating an event name from the server, and the name of the handler that
43.              *   will process the event.. 
44.              */
45.             connect: function connect(url, eventOptions) {
46.                 if (url === null) {
47.                     throw new Error("Must specify URL");
48.                 }
49.                 getEventSource(url);
50.                 if (eventSource !== null) {
51.                     if (eventOptions !== null) {
52.                         for (var i in eventOptions) {
53.                            JSF.sse.addOnEvent(i, eventOptions[i]);
54.                         }
55.                     }
56.                }
57.
58.             },
59. 
60.             /**
61.              * Register a callback for error handling. 
62.              * <p><b>Usage:</b></p>
63.              * <pre><code>
64.              * JSF.sse.addOnError(handleError); 
65.              * ... 
66.              * var handleError = function handleError(data) {
67.              * ... 
68.              * }
69.              * </pre></code>
70.              *
71.              * @member JSF.sse
72.              * @param callback a reference to a function to call on an error
73.              */
74.             addOnError: function addOnError(callback) {
75.                 if (eventSource !== null) {
76.                     if (typeof callback === 'function') {
77.                         eventSource.addEventListener('error', callback, false);
78.                     }
79.                 }
80.
81.             },
82.
83.             /**
84.              * Register a callback for event handling. 
85.              * <p><b>Usage:</b></p>
86.              * <pre><code>
87.              * JSF.sse.addOnEvent('timeEvent', handleEvent); 
88.              * ... 
89.              * var handleEvent = function handleEvent(data) {
90.              * ... 
91.              * }
92.              * </pre></code>
93.              *
94.              * @member JSF.sse
95.              * @param eventName the event name associated with the message. 
96.              * @param callback a reference to a function to call for processing messages with a specific event
97.              * name. 
98.              */
99.             addOnEvent: function addOnEvent(eventName, callback) {
100.                if (eventSource !== null) {
101.                     if (typeof callback === 'function' && eventName !== null
102.                           && typeof eventName !== 'undefined') {
103.                           eventSource.addEventListener(eventName, callback, false);
104.                     }
105.                }
106.
107.            }
108.         };
109.
110.     }();
111. } 

 

Line 9: JSF namespace declaration.

Line 21: A private function that creates an EventSource instance to connect to the server endpoint specified by the provided url.

Line 45: The public connect function that is used in the SSE composite component in Error: Reference source not found. This function calls through to the private function getEventSource and registers the application callback functions using a public addOnEvent function (line 99).

Line 74: A public utility function for registering an error processing callback function. With SSE you can register a function with the error event type.

Line 99: A public utility function for registering callback functions for processing event types.

The Clock Composite Component

The clock composite component is used to display the current time.

Listing 5: clock.html The Clock 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.
11.   </composite:interface>
12.
13.   <composite:implementation>
14.      #{timeBean.time}
15.     <div id="clock" style="font-size: 20px; border:2px solid blue"/>
16.     <h:outputScript name="js/app.js" target="head"/>
17.
18.   </composite:implementation>
19.
20. </html>

 

Lines 4 – 7 : Namespace declarations

Lines 9 – 11 : Notice this component has no attributes – which is perfectly fine. If we wanted to make this clock component fancier we could, for example, define an attribute to indicate that our clock could display in analog or digital mode.

Lines 13 – 18: The implementation of our component. On line 14, we are using the Expression Language (EL) to invoke a method on a managed bean to start the clock ticking. The managed bean could just utilize a java.util.Timer method to compose and send the current date/time string back to the client. Line 15 is a placeholder for where we will display the time event data from the server. On Line 16, we load a JavaScript file that contains application callback functions that will process the event data returned from the server. Let's take a look at the relevant section of code from this application JavaScript file for this clock component.

Listing 6: Partial JavaScript Listing for Stock Ticker Component

1.  /**
2.   * Clock message handling function. 
3.   */
4.  function clockHandler(event) {
5.      var eventData = event.data.toString().replace(/^\s*/,"").replace(/\s*$/,"");
6.      var clock = document.getElementById("clock");
7.      clock.innerHTML = eventData;
8.  }

Line 5: We obtain the event data

Line 6: We obtain our div placeholder element (created in Error: Reference source not found, line 15).

Line 7: We write the event data string (which is the current data / time)

 

Pages

Roger Kitain
Roger Kitain

What do you think?

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

Comments

Latest opinions