javawebsocketmessage-handlers

Java Websocket / MessageHandler return to global scope?


I'm facing the following problem and I found no working solution yet. I have 3 different applications that should communicate with each other:

The backend application provides a Webservice (REST) for the UI to get and put information from/to the microservice. Everything I want to grab from the microservice works fine, but: If I want to put data to the microservice, the specs require a websocket connection. This works fine too, but the microservice returns a message after the (un-)successful command, like

{"statusCode":200,"messageId":"1234567890"}

The problem now is: How can I grab this message in my application and send it back to the UI, so the user knows if the command was successful?

For the moment I tried this:

WebSocketClient.java

@OnMessage
public void onMessage(Session session, String msg) {
    if (this.messageHandler != null) {
        this.messageHandler.handleMessage(msg);
    }
}
public void addMessageHandler(MessageHandler msgHandler) {
    this.messageHandler = msgHandler;
}
public static interface MessageHandler {

    public String handleMessage(String message);
}

MyTotalAwesomeController.java

public class MyTotalAwesomeController {

    WebSocketClient wsc = new WebSocketClient();
    ...


    @RequestMapping(value="/add", method={RequestMethod.POST, RequestMethod.OPTIONS})
    public ResponseEntity<Object> putDataToMicroservice(@RequestBody Map<String, Object> payload, @RequestHeader(value = "authorization") String authorizationHeader) throws Exception {
    ...

    wsc.addMessageHandler(new WebSocketClient.MessageHandler() {
        public String handleMessage(String message) {

            System.out.println("RETURN MSG FROM WSS : " + message);
            return message;
        }
    });

    return ResponseEntity.ok("worked");
}

I can see the console output from the MessageHandler return, but I don't know how I can pass this to the parent method for return insted of just returning the ResponseEntity.ok().

I'm not very used to WebSocket connections in Java yet, so please don't judge me ;-)

Thank you for your help.


Solution

  • The code below will work under the assumption that the @OnMessage method is executed in a thread managed by the WebSocket client runtime. Please inspect the thread that runs the @OnMessage method.

    If the above premise is true, the putDataToMicroservice() method, executed by a thread in the global scope, will wait until the WebSocket response arrives at the WS client thread, which will repass the message to the global scope thread. Then the execution in your controller class will continue.

    public class MyTotalAwesomeController {
    
        WebSocketClient wsc = new WebSocketClient();
    
        // Queue for communication between threads.
        private BlockingQueue<String> queue;
    
        @PostConstruct
        void init() {
    
            queue = new SynchronousQueue<>(true);
    
            // This callback will be invoked by the WebSocket thread.
            wsc.addMessageHandler(new WebSocketClient.MessageHandler() {
                @Override
                public String handleMessage(String message) {
                    System.out.println("RETURN MSG FROM WSS : " + message);
                    // Pass message to the controller thread.
                    queue.put(message);
                    // Note that the return value is not necessary.
                    // You can take it out of the interface as well.
                    return null;
                }
            });
        }
    
        @RequestMapping(value="/add", method={RequestMethod.POST, RequestMethod.OPTIONS})
        public ResponseEntity<Object> putDataToMicroservice(@RequestBody Map<String, Object> payload, @RequestHeader(value = "authorization") String authorizationHeader) throws Exception {
    
            // At this point you make a WebSocket request, is that right?
            doWebSocketRequest();
    
            // This poll call will block the current thread
            // until the WebSocket server responds,
            // or gives up waiting after the specified timeout.
            //
            // When the WebSocket server delivers a response,
            // the WS client implementation will execute the
            // @OnMessage annotated method in a thread
            // managed by the WS client itself.
            //
            // The @OnMessage method will pass the message
            // to this thread in the queue below.
    
            String message = queue.poll(30, TimeUnit.SECONDS);
    
            if (message == null) {
                // WebSocket timeout.
            }
    
            return ResponseEntity.ok("worked");
        }
    }