JavaEE WebSockets and Periodic Message Delivery

﻿
For a project I had the need to implement a monitoring functionality based on HTML5 and WebSockets. It is quite trivial with JavaEE 7, as I will explain below.

Let us assume the easy requirement of a simple monitoring which sends periodic status information to web clients. The web client shall show the information on a web page (inside a <div>…</div> for instance). For that scenario, the technical details are shown below…

The JavaScript code is quite easy and can be taken from JavaScript WebSocket books and tutorials. (A good introduction is for instance Java WebSocket Programming by Oracle Press). A simple client might look like:

JavaScript WebSocket Client

JavaScript

1

2

3

4

5

varwebsocket=newWebSocket("ws://<host>:<port>/socket");

websocket.onmessage=function(event){

varoutputElement=document.getElementById("output");

outputElement.innerHTML=event.data;

}

The functions for onopen, onclose and onerror are neglected, because we want to focus on JavaEE. The important stuff is shown above: We connect with new WebSocket to the URL which shall provide the periodic updated and with onmessage we put the data somewhere into our web page. That’s it from the client site.

For JavaEE, there is a lot of documentation which shows how to create @ServerEndpoint classes. For instance:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

importjavax.websocket.CloseReason;

importjavax.websocket.EndpointConfig;

importjavax.websocket.OnClose;

importjavax.websocket.OnError;

importjavax.websocket.OnMessage;

importjavax.websocket.OnOpen;

importjavax.websocket.Session;

importjavax.websocket.server.ServerEndpoint;

@ServerEndpoint(value="/server",//

encoders={...},//

decoders={...}//

)

publicclassPurifinityServerSocket{

privateSession session=null;

@OnOpen

publicvoidopen(Session session,EndpointConfig config){

this.session=session;

}

@OnClose

publicvoidclose(CloseReason reason){

}

@OnMessage

publicServerStatus getServerStatus(

StatusRequest request){

return<status message>;

}

@OnError

publicvoidhandleError(Throwable throwable){

}

}

But, how to make it send periodic messages easily? After some testing on WildFly 8.2, I came to this simple solution:

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

importjavax.ejb.Schedule;

importjavax.ejb.Singleton;

importjavax.websocket.CloseReason;

importjavax.websocket.EncodeException;

importjavax.websocket.EndpointConfig;

importjavax.websocket.OnClose;

importjavax.websocket.OnError;

importjavax.websocket.OnMessage;

importjavax.websocket.OnOpen;

importjavax.websocket.Session;

importjavax.websocket.server.ServerEndpoint;

@ServerEndpoint(value="/server",//

encoders={...},//

decoders={...}//

)

@Singleton

publicclassPurifinityServerSocket{

privateSession session=null;

@OnOpen

publicvoidopen(Session session,EndpointConfig config){

this.session=session;

}

@OnClose

publicvoidclose(CloseReason reason){

}

@OnMessage

publicServerStatus getServerStatus(

StatusRequest request){

return<status message>;

}

@OnError

publicvoidhandleError(Throwable throwable){

}

@Schedule(hour="*",minute="*",second="*/5")

publicvoidperiodicUpdate(){

if(session!=null){

ServerStatus status=<server status information>;

for(Session client:session.getOpenSessions()){

try{

client.getBasicRemote().sendObject(status);

}catch(IOException|EncodeExceptione){

// Exception handling...

}

}

}

}

}

The trick is to make the @ServerEndpoint class also an EJB @Singleton. The @Singleton assures that only one instance is living at a time and this instance can keep also the session provided during @OnOpen. In other words: The actual server endpoint instance is exactly the same where the scheduler is running on. If it would not be @Singleton, multiple instances will or may exist and the session field is not set in @Schedule and might lead to a NullPointerException if not checked for.