Hot questions for Using GlassFish in tyrus

Question:

I trying to transfer a file bigger than 4M via WebSocket. I'm using org.glassfish.tyrus:tyrus-server:1.13.1 and org.glassfish.tyrus:tyrus-container-grizzly-server:1.13.1 as dependencies.

By default the incoming buffer size is about 4M (see: 8.4. Incoming buffer size). The documentation clearly says what needs to be done if I want to increase the size of the file but still can not change the incoming buffer size. Here is the essence of what I'm trying to do:

CountDownLatch messageLatch = new CountDownLatch(1);

final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
ClientManager client = ClientManager.createClient();

client.getProperties().put(ClientProperties.INCOMING_BUFFER_SIZE, new Integer(17_000_000));
Integer tyrusIncomingBufferSize = Utils.getProperty(client.getProperties(), ClientProperties.INCOMING_BUFFER_SIZE, Integer.class);
System.out.println("tyrusIncomingBufferSize: " + tyrusIncomingBufferSize); // 17000000

client.connectToServer(new Endpoint() {
    @Override
    public void onOpen(Session session, EndpointConfig config) {
        try {
            session.addMessageHandler(new MessageHandler.Whole<ByteBuffer>() {

                @Override
                public void onMessage(ByteBuffer message) {
                    System.out.println("Received message: " + message);
                    messageLatch.countDown();
                }
            });

            File pic = new File(TEST_PIC); // the size is more than 4M
            FileInputStream fileReader = new FileInputStream(pic);
            final long sizeOfScreenshotFile = pic.length();
            System.out.println(sizeOfScreenshotFile); // 4734639
            byte[] screenshotData = new byte[(int) sizeOfScreenshotFile];
            fileReader.read(screenshotData);
            fileReader.close();

            ByteBuffer bb = ByteBuffer.wrap(screenshotData);
            session.getBasicRemote().sendBinary(bb);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}, cec, new URI(URI));
messageLatch.await(100, TimeUnit.SECONDS);

But I still have the same error:

17000000
4734639
V 30, 2017 1:39:58 PM org.glassfish.tyrus.core.TyrusEndpointWrapper onError
WARNING: Unexpected error, closing connection.
java.lang.IllegalArgumentException: Buffer overflow.
    at org.glassfish.tyrus.core.Utils.appendBuffers(Utils.java:346)
    at org.glassfish.tyrus.core.TyrusWebSocketEngine$TyrusReadHandler.handle(TyrusWebSocketEngine.java:523)
    at org.glassfish.tyrus.container.grizzly.server.GrizzlyServerFilter$ProcessTask.execute(GrizzlyServerFilter.java:379)
    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.server.GrizzlyServerFilter.handleRead(GrizzlyServerFilter.java:215)
    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)

java.io.IOException: An established connection was aborted by the software in your host machine
    at sun.nio.ch.SocketDispatcher.write0(Native Method)
    at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51)
    at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
    at sun.nio.ch.IOUtil.write(IOUtil.java:51)
    at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471)
    at org.glassfish.grizzly.nio.transport.TCPNIOUtils.flushByteBuffer(TCPNIOUtils.java:149)
    at org.glassfish.grizzly.nio.transport.TCPNIOUtils.writeSimpleBuffer(TCPNIOUtils.java:133)
    at org.glassfish.grizzly.nio.transport.TCPNIOAsyncQueueWriter.write0(TCPNIOAsyncQueueWriter.java:126)
    at org.glassfish.grizzly.nio.transport.TCPNIOAsyncQueueWriter.write0(TCPNIOAsyncQueueWriter.java:106)
    at org.glassfish.grizzly.nio.AbstractNIOAsyncQueueWriter.processAsync(AbstractNIOAsyncQueueWriter.java:344)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:108)
    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.executeIoEvent(WorkerThreadIOStrategy.java:103)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.executeIoEvent(AbstractIOStrategy.java:89)
    at org.glassfish.grizzly.nio.SelectorRunner.iterateKeyEvents(SelectorRunner.java:415)
    at org.glassfish.grizzly.nio.SelectorRunner.iterateKeys(SelectorRunner.java:384)
    at org.glassfish.grizzly.nio.SelectorRunner.doSelect(SelectorRunner.java:348)
    at org.glassfish.grizzly.nio.SelectorRunner.run(SelectorRunner.java:279)
    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)
