javasocketswebsockettyrus

Tyrus - Store messages received by client


I want to have a client which will receive WS messages and store them for further analysis.

I created a Client based on https://dzone.com/articles/sample-java-web-socket-client

Unfortunately on each onMessage method call the field which will store the values is null and message cannot be added.

Code

 package com.sample.ws;

import org.apache.log4j.Logger;

import javax.websocket.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import static java.lang.Thread.sleep;

@ClientEndpoint
public class WSClient {
    private static final Logger LOG = Logger.getLogger(WSClient.class);

    private List<String> testContext;

    public static void main(String[] args) {
        WSClient client = new WSClient();

        List testContext = new ArrayList<String>();
        client.setTestContext(testContext);

        client.run();

        LOG.info("Final state: " + testContext);
    }

    private void run() {
        LOG.info("Initial state: " + testContext);

        WebSocketContainer container = null;//
        Session session = null;
        try {
            container = ContainerProvider.getWebSocketContainer();
            session = container.connectToServer(com.sample.ws.WSClient.class, URI.create("ws://localhost:8080/wsserver-1.0/ratesrv"));
            LOG.info("Connected to server");
            sleep(10000L);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @OnMessage
    private void onMessage(String message) {
        //the new USD rate arrives from the websocket server side.
        LOG.info("Received msg: " + message);
        testContext.add(message);
    }

    private void setTestContext(List<String> testContext) {
        this.testContext = testContext;
    }
}

Log

INFO  WSClient - Initial state: []
INFO  WSClient - Connected to server
INFO  WSClient - Received msg: USD Rate: 2.998
Feb 03, 2017 5:52:22 PM org.glassfish.tyrus.core.AnnotatedEndpoint onError
INFO: Unhandled exception in endpoint com.sample.ws.WSClient.
java.lang.NullPointerException
    at com.sample.ws.WSClient.onMessage(WSClient.java:55)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.glassfish.tyrus.core.AnnotatedEndpoint.callMethod(AnnotatedEndpoint.java:520)
    at org.glassfish.tyrus.core.AnnotatedEndpoint.access$100(AnnotatedEndpoint.java:87)
    at org.glassfish.tyrus.core.AnnotatedEndpoint$WholeHandler$1.onMessage(AnnotatedEndpoint.java:619)
    at org.glassfish.tyrus.core.TyrusSession.notifyMessageHandlers(TyrusSession.java:576)
    at org.glassfish.tyrus.core.TyrusEndpointWrapper.onMessage(TyrusEndpointWrapper.java:879)
    at org.glassfish.tyrus.core.TyrusWebSocket.onMessage(TyrusWebSocket.java:216)
    at org.glassfish.tyrus.core.frame.TextFrame.respond(TextFrame.java:139)
    at org.glassfish.tyrus.core.ProtocolHandler.process(ProtocolHandler.java:807)
    at org.glassfish.tyrus.client.TyrusClientEngine$TyrusReadHandler.handle(TyrusClientEngine.java:747)
    at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientFilter$ProcessTask.execute(GrizzlyClientFilter.java:476)
    at org.glassfish.tyrus.container.grizzly.client.TaskProcessor.processTask(TaskProcessor.java:114)
    at org.glassfish.tyrus.container.grizzly.client.TaskProcessor.processTask(TaskProcessor.java:91)
    at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientFilter.handleRead(GrizzlyClientFilter.java:272)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:526)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
    at java.lang.Thread.run(Thread.java:745)
INFO  WSClient - Final state: []

Solution

  • WebSocket client creates new instance and the "testContext" field is not populated there.

    Make it static and it will start to work. Also, you need to make it thread safe. (i.e.: CopyOnWriteArrayList).

    Another option is to call #connectToServer(Object, URI), which will force WebSocket client to use the provided instance (not to create a new one). Again, you need to make sure your collection (and any other fields accessed during processing) are accessed in thread-safe manner.