Commons Net FTPClient hangs indefinitely with Mule - java

I encountered an issue with the Mule ESB FTP Transport: when polling, the thread running the client would hang indefinitely without throwing an error. This causes FTP poll to stop completely. Mule uses Apache Commons Net FTPClient.
Looking further into the code, I think it is caused by the SocketTimeout of the FTPClient not being set, sometime causing infinite hanging when reading lines from the FTPClient's socket.
We can clearly see the problem in these stacks retrieved with jstack when the problem occured. The __getReply() function seems to be the more direct link to the problem.
This one hanging on connect() call when creating a new FTPClient:
receiver.172 prio=10 tid=0x00007f23e43c8800 nid=0x2d5 runnable [0x00007f24c32f1000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
- locked <0x00000007817a9578> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
- locked <0x00000007817a9578> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:294)
at org.apache.commons.net.ftp.FTP._connectAction_(FTP.java:364)
at org.apache.commons.net.ftp.FTPClient._connectAction_(FTPClient.java:540)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:178)
at org.mule.transport.ftp.FtpConnectionFactory.makeObject(FtpConnectionFactory.java:33)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1188)
at org.mule.transport.ftp.FtpConnector.getFtp(FtpConnector.java:172)
at org.mule.transport.ftp.FtpConnector.createFtpClient(FtpConnector.java:637)
at org.mule.transport.ftp.FtpMessageReceiver.listFiles(FtpMessageReceiver.java:134)
at org.mule.transport.ftp.FtpMessageReceiver.poll(FtpMessageReceiver.java:94)
at org.mule.transport.AbstractPollingMessageReceiver.performPoll(AbstractPollingMessageReceiver.java:216)
at org.mule.transport.PollingReceiverWorker.poll(PollingReceiverWorker.java:80)
at org.mule.transport.PollingReceiverWorker.run(PollingReceiverWorker.java:49)
at org.mule.transport.TrackingWorkManager$TrackeableWork.run(TrackingWorkManager.java:267)
at org.mule.work.WorkerContext.run(WorkerContext.java:286)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- <0x00000007817a3540> (a java.util.concurrent.ThreadPoolExecutor$Worker)
And the other hanging on pasv() call when using listFiles():
receiver.137" prio=10 tid=0x00007f23e433b000 nid=0x7c06 runnable [0x00007f24c2fee000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
- locked <0x0000000788847ed0> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
- locked <0x0000000788847ed0> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:294)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:490)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:534)
at org.apache.commons.net.ftp.FTP.sendCommand(FTP.java:583)
at org.apache.commons.net.ftp.FTP.pasv(FTP.java:882)
at org.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:497)
at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:2296)
at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:2269)
at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:2189)
at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:2132)
at org.mule.transport.ftp.FtpMessageReceiver.listFiles(FtpMessageReceiver.java:135)
at org.mule.transport.ftp.FtpMessageReceiver.poll(FtpMessageReceiver.java:94)
at org.mule.transport.AbstractPollingMessageReceiver.performPoll(AbstractPollingMessageReceiver.java:216)
at org.mule.transport.PollingReceiverWorker.poll(PollingReceiverWorker.java:80)
at org.mule.transport.PollingReceiverWorker.run(PollingReceiverWorker.java:49)
at org.mule.transport.TrackingWorkManager$TrackeableWork.run(TrackingWorkManager.java:267)
at org.mule.work.WorkerContext.run(WorkerContext.java:286)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- <0x0000000788832180> (a java.util.concurrent.ThreadPoolExecutor$Worker)
I think the problem is caused by the use of the default FTPClient constructor (extending SocketClient) in Mule default FtpConnectionFactory.
Note the setConnectTimeout() values seems to be used only when calling socket.connect(), but ignored on other operations using the same socket:
protected FTPClient createFtpClient()
{
FTPClient ftpClient = new FTPClient();
ftpClient.setConnectTimeout(connectionTimeout);
return ftpClient;
}
It uses the FTPClient() constructor, itself using SocketClient with a 0 timeout, defined when creating the socket.
public SocketClient()
{
...
_timeout_ = 0;
...
}
And then we call connec(), which calls _ connectAction()_.
In SocketClient:
protected void _connectAction_() throws IOException
{
...
_socket_.setSoTimeout(_timeout_);
...
}
In FTP, a new Reader is instanciated with our everlasting socket:
protected _connectAction_(){
...
_controlInput_ =
new BufferedReader(new InputStreamReader(_socket_.getInputStream(),
getControlEncoding()));
...
}
Then, when calling __getReply() function, we use this Reader-with-everlasting-socket:
private void __getReply() throws IOException
{
...
String line = _controlInput_.readLine();
...
}
Sorry for the long post, but I think this required correct explanations. A solution may be to call setSoTimeout() just after connect(), to define a Socket Timeout.
Having a default timeout does not seem an acceptable solution, as each users may have different needs and a default is not suitable in any case. https://issues.apache.org/jira/browse/NET-35
Finally, this raises 2 questions:
It seems like a bug to me, as it will completely stops FTP polling without giving error. What do you think?
What could be an easy way to avoid such situation? Calling setSoTimeout() with a custom FtpConnectionFactory? Am I missing a configuration or parameter somewhere?
Thanks by advance.
EDIT: I am using Mule CE Standalone 3.5.0, which seems to use Apache Commons Net 2.0. But looking in the code, Mule CE Standalone 3.7 with Commons Net 2.2 does not seem different. Here are the source codes involved:
https://github.com/mulesoft/mule/blob/mule-3.5.x/transports/ftp/src/main/java/org/mule/transport/ftp/FtpConnectionFactory.java
http://grepcode.com/file/repo1.maven.org/maven2/commons-net/commons-net/2.0/org/apache/commons/net/SocketClient.java
http://grepcode.com/file/repo1.maven.org/maven2/commons-net/commons-net/2.0/org/apache/commons/net/ftp/FTP.java
http://grepcode.com/file/repo1.maven.org/maven2/commons-net/commons-net/2.0/org/apache/commons/net/ftp/FTPClient.java

