type=page
status=published
title=The websocketbot Example Application
next=websocket013.html
prev=websocket011.html
~~~~~~
The websocketbot Example Application
====================================

[[BABCDBBC]]

[[the-websocketbot-example-application]]
The websocketbot Example Application
------------------------------------

The `websocketbot` example application, located in the
tut-install`/examples/web/websocket/websocketbot/` directory,
demonstrates how to use a WebSocket endpoint to implement a chat. The
example resembles a chat room in which many users can join and have a
conversation. Users can ask simple questions to a bot agent that is
always available in the chat room.

The following topics are addressed here:

* link:#CIHICIDE[Architecture of the websocketbot Example Application]
* link:#CIHHJHDB[Running the websocketbot Example Application]

[[CIHICIDE]]

[[architecture-of-the-websocketbot-example-application]]
Architecture of the websocketbot Example Application
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The `websocketbot` example application consists of the following
elements:

* link:#CIHDAEHF[The CDI Bean] – A CDI bean (`BotBean`) that contains
the logic for the bot agent to reply to messages
* link:#CIHJJJHG[The WebSocket Endpoint] – A WebSocket endpoint
(`BotEndpoint`) that implements the chat room
* link:#CIHFDGHG[The Application Messages] – A set of classes
(`Message`, `ChatMessage`, `InfoMessage`, `JoinMessage`, and
`UsersMessage`) that represent application messages
* link:#CIHGHHBD[The Encoder Classes] – A set of classes
(`ChatMessageEncoder`, `InfoMessageEncoder`, `JoinMessageEncoder`, and
`UsersMessageEncoder`) that encode application messages into WebSocket
text messages as JSON data
* link:#CIHHFICG[The Message Decoder] – A class (`MessageDecoder`) the
parses WebSocket text messages as JSON data and decodes them into
`JoinMessage` or `ChatMessage` objects
* link:websocket011.html#CIHHIEFH[The HTML Page] – An HTML page
(`index.html`) that uses JavaScript code to implement the client for the
chat room

[[CIHDAEHF]]

[[the-cdi-bean]]
The CDI Bean
^^^^^^^^^^^^

The CDI bean (`BotBean`) is a Java class that contains the `respond`
method. This method compares the incoming chat message with a set of
predefined questions and returns a chat response.

[source,oac_no_warn]
----
@Named
public class BotBean {
    public String respond(String msg) { ... }
}
----

[[CIHJJJHG]]

[[the-websocket-endpoint]]
The WebSocket Endpoint
^^^^^^^^^^^^^^^^^^^^^^

The WebSocket endpoint (`BotEndpoint`) is an annotated endpoint that
performs the following functions:

* Receives messages from clients
* Forwards messages to clients
* Maintains a list of connected clients
* Invokes the bot agent functionality

The endpoint specifies its deployment URI and the message encoders and
decoders using the `ServerEndpoint` annotation. The endpoint obtains an
instance of the `BotBean` class and a managed executor service resource
through dependency injection:

[source,oac_no_warn]
----
@ServerEndpoint(
   value = "/websocketbot",
   decoders = { MessageDecoder.class },
   encoders = { JoinMessageEncoder.class, ChatMessageEncoder.class,
                InfoMessageEncoder.class, UsersMessageEncoder.class }
)
/* There is a BotEndpoint instance per connection */
public class BotEndpoint {
   private static final Logger logger = Logger.getLogger("BotEndpoint");
   /* Bot functionality bean */
   @Inject private BotBean botbean;
   /* Executor service for asynchronous processing */
   @Resource(name="comp/DefaultManagedExecutorService")
   private ManagedExecutorService mes;

   @OnOpen
   public void openConnection(Session session) {
       logger.log(Level.INFO, "Connection opened.");
   }
   ...
}
----

The `message` method processes incoming messages from clients. The
decoder converts incoming text messages into `JoinMessage` or
`ChatMessage` objects, which inherit from the `Message` class. The
`message` method receives a `Message` object as a parameter:

