The dukeetf2 Example Application (original) (raw)
The dukeetf2
example application, located in the tut-install`/examples/web/websocket/dukeetf2/` directory, demonstrates how to use a WebSocket endpoint to provide data updates to web clients. The example resembles a service that provides periodic updates on the price and trading volume of an electronically traded fund (ETF).
The following topics are addressed here:
Architecture of the dukeetf2 Sample Application
The dukeetf2
example application consists of a WebSocket endpoint, an enterprise bean, and an HTML page.
- The endpoint accepts connections from clients and sends them updates when new data for price and trading volume becomes available.
- The enterprise bean updates the price and volume information once every second.
- The HTML page uses JavaScript code to connect to the WebSocket endpoint, parse incoming messages, and update the price and volume information without reloading the page.
The Endpoint
The WebSocket endpoint is implemented in the ETFEndpoint
class, which stores all connected sessions in a queue and provides a method that the enterprise bean calls when there is new information available to send:
@ServerEndpoint("/dukeetf")
public class ETFEndpoint {
private static final Logger logger = Logger.getLogger("ETFEndpoint");
/* Queue for all open WebSocket sessions */
static Queue<Session> queue = new ConcurrentLinkedQueue<>();
/* PriceVolumeBean calls this method to send updates */
public static void send(double price, int volume) {
String msg = String.format("%.2f / %d", price, volume);
try {
/* Send updates to all open WebSocket sessions */
for (Session session : queue) {
session.getBasicRemote().sendText(msg);
logger.log(Level.INFO, "Sent: {0}", msg);
}
} catch (IOException e) {
logger.log(Level.INFO, e.toString());
}
}
...
}
The lifecycle methods of the endpoint add and remove sessions to and from the queue:
@ServerEndpoint("/dukeetf")
public class ETFEndpoint {
...
@OnOpen
public void openConnection(Session session) {
/* Register this connection in the queue */
queue.add(session);
logger.log(Level.INFO, "Connection opened.");
}
@OnClose
public void closedConnection(Session session) {
/* Remove this connection from the queue */
queue.remove(session);
logger.log(Level.INFO, "Connection closed.");
}
@OnError
public void error(Session session, Throwable t) {
/* Remove this connection from the queue */
queue.remove(session);
logger.log(Level.INFO, t.toString());
logger.log(Level.INFO, "Connection error.");
}
}
The Enterprise Bean
The enterprise bean uses the timer service to generate new price and volume information every second:
@Startup
@Singleton
public class PriceVolumeBean {
/* Use the container's timer service */
@Resource TimerService tservice;
private Random random;
private volatile double price = 100.0;
private volatile int volume = 300000;
private static final Logger logger = Logger.getLogger("PriceVolumeBean");
@PostConstruct
public void init() {
/* Initialize the EJB and create a timer */
logger.log(Level.INFO, "Initializing EJB.");
random = new Random();
tservice.createIntervalTimer(1000, 1000, new TimerConfig());
}
@Timeout
public void timeout() {
/* Adjust price and volume and send updates */
price += 1.0*(random.nextInt(100)-50)/100.0;
volume += random.nextInt(5000) - 2500;
ETFEndpoint.send(price, volume);
}
}
The HTML Page
The HTML page consists of a table and some JavaScript code. The table contains two fields referenced from JavaScript code:
<!DOCTYPE html>
<html>
<head>...</head>
<body>
...
<table>
...
<td id="price">--.--</td>
...
<td id="volume">--</td>
...
</table>
</body>
</html>
The JavaScript code uses the WebSocket API to connect to the server endpoint and to designate a callback method for incoming messages. The callback method updates the page with the new information.
var wsocket;
function connect() {
wsocket = new WebSocket("ws://localhost:8080/dukeetf2/dukeetf");
wsocket.onmessage = onMessage;
}
function onMessage(evt) {
var arraypv = evt.data.split("/");
document.getElementById("price").innerHTML = arraypv[0];
document.getElementById("volume").innerHTML = arraypv[1];
}
window.addEventListener("load", connect, false);
The WebSocket API is supported by most modern browsers, and it is widely used in HTML5 web client development.
Running the dukeetf2 Example Application
This section describes how to run the dukeetf2
example application using NetBeans IDE and from the command line.
The following topics are addressed here:
- To Run the dukeetf2 Example Application Using NetBeans IDE
- To Run the dukeetf2 Example Application Using Maven
To Run the dukeetf2 Example Application Using NetBeans IDE
- Make sure that GlassFish Server has been started (seeStarting and Stopping GlassFish Server).
- From the File menu, choose Open Project.
- In the Open Project dialog box, navigate to:
tut-install/examples/web/websocket
- Select the
dukeetf2
folder. - Click Open Project.
- In the Projects tab, right-click the
dukeetf2
project and select Run.
This command builds and packages the application into a WAR file (dukeetf2.war
) located in thetarget/
directory, deploys it to the server, and launches a web browser window with the following URL:
http://localhost:8080/dukeetf2/
Open the same URL on a different web browser tab or window to see how both pages get price and volume updates simultaneously.
To Run the dukeetf2 Example Application Using Maven
- Make sure that GlassFish Server has been started (seeStarting and Stopping GlassFish Server).
- In a terminal window, go to:
tut-install/examples/web/websocket/dukeetf2/
- Enter the following command to deploy the application:
- Open a web browser window and enter the following URL:
http://localhost:8080/dukeetf2/
Open the same URL on a different web browser tab or window to see how both pages get price and volume updates simultaneously.