In an ideal world the timeout should not be necessary, but it looks like in your case it is.
Your description is very comprehensive, have you considered to raise a bug?
To workaround I would suggest first to use "Response Timeout" in the advanced tab. If that doesnt work I would use a service override, from there you should be able to override the receiver.

I reproduced the error in both my previous cases using MockFtpServer, and I was able to use a FtpConnectionFactory which seems to solve the issue.
public class SafeFtpConnectionFactory extends FtpConnectionFactory{
//define a default timeout
public static int defaultTimeout = 60000;
public static synchronized int getDefaultTimeout() {
return defaultTimeout;
}
public static synchronized void setDefaultTimeout(int defaultTimeout) {
SafeFtpConnectionFactory.defaultTimeout = defaultTimeout;
}
public SafeFtpConnectionFactory(EndpointURI uri) {
super(uri);
}
#Override
protected FTPClient createFtpClient() {
FTPClient client = super.createFtpClient();
//Define the default timeout here, which will be used by the socket by default,
//instead of the 0 timeout hanging indefinitely
client.setDefaultTimeout(getDefaultTimeout());
return client;
}
}
And then attaching it to my connector:
<ftp:connector name="archivingFtpConnector" doc:name="FTP"
pollingFrequency="${frequency}"
validateConnections="true"
connectionFactoryClass="my.comp.SafeFtpConnectionFactory">
<reconnect frequency="${reconnection.frequency}" count="${reconnection.attempt}"/>
</ftp:connector>
Using this configuration, a java.net.SocketTimeoutException will be thrown after the specified timeout, such as:
java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at org.apache.commons.net.ftp.FTP.__getReply(FTP.java:294)
at org.apache.commons.net.ftp.FTP._connectAction_(FTP.java:364)
at org.apache.commons.net.ftp.FTPClient._connectAction_(FTPClient.java:540)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:178)
at org.mule.transport.ftp.FtpConnectionFactory.makeObject(FtpConnectionFactory.java:33)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1188)
at org.mule.transport.ftp.FtpConnector.getFtp(FtpConnector.java:172)
at org.mule.transport.ftp.FtpConnector.createFtpClient(FtpConnector.java:637)
...
Otherwise, an attempt at connect() or pasv() would hang indefinitely without server response. I reproduced this exact behavior using mock FTP.
Note: I used setDefaultTimeout() as it seems to be the variable used with connect() and connectAction() (from SocketClient source):
public abstract class SocketClient
{
...
protected void _connectAction_() throws IOException
{
...
_socket_.setSoTimeout(_timeout_);
...
}
...
public void setDefaultTimeout(int timeout)
{
_timeout_ = timeout;
}
...
}
EDIT: For those who are interested, here is the test code for mock FTP used to reproduce the never answering server. The infinite loop is far from good practice though. It should be replaced with something like sleep with an enclosing Test class expecting a SocketTimeout exception and ensuring failure after a given timeout.
private static final int CONTROL_PORT = 2121;
public void startStubFtpServer(){
FakeFtpServer fakeFtpServer = new FakeFtpServer();
//define the command which should never be answered
fakeFtpServer.setCommandHandler(CommandNames.PASV, new EverlastingCommandHandler());
//fakeFtpServer.setCommandHandler(CommandNames.CONNECT, new EverlastingConnectCommandHandler());
//or any other command...
//server config
...
//start server
fakeFtpServer.setServerControlPort(CONTROL_PORT);
fakeFtpServer.start();
...
}
//will cause any command received to never have an answer
public class EverlastingConnectCommandHandler extends org.mockftpserver.core.command.AbstractStaticReplyCommandHandler{
#Override
protected void handleCommand(Command cmd, Session session, InvocationRecord rec) throws Exception {
while(true){
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
//TODO
}
}
}
}
public class EverlastingCommandHandler extends AbstractFakeCommandHandler {
#Override
protected void handle(Command cmd, Session session) {
while(true){
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
//TODO
}
}
}
};