[source,oac_no_warn]
----
@OnMessage
public void message(Session session, Message msg) {
   logger.log(Level.INFO, "Received: {0}", msg.toString());

   if (msg instanceof JoinMessage) {
      /* Add the new user and notify everybody */
      JoinMessage jmsg = (JoinMessage) msg;
      session.getUserProperties().put("name", jmsg.getName());
      session.getUserProperties().put("active", true);
      logger.log(Level.INFO, "Received: {0}", jmsg.toString());
      sendAll(session, new InfoMessage(jmsg.getName() +
              " has joined the chat"));
      sendAll(session, new ChatMessage("Duke", jmsg.getName(),
              "Hi there!!"));
      sendAll(session, new UsersMessage(this.getUserList(session)));

   } else if (msg instanceof ChatMessage) {
      /* Forward the message to everybody */
      ChatMessage cmsg = (ChatMessage) msg;
      logger.log(Level.INFO, "Received: {0}", cmsg.toString());
      sendAll(session, cmsg);
      if (cmsg.getTarget().compareTo("Duke") == 0) {
         /* The bot replies to the message */
         mes.submit(new Runnable() {
            @Override
            public void run() {
               String resp = botbean.respond(cmsg.getMessage());
               sendAll(session, new ChatMessage("Duke",
                       cmsg.getName(), resp));
            }
         });
      }
   }
}
----

If the message is a join message, the endpoint adds the new user to the
list and notifies all connected clients. If the message is a chat
message, the endpoint forwards it to all connected clients.

If a chat message is for the bot agent, the endpoint obtains a response
using the `BotBean` instance and sends it to all connected clients. The
`sendAll` method is similar to the example in
link:websocket005.html#BABIFBCG[Sending Messages to All Peers Connected
to an Endpoint].

Asynchronous Processing and Concurrency Considerations

The WebSocket endpoint calls the `BotBean.respond` method to obtain a
response from the bot. In this example, this is a blocking operation;
the user that sent the associated message would not be able to send or
receive other chat messages until the operation completes. To avoid this
problem, the endpoint obtains an executor service from the container and
executes the blocking operation in a different thread using the
`ManagedExecutorService.submit` method from Concurrency Utilities for
Java EE.

The Java API for WebSocket specification requires that Java EE
implementations instantiate endpoint classes once per connection. This
facilitates the development of WebSocket endpoints, because you are
guaranteed that only one thread is executing the code in a WebSocket
endpoint class at any given time. When you introduce a new thread in an
endpoint, as in this example, you must ensure that variables and methods
accessed by more than one thread are thread safe. In this example, the
code in `BotBean` is thread safe, and the `BotEndpoint.sendAll` method
has been declared `synchronized`.

Refer to link:concurrency-utilities.html#GKJIQ8[Chapter 59, "Concurrency
Utilities for Java EE"] for more information on the managed executor
service and Concurrency Utilities for Java EE.

[[CIHFDGHG]]

[[the-application-messages]]
The Application Messages
^^^^^^^^^^^^^^^^^^^^^^^^

The classes that represent application messages (`Message`,
`ChatMessage`, `InfoMessage`, `JoinMessage`, and `UsersMessage`) contain
only properties and getter and setter methods. For example, the
`ChatMessage` class looks like this:

[source,oac_no_warn]
----
public class ChatMessage extends Message {
    private String name;
    private String target;
    private String message;
    /* ... Constructor, getters, and setters ... */
}
----

[[CIHGHHBD]]

[[the-encoder-classes]]
The Encoder Classes
^^^^^^^^^^^^^^^^^^^

The encoder classes convert application message objects into JSON text
using the Java API for JSON Processing. For example, the
`ChatMessageEncoder` class is implemented as follows:

[source,oac_no_warn]
----
/* Encode a ChatMessage as JSON.
 * For example, (new ChatMessage("Peter","Duke","How are you?"))
 * is encoded as follows:
 * {"type":"chat","target":"Duke","message":"How are you?"}
 */
public class ChatMessageEncoder implements Encoder.Text<ChatMessage> {
   @Override
   public void init(EndpointConfig ec) { }
   @Override
   public void destroy() { }
   @Override
   public String encode(ChatMessage chatMessage) throws EncodeException {
      // Access properties in chatMessage and write JSON text...
   }
}
----

See link:jsonp.html#GLRBB[Chapter 20], link:jsonp.html#GLRBB[JSON
Processing] for more information on the Java API for JSON Processing.

[[CIHHFICG]]

[[the-message-decoder]]
The Message Decoder
^^^^^^^^^^^^^^^^^^^

The message decoder (`MessageDecoder`) class converts WebSocket text
messages into application messages by parsing JSON text. It is
implemented as follows:

[source,oac_no_warn]
----
/* Decode a JSON message into a JoinMessage or a ChatMessage.
 * For example, the incoming message
 * {"type":"chat","name":"Peter","target":"Duke","message":"How are you?"}
 * is decoded as (new ChatMessage("Peter", "Duke", "How are you?"))
 */
public class MessageDecoder implements Decoder.Text<Message> {
    /* Stores the name-value pairs from a JSON message as a Map */
    private Map<String,String> messageMap;

