How one can know if the client has closed the connection - java

I've been playing with the new Servlet 3.0 async features with Tomcat 7.0.4. I found this Chat Application, that lets clients hang on GET request to get message updates. This is working just fine when it comes to receiving the messages.
The problem arises when the client is disconnected i.e. the user closes the browser. It seems that the server does not raise IOException, even though the client has disconnected. The message thread (see the source code from link above) is happily writing to all stored AsyncContext's output streams.
Is this a Tomcat bug? or am I missing something here? If this is not a bug, then how I'm supposed to detect whether the client has closed the connection?

The code there at line 44 - 47 is taking care of it,
} catch(IOException ex) {
System.out.println(ex);
queue.remove(ac);
}
And here too at 75 - 83, using timeout thingie,
req.addAsyncListener(new AsyncListener() {
public void onComplete(AsyncEvent event) throws IOException {
queue.remove(ac);
}
public void onTimeout(AsyncEvent event) throws IOException {
queue.remove(ac);
}
});
EDIT: After getting a little more insight.
Tomcat 7.0.4 is still in beta. So, you can expect such behaviour
I tried hard but can't find the method setAsyncTimeout() in the doc, neither here, nor here. So, I think they dropped it completely in the final version due to some unknown valid reason
The example states, "why should I use the framework instead of waiting for Servlet 3.0 Async API". Which infers that its written before the final thingie
So, what I can say, after combining all these fact, that you are trying to work with the thing that is broken in a sense. That also, may be, the reason for different and weird results.

Related

Avoid logs full of java.io.IOException: Broken pipe

I am using Server-Sent events on one browser, and a spring boot application on the back end. When I shot down the client, I get the next exception:
14:35:09.458 [http-nio-8084-exec-25] ERROR o.a.c.c.C.[Tomcat].[localhost] - Exception Processing ErrorPage[errorCode=0, location=/error]
org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
I understand this is the expected behavior; on the other hand, my application works fine, but I have awful logs full of those exceptions. I guess this is caused by Tomcat. Is there a way to catch these exceptions, or at least to prevent Tomcat from writing this exception stack trace to the log? I mean, without modifying Tomcat's code.
To prevent this exception in logs you can try some changes in your code that performs push on client. Here is my example. I listen to api called and then it called I push socket to the client. I think you could understand the code:
#GetMapping("/api/status/{groupId}/{groupstatusId}")
#ResponseStatus(HttpStatus.NO_CONTENT)
#ExceptionHandler(IOException.class)
public void listenToNewStatus(#PathVariable Long groupId, #PathVariable String groupstatusId, IOException e) {
Group group = groupDTOService.findById(groupId);
if (group != null) {
if (StringUtils.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(e), "Broken pipe")) {
logger.info("broken pipe");
} else {
template.convertAndSend("/topic/callstatus/" + group.getUser().getId(), groupstatusId);
}
}
In this code to prevent broken pipe I add annotation #ExceptionHandler(IOException.class) and check if exception contains broken pipe then nothing else send message to client.
I see that this question is quite old, but in case someone is still looking for an answer, there are a few blog posts on how to mute ClientAbortException so it doesn't flood your logs.
https://tutorial-academy.com/jersey-workaround-clientabortexception-ioexception/

java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED

I have a service that is expected to execute requests at ~5 or more requests/min. This service depends on Apache AsyncHttpClient. After every few minutes, the clients hits some condition which causes java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED. All requests to the client start failing with same exception message. After service is restarted, this cycle repeats.
It is really hard to debug this problem as the request execution failure surprisingly does not cause a callback to the failed() method of the AsyncResponse.
From what I could gather, there has been a fix HTTPCORE-370 in HttpCore NIO which solved a similar problem in 4.3.2. I am using the following version -
commons-httpclient-3.1.jar
httpasyncclient-4.1.1.jar
httpcore-4.4.4.jar
httpcore-nio-4.4.4.jar
Yet seeing this problem.
I've been dealing with this same exception in my application, and I finally found a helpful suggestion from this post - http://httpcomponents.10934.n7.nabble.com/I-O-reactor-status-STOPPED-td29059.html
You can use #getAuditLog() method of the I/O reactor to find out exactly
what exception caused it to terminate.
If you keep a reference to your ConnectionManager's IOReactor, you can call this method to get insight into the actual problem:
http://hc.apache.org/httpcomponents-core-4.4.x/httpcore-nio/apidocs/org/apache/http/impl/nio/reactor/AbstractMultiworkerIOReactor.html#getAuditLog()
Turns out I was doing something incredibly stupid in my own code. But I couldn't figure it out until I read the audit log.
If you see OutOfMemoryError before this, try this
-XX:MaxDirectMemorySize=512M
See https://issues.apache.org/jira/browse/HTTPASYNC-104
In my case, using Elasticsearch high level client, this exception is due to esclient.indexAsync(indexRequest,RequestOptions.DEFAULT,null)
I fixed it by add an action listeners in all async requests like this
esclient.indexAsync(indexRequest,RequestOptions.DEFAULT,
new ActionListener<IndexResponse>() {
#Override
public void onResponse(IndexResponse response) {
}
#Override
public void onFailure(Exception e) {
});
We had encountered the same issue and after lots of digging we found that a proper IOReactorExceptionHandler needs to provided to the HttpAsyncClient to avoid this. It's unfortunate that it's not well covered in the documentation.
Below is a snippet of our code where a more robust client builder tries to add the exception handler. Note that IOExceptions would still stop the I/O reactor as they might imply underlying network communication failures. You may adjust according to your unique use cases.
public RobustCloseableHttpAsyncClientBuilder withDefaultExceptionHandler() {
return withExceptionHandler(
new IOReactorExceptionHandler() {
#Override
public boolean handle(RuntimeException ex) {
logger.error(
"RuntimeException occurs in callback, handled by default exception handler and the I/O reactor will be resumed.",
ex);
return true;
}
#Override
public boolean handle(IOException ex) {
logger.error(
"IOException occurs in callback, handled by default exception handler and the I/O reactor will be stopped.",
ex);
return false;
}
});
}
Read this issue report in elasticsearch on Github for more exposure.

Java ObjectOutputStream reset error

my project consists of 2 parts: server side and client side. When I start server side everything is OK, but when I start client side from time to time I get this error:
java.io.IOException: stream active
at java.io.ObjectOutputStream.reset(Unknown Source)
at client.side.TcpConnection.sendUpdatedVersion(TcpConnection.java:77)
at client.side.Main.sendCharacter(Main.java:167)
at client.side.Main.start(Main.java:121)
at client.side.Main.main(Main.java:60)
When I tried to run this project on the other pc this error occurred even more frequently. In Java docs I found this bit.
Reset may not be called while objects are being serialized. If called
inappropriately, an IOException is thrown.
And this is the function where error is thrown
void sendUpdatedVersion(CharacterControlData data) {
try {
ServerMessage msg = new ServerMessage(SEND_MAIN_CHARACTER);
msg.setCharacterData(data);
oos.writeObject(msg);
oos.reset();
} catch (IOException e) {
e.printStackTrace();
}
}
I tried to put flush() but that didn't help. Any ideas? Besides, no errors on server side.
I think you're misunderstanding what reset() does. It resets the stream to disregard any object instances previously written to it. This is pretty clearly not what you want in your case, since you're sending an object to the stream and then resetting straight away, which is pointless.
It looks like all you need is a flush(); if that's insufficient then the problem is on the receiving side.
I think you are confusing close() with reset().
use
oos.close();
instead of oos.reset();
calling reset() is a perfectly valid thing to want to do. It is possible that 'data' is reused, or some field in data is reused, and the second time he calls sendUpdatedVersion, that part is not sent. So those who complain that the use is invalid are not accurate. Now as to why you are getting this error message
What the error message is saying is that you are not at the top level of your writeObject call chain. sendUpdatedVersion must be being called from an method that was called from another writeObject.
I'm assuming that some object is implementing a custom writeObject() and that method, is calling this method.
So you have to differentiate when sendUpdatedVersion is being called at the top level of the call chain and only use reset() in those cases.

Binary Partial Writing Error

The code directly below attempts to send a message through a Java websocket; however, it occasionally produces the error (shown in the error stack below). Could anyone suggest what the problem'solution?
Code:
if(currSession != null && currSession.isOpen()) {
try {
currSession.sendMessage(new BinaryMessage(flowTable.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
Error Stack:
java.lang.IllegalStateException: The remote endpoint was in state [BINARY_PARTIAL_WRITING] which is an invalid state for called method
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.checkState(WsRemoteEndpointImplBase.java:1015)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.binaryPartialStart(WsRemoteEndpointImplBase.java:963)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialBytes(WsRemoteEndpointImplBase.java:140)
at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendBinary(WsRemoteEndpointBasic.java:56)
at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.sendBinaryMessage(StandardWebSocketSession.java:202)
at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:107)
at com.hp.fucms.impl.TopoRestController.setFlowTable(TopoRestController.java:147)
Wrap your code in a synchronized method and funnel all calls through this new method. It appears the tomcat web socket cannot handle multiple messages being placed on the same websocket session at the same time. I have code which has been running flawlessly under Glassfish and fell apart instantly when I moved to Tomcat. I then altered my code as explained above and all my problems went away....and there was much rejoicing.

Apache Camel, send message when server starts and stops

I have a simply camel MINA server using the JAVA DSL, and I am running like the example documented here:
Running Camel standalone and have it keep running in JAVA
MINA 2 Component
Currently this server receives reports from a queue, updates them, and then sends them away to the next server. A very simple code:
public class MyApp_B {
private Main main;
public static void main(String... args) throws Exception {
MyApp_B loadbalancer = new MyApp_B();
loadbalancer.boot();
}
public void boot() throws Exception {
main = new Main();
main.enableHangupSupport();
main.addRouteBuilder(
new RouteBuilder(){
#Override
public void configure() throws Exception {
from("mina:tcp://localhost:9991")
.setHeader("minaServer", constant("localhost:9991"))
.beanRef("service.Reporting", "updateReport")
.to("direct:messageSender1");
from("direct:messageSender1")
.to("mina:tcp://localhost:9993")
.log("${body}");
}
}
);
System.out.println("Starting Camel MyApp_B. Use ctrl + c to terminate the JVM.\n");
main.run();
}
}
Now, I would like to know if it is possible to do two things:
Make this server send a message to a master server when it starts running. This is an "Hello" message with this server's information basically.
Tell the master server to forget him when I shut it down pressing CTRL+C or doing something else.
I have also read this:
http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/support/ServiceSupport.html#doStart%28%29
technically, by overriding the doStart and doStop methods I should get the intended behavior, however, those methods (specially the doStop method) don't work at all.
Is there a way to do this ? If yes how? If not, what are my options?
Thanks in advance, Pedro.
The code does work properly after all. The problem is my IDE, Eclipse. When using the Terminate button, Eclipse simply kills the process instead of send the CTRL+C signal to it. Furthermore it looks like Eclipse has no way of being able to send a CTRL+C signal to a process running on its console.
I have also created a discussion on Eclipse's official forums:
http://www.eclipse.org/forums/index.php/m/1176961/#msg_1176961
And may it some day help some one in a situation similar to mine.

Categories