-9.8634088E7
V 30, 2017 1:41:38 PM org.glassfish.grizzly.http.server.NetworkListener shutdownNow
INFO: Stopped listener bound to [0.0.0.0:8025]
V 30, 2017 1:41:38 PM org.glassfish.tyrus.server.Server stop
INFO: Websocket Server stopped.

I tried to debug the Tyrus project and saw that the incomingBufferSize variable actually remained with the default value.

Does anyone have any idea how can I fix this?


Answer:

You are setting the property in the client, but the exception is clearly thrown on the server.

How are you starting the server? It almost seems like you are using Grizzly standalone - if you do, you can try starting the server with TyrusWebSocketEngine#INCOMING_BUFFER_SIZE property set to 17_000_000 or whatever value you want.

(That can be done by creating the server by using Server(Map, Class ...) or other constructors. See Server class javadoc for more details.

Question:

I'm writing a Websocket client in Java, using javax.websocket API, and org.glassfish.tyrus as the implementation.

Everything usually works, but sometimes, when I'm receiving very large strings, the connection closes with a mysterious 'Illegal UTF-8 Sequence' as the close reason.

log.info("Ws closed cuz: " 
   + reason.getCloseCode() + " , " 
   + reason.getReasonPhrase() + " , " 
   + reason.toString());

Output:

INFO: Ws closed cuz: NOT_CONSISTENT , Illegal UTF-8 Sequence ,
CloseReason[1007,Illegal UTF-8 Sequence]

I'm guessing that either the string was too large, or the string contained any characters which aren't UTF-8 compatible.

Is there a way to get any more info on the actual string / packet / frame which causes this issue? Or, if there's a way to tell tyrus to ignore any encoding issues and just pass me the raw string and let me handle it?

If not, is there another java websockets client which does the bare bones work of transmitting the strings over socket and doesn't do any validation, and just lets me handle the responses?

Appreciate any feedback.


Answer:

The following is just a guess.

(1) On the server side, the large string is split into one text frame and one or more following continuation frames. Technically, the original large string is converted into a byte array and then the byte array is split into multiple sub byte arrays. The sub arrays are set to frames one by one (= Each frame contains one sub byte array).

(2) Although there is no guarantee that each sub byte array is a valid UTF-8 sequence, validity check is performed either on the server side or on the client side. If so, it's a bug of Tyrus.

WebSocketListener of nv-websocket-client has callback methods in frame granularity such as onFrame, onTextFrame, onContinuationFrame and others (note that onTextMessage and onTextFrame are different), so you can examine the byte array of each frame there.

WebSocket websocket = new WebSocketFactory()
    .createSocket("ws://...")
    .addListener(new WebSocketAdapter() {
        @Override
        public void onFrame(WebSocket ws, WebSocketFrame frame) {
            // If the frame is a text frame with FIN bit cleared, or
            // if the frame is a continuation frame.
            if ((frame.isTextFrame() && frame.getFin() == false) ||
                frame.isContinuationFrame()) {
                // The payload of the frame. There is no guarantee
                // that this byte array is a valid UTF-8 sequence.
                byte[] payload = frame.getPayload();

                // Check whether the payload is a valid UTF-8 sequence
                // if you want to.
                checkPayload(payload);
            }
        }
    })
    .connect();

Why don't you use nv-websocket-client to examine what is happening in your WebSocket connection?

Question:

im new to WebSockets . for start learning i followed this tutorial . and simply copied the ClientEndpoint and ServerEndpoint and deployed the server with tyrus .

but when i try to run the server it will throw this error :

Failed to load container provider class: org.glassfish.tyrus.container.grizzly.GrizzlyEngine

here is the deploy code ( i've commented the line which i get error on ) :

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.glassfish.tyrus.server.Server;

public class WebSocketServer {

    public static void main(String[] args) {
        runServer();
    }

    public static void runServer() {

        Server server = new Server("localhost", 8000, "/websockets", WordgameServerEndpoint.class);

        try {
            server.start(); //i get the error on this line
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            System.out.print("Please press a key to stop the server.");
            reader.readLine();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            server.stop();
        }
    }
}

here is the server code :

import java.io.IOException;
import java.util.logging.Logger;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/game")
public class WordgameServerEndpoint {

    private Logger logger = Logger.getLogger(this.getClass().getName());

    @OnOpen
    public void onOpen(Session session) {
        logger.info("Connected ... " + session.getId());
    }

    @OnMessage
    public String onMessage(String message, Session session) {
        switch (message) {
            case "quit":
                try {
                    session.close(new CloseReason(CloseCodes.NORMAL_CLOSURE, "Game ended"));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                break;
        }
        return message;
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason));
    }
}

i'd appreciate any help :)


Answer:

it seems to be a dependencies issue,

if you are a maven user:

<dependencies>
    <dependency>
        <groupId>javax.websocket</groupId>
        <artifactId>javax.websocket-api</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-server</artifactId>
        <version>1.1</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-client</artifactId>
        <version>1.1</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-container-grizzly</artifactId>
        <version>1.1</version>
    </dependency>

    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.5</version>
    </dependency>
</dependencies>

or load the following jars:

  • grizzly-framework-2.3.3.jar
  • grizzly-http-2.3.3.jar
  • grizzly-http-server-2.3.3.jar
  • grizzly-rcm-2.3.3.jar
  • javax.websocket-api-1.0.jar
  • tyrus-client-1.1.jar
  • tyrus-container-grizzly-1.1.jar
  • tyrus-core-1.1.jar
  • tyrus-server-1.1.jar
  • tyrus-spi-1.1.jar
  • tyrus-websocket-core-1.1.jar

please keep in mind these dependencies for both client and server implementations.

Question:

  1. Trying to make a websocket connection between two separate deployable wars on two different wildfly instances.
  2. Using tyrus-standalone-client to make websocket connection to the another war.

  3. Below is the snippet used to make web-socket connection

Question : Could be it possible to make the websocket connection to another deployable war ? I couldnt find any exceptions when I ran and not even 101-404 errors (Handshake Errors).

    AuthConfig authConfig = AuthConfig.Builder.create().disableProvidedDigestAuth().build();
    Credentials credentials = new Credentials("root", "xyz");
    ClientManager client = ClientManager.createClient();
    client.getProperties().put(ClientProperties.AUTH_CONFIG, authConfig);
    client.getProperties().put(ClientProperties.CREDENTIALS, credentials);


    try (Session session = client.connectToServer(GovernorNodeWebSocketClient.class, new URI("ws://10.203.67.168:8080/xyz"))) {

        session.getBasicRemote().sendObject("xyz");
        System.out.println("4sendMessageToRemoteGovernor :: sent the message from device services to the remote governor"+responseObject.getJson());
    }
    catch (Exception e) {

        e.printStackTrace();
    }

Answer:

Yes its possible when I used below snippets, after removing credentials part and ClientManager.createClient();

So by adding the tyrus dependencies over your wildfly instance we can setup websocket connection between them

try  {
        System.out.println("sendMessageToRemoteGovernor :: "+message.getJson());
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        session = container.connectToServer(XYZ.class, new URI("ws://localhost:8080/xyz/xyz"));
        session.getBasicRemote().sendObject(message);
    }
    catch (Exception e) {
        e.printStackTrace();
    }

Question:

UPDATE It seems that in org.glassfish.tyrus.core.BaseContainer in the constructor, these two lines run super slow:

this.managedExecutorService = lookupManagedExecutorService();
this.managedScheduledExecutorService = lookupManagedScheduledExecutorService();

What is the deal with the mention of Android in the comments of both methods, am I using the wrong code for Java that is aimed at Desktops?


I am using the code exactly as it is from:

Tyrus Websocket Documentation: 1.1.2 Client Endpoint

Somehow it takes about 10 seconds to connect, specifically at this line when I run Eclipse's debugger:

ClientManager client = ClientManager.createClient();

Could it be related to this? Potentially similar Stack Overflow Question

I am really lost, I feel like I am a rare outlier trying to use websockets with a Java client as opposed to a browser with Javascript.


Answer:

So the cause of the troubles is the initialization of an InitialContext by Tyrus in order to re-use a (scheduled) executor service if one is available. Normally this fails fast if none is available (and this is logged as a debug-message, see further on), but in this case it failed only after trying to initialize a INITIAL_CONTEXT_FACTORY that could not work. To override this behavior, call System.setProperty(javax.naming.InitialContext.INITIAL_CONTEXT_FACTORY, "javax.naming.spi.InitialContextFactory") before creating a client. The initial InitialContext will then try to create an instance of an interface and this fails fast.

The problems could probably be found earlier with verbose logging. Tyrus does not do a lot of (debug) logging but in this case a setup with jul over logback could have shown an early indication of the underlying troubles. As a general rule: always ensure you can view trace/debug logging when running into weird problems.

As for the Android comments in the source code: this is just the platform where the incompatability with the "JDK8 compact2 profile" was noticed and it was resolved by not importing the javax.naming.InitialContext class directly (since apparently it does not exist in the compact2 profile) but using reflection instead (see also TYRUS-242).

If you are creating a purely Java Websocket client, consider using the JDK 7 client. The JDK 7 client bundle (org.glassfish.tyrus:tyrus-container-jdk-client:1.10) is considerably smaller as the default one (org.glassfish.tyrus.bundles:tyrus-standalone-client:1.10).

I also felt like I an outlier when I started using websockets with a Java client (I choose the Jetty websocket client API implementation). I also started using Tomcat embedded more (for example basic-jsp-embed). When combining these techniques, you get a powerful ("full duplex") network solution (more akin to peer-to-peer instead of client-server). Hopefully it will catch on. One caveat to remember is that some firewalls will drop connections (that look like http-connections) after 30 minutes (even when the connection is in use). So for a stable connection, make sure the client sends regular ping-messages to ensure a healthy connection and creates a new connection within/every 30 minutes.

Question:

I'm facing with strange problem.

My application (works with Payara 4.1.1.163) uses Websockets to send messages between clients. TyrusRemoteEndpoint is used for this functionality.

After deployment it works well around 3-4 days, and then (if application were not deployed or redeployed this time) tyrus suddenly blocks thread (or even threads) when calling a function:

session.getBasicRemote().sendObject(obj);

Using VisualVM I've dumped all threads and see the following:

"http-thread-pool(56)" - Thread t@209
   java.lang.Thread.State: WAITING
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for <6929ab6a> (a java.util.concurrent.CountDownLatch$Sync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
    at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)
    at org.glassfish.tyrus.core.TyrusFuture.get(TyrusFuture.java:77)
    at org.glassfish.tyrus.core.TyrusRemoteEndpoint$Basic.sendObject(TyrusRemoteEndpoint.java:183)

It seems that this thread holds in a forever wait. Messages do not send for this client.

If I use syncronized blocks it even causes deadlock and all http-thread-pools (that concerns websockets) became blocked.

What can cause such an issue? How can I cope with it? Should I need to provide something else? Thank you in advance!

P.S. I'm using default Payara settings concerning thread pools (if it can help).


Answer:

Seems that this is a bug in tyrus-websocket library.

It can be at least resolved by recovering application using

getAsyncRemote()

instead of

getBasicRemote()

in the following way:

Future<Void> f = session.getAsyncRemote().sendObject(...)        
try {
    f.get(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    throw new IOException(e);
} catch (ExecutionException e) {
    Throwable cause = e.getCause();
    if (cause instanceof IOException) {
        throw (IOException) cause;
    } else if (cause instanceof EncodeException) {
        throw (EncodeException) cause;
    } else {
        throw new IOException(e);
    }
} catch (TimeoutException e) {
    throw new IOException(e);
}

In this case we are using timeout and at least ensure that our application can be recovered after that issue.

I've also checked this repository for the newest version of tyrus and cannot say that they've fixed an issue. Probably it should be posted as a bug of Payara or Glassfish.