    @Override
    public void init(EndpointConfig ec) { }
    @Override
    public void destroy() { }

    /* Create a new Message object if the message can be decoded */
    @Override
    public Message decode(String string) throws DecodeException {
       Message msg = null;
       if (willDecode(string)) {
          switch (messageMap.get("type")) {
             case "join":
                msg = new JoinMessage(messageMap.get("name"));
                break;
             case "chat":
                msg = new ChatMessage(messageMap.get("name"),
                                      messageMap.get("target"),
                                      messageMap.get("message"));
          }
       } else {
          throw new DecodeException(string, "[Message] Can't decode.");
       }
       return msg;
   }

   /* Decode a JSON message into a Map and check if it contains
    * all the required fields according to its type. */
   @Override
   public boolean willDecode(String string) {
      // Convert JSON data from the message into a name-value map...
      // Check if the message has all the fields for its message type...
   }
}
----

[[CIHGDBGF]]

[[the-html-page]]
The HTML Page
^^^^^^^^^^^^^

The HTML page (`index.html`) contains a field for the user name. After
the user types a name and clicks Join, three text areas are available:
one to type and send messages, one for the chat room, and one with the
list of users. The page also contains a WebSocket console that shows the
messages sent and received as JSON text.

The JavaScript code on the page uses the WebSocket API to connect to the
endpoint, send messages, and designate callback methods. The WebSocket
API is supported by most modern browsers and is widely used for web
client development with HTML5.

[[CIHHJHDB]]

[[running-the-websocketbot-example-application]]
Running the websocketbot Example Application
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This section describes how to run the `websocketbot` example application
using NetBeans IDE and from the command line.

The following topics are addressed here:

* link:#CIHFDDGE[To Run the websocketbot Example Application Using
NetBeans IDE]
* link:#CIHEDEHB[To Run the websocketbot Example Application Using
Maven]
* link:#BABDDAAG[To Test the websocketbot Example Application]

[[CIHFDDGE]]

[[to-run-the-websocketbot-example-application-using-netbeans-ide]]
To Run the websocketbot Example Application Using NetBeans IDE
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

1.  Make sure that GlassFish Server has been started (see
link:usingexamples002.html#BNADI[Starting and Stopping GlassFish
Server]).
2.  From the File menu, choose Open Project.
3.  In the Open Project dialog box, navigate to:
+
[source,oac_no_warn]
----
tut-install/examples/web/websocket
----
4.  Select the `websocketbot` folder.
5.  Click Open Project.
6.  In the Projects tab, right-click the `websocketbot` project and
select Run.
+
This command builds and packages the application into a WAR file,
`websocketbot.war`, located in the `target/` directory; deploys it to
the server; and launches a web browser window with the following URL:
+
[source,oac_no_warn]
----
http://localhost:8080/websocketbot/
----
+
See link:#BABDDAAG[To Test the websocketbot Example Application] for
more information.

[[CIHEDEHB]]

[[to-run-the-websocketbot-example-application-using-maven]]
To Run the websocketbot Example Application Using Maven
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

1.  Make sure that GlassFish Server has been started (see
link:usingexamples002.html#BNADI[Starting and Stopping GlassFish
Server]).
2.  In a terminal window, go to:
+
[source,oac_no_warn]
----
tut-install/examples/web/websocket/websocketbot/
----
3.  Enter the following command to deploy the application:
+
[source,oac_no_warn]
----
mvn install
----
4.  Open a web browser window and type the following address:
+
[source,oac_no_warn]
----
http://localhost:8080/websocketbot/
----
+
See link:#BABDDAAG[To Test the websocketbot Example Application] for
more information.

[[BABDDAAG]]

[[to-test-the-websocketbot-example-application]]
To Test the websocketbot Example Application
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

1.  On the main page, type your name on the first text field and press
the Enter key.
+
The list of connected users appears on the text area on the right. The
text area on the left is the chat room.
2.  Type a message on the text area below the login button. For example,
type the messages in bold and press enter to obtain responses similar to
the following:
+
[source,oac_no_warn]
----
[--Peter has joined the chat--]
Duke: @Peter Hi there!!
Peter: @Duke how are you?
Duke: @Peter I'm doing great, thank you!
Peter: @Duke when is your birthday?
Duke: @Peter My birthday is on May 23rd. Thanks for asking!
----
3.  Join the chat from another browser window by copying and pasting the
URI on the address bar and joining with a different name.
+
The new user name appears in the list of users in both browser windows.
You can send messages from either window and see how they appear in the
other.
4.  Click Show WebSocket Console.
+
The console shows the messages sent and received as JSON text.
