WebSocket Java Client API in GlassFish 4 (TOTD #208)


The WebSocket API by W3C defines an API that enables Web pages to use the WebSocket protocol for two-way communication with a remote host. Java API for WebSocket (JSR 356) has added an ability to invoke a WebSocket endpoint from a Java application. This Tip Of The Day (TOTD) will explain how you can use that API for invoking a WebSocket endpoint.

JSR 356 introduces a class-level annotation @WebSocketClient that converts a POJO into a WebSocket client. The usual lifecycle annotations such as @WebSocketOpen, @WebSocketClose, @WebSocketError, and @WebSocketMessage can be specified on methods.

@WebSocketClient
public class MyClientEndpoint {
@WebSocketOpen
public void onOpen(Session session) {
System.out.println("Connected to endpoint: " + session.getRemote());
try {
String name = "Duke";
System.out.println("Sending message to endpoint: " + name);
session.getRemote().sendString(name);
} catch (IOException ex) {
Logger.getLogger(MyClientEndpoint.class.getName()).log(Level.SEVERE, null, ex);
}
}

@WebSocketMessage
public void processMessage(String message) {
System.out.println("Received message in client: " + message);
Client.messageLatch.countDown();
}
}
This code sends a message to the connected WebSocket endpoint from the onOpen method and prints the message when it comes back. This endpoint is bootstrapped using a separate class:

public class Client {

final static CountDownLatch messageLatch = new CountDownLatch(1);

public static void main(String[] args) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://echo.websocket.org:80/";
System.out.println("Connecting to " + uri);
container.connectToServer(MyClientEndpoint.class, URI.create(uri));
messageLatch.await(100, TimeUnit.SECONDS);
} catch (DeploymentException | InterruptedException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Note that a CountDownLatch is used to ensure that there is enough time for the client to connect to the endpoint. The latch counts down in MyClientEndpoint.processMessage once the echo message is received from the endpoint.

The source code can be downloaded here, connects to a remote WebSocket endpoint, and displays the output on the console.

The Maven dependency for now are specified as:
<dependencies>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-container-grizzly</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

Hopefully these could be specified as javax dependencies only!

The latest specification draft and javadocs can be downloaded here. Share your feedback at users@websocket-spec.java.net (for the specification) or users@tyrus.java.net (for the implenentation).

Comments:

Using something like Apache Apollo, how might you connect via WebSocket and publish a message to channel/topic such that a JavaScript client will see the message?

Typically what I see is a JavaScript client connects to the WebSocket server (such as Apollo) and then "subscribes" to a destination, such as a topic on Apollo.

In the example you've provided it looks like a message it published to a WebSocket server but no particular destination, is that correct?

Posted by craig on May 24, 2013 at 03:01 AM PDT #

ignore my last comment...not sure how I got so confused :) I don't need to connect via WebSocket, I just created a normal MessageProducer and dropped the message on the topic, duh! :)

Posted by craig on May 24, 2013 at 03:22 AM PDT #

Hello,

I'm trying to use websockets in my Java EE application. I can send messages from the client and echo them from the server but I don't know how to send custom notification messages from the server, based on certain events. Say, notify the client when a job is done.

On the client side it's simple:
String uri = "ws://localhost:8080" + request.getContextPath() + "/websocket";
Session s = container.connectToServer(MyClient.class, null, uri);
s.getBasicRemote().sendText("message from client");

How could I do this on the server side ?

Thanks

Posted by manu on June 11, 2013 at 01:42 PM PDT #

Take a look at https://blogs.oracle.com/arungupta/entry/collaborative_whiteboard_using_websocket_in for more detailed sample of server-side interaction. Basically, you'll identify your business message on the server-side to respond back and tag it with @OnMessage.

Posted by Arun Gupta on June 16, 2013 at 03:14 AM PDT #

Hello again,

Thank you for your answer. Eventually I managed to do what I wanted.
However I had some problems with the websocket initialization which look like a bug.

session = container.connectToServer(ClientEndpoint.class, null, uri);
session.getBasicRemote().sendText("hello");

The API specification states about the connectToServer method: "This method blocks until the connection is established, or throws an error if the connection could not be made."
I guess it doesn't actually always block until connection is properly established as I got lots of <RuntimeException: socket is closed> errors when sending that text message.

A Thread.yield() call after the connectToServer line (hint from a professor of mine) solved the problem, which supports the idea above.

Cheers

Posted by manu on June 20, 2013 at 10:23 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
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
   
       
Today