Related

How to open a websocket directly to a wss: in Java

I am trying to establish a websocket connection in java to wss://data.tradingview.com/socket.io/websocket?from=chart%2F&date=2023_01_26-12_41&type=chart to acquire both historical and current chart data.
I have search and read through multiple posts and online resources and I am confused as to the correct procedure to connect to a websocket and acquire incoming data.
I found a post that is very close to what I'm trying to do that open a websocket and uses a ws as the target location. Here I've modified it slightly to implement the solution suggested.
import java.io.IOException;
import java.net.URI;
import javax.websocket.*;
#ClientEndpoint
public class ExampleTest2 {
Session session;
//Problem resolved, this original echo server is no longer operational
//private final static String url = "ws://echo.websocket.org:80";
private final static String url = "wss://websocket-echo.com:443/";
public static void main(String[] args) throws Exception, IOException {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
System.out.println("connecting...");
container.connectToServer(ExampleTest2.class,
URI.create(url));
System.out.println("Press ENTER key to exit.");
System.in.read();
}
#OnMessage
public void newMessage(String message, Session session) {
System.out.println(message);
}
#OnOpen
public void newConnection(Session session) throws IOException {
this.session = session;
System.out.println("The connection has been started");
session.getBasicRemote().sendText("hello");
}
#OnClose
public void disconnection() {
System.out.println("The connection has been ended");
}
}
This is the error output I receive.
Exception in thread "main" java.lang.RuntimeException: Could not find an implementation class.
at javax.websocket.ContainerProvider.getWebSocketContainer(ContainerProvider.java:73)
at ExampleTest2.main(ExampleTest2.java:14)
I continued to search and found a post about that specific error. Here Their solution is to use a different external library. I was using javax.websocket-api-1.1.jar now I've switched to their recommendation of tyrus, tyrus-standalone-client-2.1.2
However it's still not connecting properly.
Exception in thread "main" jakarta.websocket.DeploymentException: Handshake error.
at org.glassfish.tyrus.client.ClientManager$3$1.run(ClientManager.java:658)
at org.glassfish.tyrus.client.ClientManager$3.run(ClientManager.java:696)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.glassfish.tyrus.client.ClientManager$SameThreadExecutorService.execute(ClientManager.java:849)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:493)
at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:299)
at ExampleTest2.main(ExampleTest2.java:19)
Caused by: org.glassfish.tyrus.core.HandshakeException: Response code was not 101: 200.
at org.glassfish.tyrus.client.TyrusClientEngine.processResponse(TyrusClientEngine.java:301)
at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientFilter.handleHandshake(GrizzlyClientFilter.java:323)
at org.glassfish.tyrus.container.grizzly.client.GrizzlyClientFilter.handleRead(GrizzlyClientFilter.java:292)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:88)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:246)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:178)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:118)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:96)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:51)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:510)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:82)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:83)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:101)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:535)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:515)
at java.base/java.lang.Thread.run(Thread.java:832)
At this point I am very much in the weeds and I'm not even sure if I'm going in the right direction anymore I'm just following rabbit hole after rabbit hole. At this point I'm trying to resolve indirect problems that I'm not even sure if it is required to accomplish my task of opening a socket and acquiring data.
Any help like code examples or learning material directly related to the task would be greatly appreciated.
I just found the solution. The original server in the code is no longer operation. I used this one instead "wss://websocket-echo.com:443/"

