13

There appears to be very little Java 11 (pure Java non framework based) WebSocket client code examples on the web so I'm hoping StackOverflow can come to the rescue for me once again.

This is the closest I've found, but unfortunately to my (novice) eyes, it doesn't appear to be a complete solution in showing how to consume the data from the WebSocket listener.

Looking at the WebSocket.Listener implementation, the onText callback method I presume would provide what I need, but I'm struggling to figure out how to return the CompletionStage object and some sort of string data from the socket.

This is some test code I have so far.

Would appreciate assistance. Thanks

    public class Main {

        public static void main(String[] args) {

           WebSocketClient wsc = new WebSocketClient();
           wsc.startSocket("ws://demos.kaazing.com/echo");

           int i = 0;   

           // Bad, very bad
           do {} while (i == 0);
        }
    }


    public class WebSocketClient implements WebSocket.Listener {

        @Override
        public void onOpen(WebSocket webSocket) {
           //...
            System.out.println("Go...Open".concat(
                    webSocket.getSubprotocol()));
        }

        @Override
        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
           //...
            System.out.println(data.toString());

            // How do I return the CompletionStage object
            // return CompletionStage<String>
        }

        @Override
        public void onError(WebSocket webSocket, Throwable error) {
           //..
            System.out.println("Bad day! ".concat(webSocket.toString()));
        }

        void startSocket(String connection) {
            CompletableFuture<WebSocket> server_cf = HttpClient.
                    newHttpClient().
                    newWebSocketBuilder().
                    buildAsync(URI.create(connection),
                            new WebSocketClient());
            WebSocket server = server_cf.join();
            server.sendText("Hello!", true);
        }
    }
pavel
  • 122
  • 13
eodeluga
  • 608
  • 2
  • 7
  • 20
  • Not really concerned (as I just would like some help), but if you're going to downvote, it would be great if you could at least say what's wrong. – eodeluga Mar 27 '19 at 15:32
  • Do you know why `CompletionStage` needs to be returned from `on`-methods of `Listener`? In other words, what purpose do these stages serve? – pavel Mar 28 '19 at 12:48

3 Answers3

16

Below you find a working example. I have made some changes to your code above:

  • onOpen needs to invoke request(1) on the websocket (invoking the default implementation) in order to receive further invocations.
  • moved method startSocket into the main method
  • replaced busy waiting with a count down latch
  • declared class WebSocketClient as a (static) inner class

but beyond these (cosmetic) changes the program follows your idea, i.e. first a websocket connection is build and after successful construction the text Hello! is sent to the echo server. This could also be done in method onOpen directly.

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;
        
public class Main {
        
    public static void main(String[] args) throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        
        WebSocket ws = HttpClient
                .newHttpClient()
                .newWebSocketBuilder()
                .buildAsync(URI.create("ws://demos.kaazing.com/echo"), new WebSocketClient(latch))
                .join();
        ws.sendText("Hello!", true);
        latch.await();
    }
            
    private static class WebSocketClient implements WebSocket.Listener {
        private final CountDownLatch latch;
                
        public WebSocketClient(CountDownLatch latch) { this.latch = latch; }
        
        @Override
        public void onOpen(WebSocket webSocket) {
            System.out.println("onOpen using subprotocol " + webSocket.getSubprotocol());
            WebSocket.Listener.super.onOpen(webSocket);
        }
        
        @Override
        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
            System.out.println("onText received " + data);
            latch.countDown();
            return WebSocket.Listener.super.onText(webSocket, data, last);
        }
        
        @Override
        public void onError(WebSocket webSocket, Throwable error) {
            System.out.println("Bad day! " + webSocket.toString());
            WebSocket.Listener.super.onError(webSocket, error);
        }
    }
}

Btw, no supprotocol was negotiated, therefore method webSocket.getSubprotocol() returns an empty string. The output in the console is

    onOpen using subprotocol 
    onText received Hello!
dbreaux
  • 4,982
  • 1
  • 25
  • 64
Dominik
  • 1,058
  • 8
  • 20
  • 1
    You don't need to call `WebSocket.Listener.super.onXXX` methods religiously. It all depends on your needs. In particular, `onError` specifies that: `@implSpec The default implementation does nothing` – pavel Apr 02 '19 at 12:34
  • 1
    @pavel, you are right, thanks for the comment. All the super-calls could be dropped (and `onText` could return `null` instead), but then in method `onOpen` the statement `webSocket.request(1)` would have to be executed in order to enable the websocket to invoke at least one receive method (this was missing in the original example). – Dominik Apr 02 '19 at 20:42
  • Hi, In case I want to send authentication data to the websocket server to get connected how do we do this. I was able to establish the web socket connection using JavaScript successfully. Below is the JSON object I send to the WS conncetion => jsonSendObj = { "channel":"", "task":"cn", "acctid":UID, "user":UID, "token": userSessionID }; How do we send this using Java? Can we use teh same SetString method? – Jerokdeep Nov 28 '19 at 13:14
1

The pattern for managing a WebSocket response returning a CompletionStage is:

@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
    // return inmmediately but response is geenrated lazyly.
    return CompletableFuture.supplyAsync(() -> {
        String response = "Received  ...";
        // do slow task. Access to database or access to a server.
        return response;
    });
}

This simple implementation only is recommended when the response is generated quickly.

@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
    // fast response.
    String response = "The text has " + data.length() + " chars";
    return CompletableFuture.completedFuture(response);
}
Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
0

I have had some trouble getting various examples working. Specifically, I had trouble finding examples that actually showed how to open, send, and receive simple text messages. One important piece was having a server to which to connect. Here is what I managed to make work.

    package webSockets;
    
    import java.io.IOException;
    import java.net.URI;
    
    import javax.websocket.CloseReason;
    import javax.websocket.ContainerProvider;
    import javax.websocket.Endpoint;
    import javax.websocket.EndpointConfig;
    import javax.websocket.MessageHandler;
    import javax.websocket.Session;
    import javax.websocket.WebSocketContainer;
    
    public class SimpleWebsocketClient extends Endpoint {
        private Session session;
    
        public SimpleWebsocketClient() {}
    
        public SimpleWebsocketClient(URI endpointURI) {
            try {
                WebSocketContainer container = ContainerProvider.getWebSocketContainer();
                container.connectToServer(this, endpointURI);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
      
        @Override
        public void onClose(Session session, CloseReason reason){
            System.out.println("Disconnected as a result of "+ reason.getReasonPhrase());
        }
        @Override
        public void onError(Session session, Throwable error){
            System.out.println("Error communicating with server: " + error.getMessage());
        }
        @Override
        public void onOpen(Session s, EndpointConfig config) {
            System.out.println("Session opened");
            session = s;
            session.addMessageHandler(new MessageHandler.Whole<String>() {
    
                @Override
                public void onMessage(String msg) {
                    System.out.println("Text Message Received:" + msg);
                }
            });
            try {
                session.getBasicRemote().sendText("Hello there.");
                session.getBasicRemote().sendText("Hope you are well!");
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
        
        public static void main(String...arg) {
            URI uri = URI.create("ws://connect.websocket.in/v3/1?api_key=oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm&notify_self");
            new SimpleWebsocketClient(uri);
            while(true) {}
        }
    }
dbreaux
  • 4,982
  • 1
  • 25
  • 64
ae6ty
  • 1
  • 2