too much waiting thread cause java heap dump in websocket client in java 8

Today my java application heap dump, and I copy the dump file from server analysis using visualVM, the log look like this:
"WebSocketClient-SecureIO-1" daemon prio=5 tid=888 WAITING
at sun.misc.Unsafe.park(Native Method)
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)
local variable: java.util.concurrent.locks.AbstractQueuedSynchronizer$Node#184
local variable: java.util.concurrent.locks.AbstractQueuedSynchronizer$Node#185
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
local variable: java.util.concurrent.CountDownLatch$Sync#36
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)
local variable: java.util.concurrent.CountDownLatch#35
at sun.nio.ch.PendingFuture.get(PendingFuture.java:180)
at org.apache.tomcat.websocket.AsyncChannelWrapperSecure$ReadTask.run(AsyncChannelWrapperSecure.java:269)
local variable: sun.nio.ch.PendingFuture#47
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
local variable: java.util.concurrent.ThreadPoolExecutor#1
local variable: org.apache.tomcat.websocket.AsyncChannelWrapperSecure$ReadTask#6
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
local variable: java.util.concurrent.ThreadPoolExecutor$Worker#1
at java.lang.Thread.run(Thread.java:748)
"WebSocketClient-SecureIO-2" daemon prio=5 tid=889 WAITING
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
local variable: java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject#5
local variable: java.util.concurrent.locks.AbstractQueuedSynchronizer$Node#114
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
local variable: java.util.concurrent.LinkedBlockingQueue#1
local variable: java.util.concurrent.atomic.AtomicInteger#56
local variable: java.util.concurrent.locks.ReentrantLock#9
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
local variable: java.util.concurrent.ThreadPoolExecutor$Worker#2
at java.lang.Thread.run(Thread.java:748)
"pool-87-thread-1" prio=5 tid=890 TIMED_WAITING
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
local variable: java.util.concurrent.locks.AbstractQueuedSynchronizer$Node#183
local variable: java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject#558
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
local variable: java.util.concurrent.locks.ReentrantLock#3654
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
local variable: java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue#1
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
local variable: java.util.concurrent.ScheduledThreadPoolExecutor#129
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
local variable: java.util.concurrent.ThreadPoolExecutor$Worker#3
at java.lang.Thread.run(Thread.java:748)
seems too many thread(maybe thousands) is waithing all the time, my memory now config to 500MB, and I now I have no idea why would this happen.This my websocket connection code:
public WebsocketClientEndpoint robotNewConnect(Long roomTypeId, String token, String userMark) {
WebsocketClientEndpoint clientEndPoint = null;
String websocketConnUrl = websocketUrl + "?token=" + token + "&roomTypeId=" + roomTypeId + "&robotFlag=1";
try {
String appMark = SessionUtil.getThreadLocal("appMark");
clientEndPoint = new WebsocketClientEndpoint(new URI(websocketConnUrl));
clientEndPoint.userSession.getUserProperties().put("userIdentity", userMark + "-" + appMark + "-" + roomTypeId);
clientEndPoint.addMessageHandler(message -> {
log.info("addMessageHandler:", message);
});
} catch (Exception e) {
log.error("Websocket", e);
}
return clientEndPoint;
}
I am searhing from internet and try to incrase my memory but problem still not resolve. what may cause this problem and what should I do to fix this?
what I have tried:
I follow to the souce code of tomcat-embed-websocket-9.0.30 where class WebsocketClientEndpoint belong. the connect was success, but stuck on this line code:
WsFrameClient wsFrameClient = new WsFrameClient(response, channel, wsSession, transformation);
and I step into the class and find the code was stuck in the dead lock code:
private void doResumeProcessing(boolean checkOpenOnError) {
while (true) {
switch (getReadState()) {
case PROCESSING:
if (!changeReadState(ReadState.PROCESSING, ReadState.WAITING)) {
continue;
}
resumeProcessing(checkOpenOnError);
return;
case SUSPENDING_PROCESS:
if (!changeReadState(ReadState.SUSPENDING_PROCESS, ReadState.SUSPENDED)) {
continue;
}
return;
default:
throw new IllegalStateException(
sm.getString("wsFrame.illegalReadState", getReadState()));
}
}
}
the read state getReadState is always PROCESSING, and the code loop forever, this is why so much waiting thread in dump file.
But now I do not know why the read state is PROCESSING and how to solve it? any one could help me?
I had faced similar problems with WebSocket when I was not properly closing the interrupted WebSocket connections and creating new one while retrying.
Steps I took to fix them:
Ensure that the WebSocketClient objects are garbage collected when connection is closed due to error. I was using Spring so I registered WebSocketClient as bean with prototype scope. So that when connection is closed Spring will do the cleanup.
#Bean
#Scope("prototype")
public StandardWebSocketClient webSocketClient() throws Exception {
StandardWebSocketClient standardWebSocketClient = new StandardWebSocketClient(clientContainer());
standardWebSocketClient.setTaskExecutor(webSocketTaskExecutor());
return standardWebSocketClient;
}
#Bean
public AsyncListenableTaskExecutor webSocketTaskExecutor() {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("webSocketTaskExecutor-");
executor.setConcurrencyLimit(20);
return executor;
}
Declared ClientContainer bean with destroyMethod in annotation.
#Bean(destroyMethod = "doStop")
public ClientContainer clientContainer() throws Exception {
WebSocketPolicy webSocketPolicy = WebSocketPolicy.newClientPolicy();
webSocketPolicy.setMaxTextMessageSize(1024000); //1MB
ClientContainer clientContainer = new ClientContainer(new SimpleContainerScope(webSocketPolicy));
clientContainer.start();
return clientContainer;
}
FYI I was using spring-boot-starter-websocket with jetty-server.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
These changes reduced the number of threads drastically.

IllegalStateException when HTTP Streaming using ResponseBodyEmitter in Spring-MVC

I'm using the newly added HTTP Streaming feature with ResponseBodyEmitter in Spring 4.2.0.BUILD-SNAPSHOT.
I would like to implement a long running persistent TCP connection on an undending stream of data between a (possibly java) client and server until the client breaks the connection. I would like to avoid using the websocket protocol.
If a client breaks the connection while streaming, a runtime IllegalStateException is thrown. I would like to handle this gracefully and cleanup the emitter. Short of catching a runtime exception, is there any way to gracefully handle this?
I have to specify an artifically high timeout value on the emitter for a "persistent" connection. Can I set no timeout?
The webapp is deployed on apache-tomcat-7.0.62.
Relevant code as follows:
#RequestMapping(value = "stream", method = RequestMethod.GET)
public ResponseBodyEmitter handleStreaming() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter(timeout);
emitters.add(emitter);
emitter.onCompletion(new Runnable() {
#Override
public void run() {
emitters.remove(emitter);
}
});
emitter.onTimeout(new Runnable() {
#Override
public void run() {
emitters.remove(emitter);
}
});
return emitter;
}
.
while (true) {
for (Iterator<ResponseBodyEmitter> iterator = emitters.iterator(); iterator.hasNext();) {
ResponseBodyEmitter emitter = iterator.next();
try {
emitter.send("data...", MediaType.TEXT_PLAIN);
} catch (IOException | IllegalStateException e) {
LOGGER.error(e);
iterator.remove();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LOGGER.error(e);
}
}
Logs:
INFO: An error occurred in processing while on a non-container thread. The connection will be closed immediately
java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
at java.net.SocketOutputStream.write(SocketOutputStream.java:153)
at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:215)
at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:480)
at org.apache.coyote.http11.InternalOutputBuffer.flush(InternalOutputBuffer.java:119)
at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:801)
at org.apache.coyote.Response.action(Response.java:172)
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:363)
at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:331)
at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:101)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:297)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)
at org.springframework.util.StreamUtils.copy(StreamUtils.java:106)
at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:109)
at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:40)
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:193)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.sendInternal(ResponseBodyEmitterReturnValueHandler.java:157)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.send(ResponseBodyEmitterReturnValueHandler.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.sendInternal(ResponseBodyEmitter.java:180)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.send(ResponseBodyEmitter.java:164)
....
[ERROR] [02/07/15 18:11 PM] [Controller$TestResponseBodyEmitter:74] - java.lang.IllegalStateException: The request associated with the AsyncContext has already completed processing.
Command:
curl http://localhost:8080/myapp/stream -v -N
data...data...
Ctrl-C
According to the Javadoc of the ResponseBodyEmitter's constructor (found here).
Create a ResponseBodyEmitter with a custom timeout value. By default
not set in which case the default configured in the MVC Java Config or
the MVC namespace is used, or if that's not set, then the timeout
depends on the default of the underlying server.
Therefore do give a timeout when you create the c instance.
PS: In my environment ResponseBodyEmitter#getTimeout() returned null; this does not mean that there is an infinite timeout. On the contrary after 5-10 sec the connection timed out.

Exception during Netty server shutdown

I have application running on Tomcat. I use Netty 4 for websocket handling.
Netty server run in ServletContextListener in contextInitialized method and stop in contextDestroyed.
This my class for Netty server:
public class WebSocketServer {
private final int port;
private final EventLoopGroup bossGroup;
private final EventLoopGroup workerGroup;
private Channel serverChannel;
public WebSocketServer(int port) {
this.port = port;
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
}
public void run() throws Exception {
final ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new WebSocketServerInitializer());
serverChannel = b.bind(port).sync().channel();
System.out.println("Web socket server started at port " + port + '.');
System.out
.println("Open your browser and navigate to http://localhost:"
+ port + '/');
}
public void stop() {
if (serverChannel != null) {
ChannelFuture chFuture = serverChannel.close();
chFuture.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
shutdownWorkers();
}
});
} else {
shutdownWorkers();
}
}
private void shutdownWorkers() {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
It's work fine after running, but when I try stop Tomcat I get exception:
INFO: Illegal access: this web application instance has been stopped already. Could not load io.netty.util.concurrent.DefaultPromise$3. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1610)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1569)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:592)
at io.netty.util.concurrent.DefaultPromise.setSuccess(DefaultPromise.java:403)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:139)
at java.lang.Thread.run(Thread.java:662)
After Tomcat hangs up.
What can be reason?
I assume you call shutdownWorkers() somewhere from Servlet.destroy() or use some other mechanism that ensures your Server goes down when servlet stops / unloads.
Then you need to do
void shutdownWorkers() {
Future fb = trbossGroup.shutdownGracefully();
Future fw = workerGroup.shutdownGracefully();
try {
fb.await();
fw.await();
} catch (InterruptedException ignore) {}
}
It is because shutdownGracefully() returns a Future, and, well, without waiting for it to come, you leave things that try to close the connections in very stressful environment. It also makes sense to first initiate all shutdown's and then wait till futures are awailable, this way it all runs in parallel and happens faster.
It fixed the issue for me. Obviously, you can make it nicer to your system without swallowing InterruptedException and wrapping each call in a nice method and putting reasonable timeout for each await(). Nice excercise in general, but in reality most probably you wouldn't care at this point in your code.
Side note: and yes, for WebSockets you will be better off with Tomcat's native, standards-compliant and robust implementation. Netty is awseome for many other things, but would be a wrong tool here.

How to deal with SocketTimeoutException inside Java Code / Webservices

This is my Method code shown below
Actually this serves as a Webservices Method .
public Response getData(ServiceRequest request)
{
try
{
final boolean toProceedorNot = validate(legdata);
if (!toProceedorNot) {
status.setErrorText(errorText);
return response;
}
else {
// Some Processing is done here
response.setMessage(result);
}
}
catch(Exception e)
{
errorText = e.getMessage().toString();
status.setErrorText(errorText);
response.setStatus(status);
}
return response;
}
If the execution of the Method takes longer time , an SocketTimeoutException will be thrown by the Apache CXF Framework
at java.lang.Thread.run(Thread.java:662)
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)
at java.io.BufferedInputStream.read(BufferedInputStream.java:317)
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:695)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:640)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1195)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:379)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:2034)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:2013)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1938)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:66)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:626)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
My Question is that, even though an SocketTimeoutException is thrown, it is not coming inside the Exception block.
I am not sure whether this Exception , should be handled by whom ( The client or inside the Webservices Implementation method )
But as a Webservice provider , please tell me how to deal with this Exception ??

Categories