> I have a problem with my java application related to HTTP communication.
>
> Application description:
>
> 1. Client – server. Server is running in servlet container. We
> use Tomcat.
>
> Client use java HTTP library to communicate with the server.
>
> 2. When client establish connection to the server it sends GET
> request (keepalive) and server creates AsyncContext for the client
> with 10 days timeout.
>
> 3. After connection established server periodically sends data
> to the client using AsyncContext and on the client side there is
> special thread which reads and processes the data.
>
> Client reading thread example:
>
> private class GetRunnable implements Runnable {
>
> private final HttpURLConnection httpConn;
> private final MyConnection myConn;
>
> public GetRunnable(final MyConnection myConn) throws
> IOException, AppException {
> this.myConn = myConn;
> final String jSession = myConn.getJSession();
> httpConn = openHttpUrlConnection(false,
> httpPostTimeout, jSession);
> httpConn.connect();
> final int respCode = httpConn.getResponseCode();
> if (respCode != HttpURLConnection.HTTP_OK) {
> String info = "Incorrect response from server,
> responseCode:" + respCode + ", message:" +
httpConn.getResponseMessage();
> log.error(info);
> throw new AppException(info);
> } else {
> log.trace("GET connected");
> }
> }
>
> @Override
> public void run() {
> try (final BufferedReader reader = new BufferedReader
> (new InputStreamReader(httpConn.getInputStream(), "UTF-8")) ) {
> log.info("doGet STARTED");
> final Thread t = Thread.currentThread();
> while (!t.isInterrupted()) {
> log.trace("before readline");
> final String line = reader.readLine();
> log.trace("after readline: [" + line + "]");
>
> //INFO: DATA PROCESSING HERE
> }
> } catch (IOException e) {
> log.error("Error while doGet");
> } catch (Throwable e) {
> log.debug("doGet STOPED...");
> log.error("Error while read input msg, do logoff",
e);
> logoff();
> throw e;
> }
> log.debug("doGet STOPED...");
> myCallback.onGetClose(myConn, connInfo);
> }
> }
>
> Server side code to push data looks like this:
>
> protected int sendMsgs(final MyConnection myConn, final String info) {
>
> int res;
> try {
> final AsyncContext lAc = _ac;
> final ServletRequest req = lAc.getRequest();
> if (!req.isAsyncStarted()) {
> log.debug("AsyncStarted=false on begin, uid:" +
> uid + ", sid:" + sid);
> return -1;
> }
>
> final ServletResponse res = lAc.getResponse();
> ServletOutputStream os = null;
> final String text = getData(myConn);
>
> if (os == null)
> os = res.getOutputStream();
>
> write2stream(os, text, 0x4000);
> os.println();
>
> if (os != null)
> os.flush();
> } catch(IOException ex) {
> log.error("Notify failed");
> } catch (InterruptedException e) {
> log.error("Notify failed");
> Thread.currentThread().interrupt();
> } catch (Throwable e) {
> log.error("Notify failed");
> logoff(true);
> }
>
> return res;
> }
>
> public static void write2stream(final OutputStream outputStream,
> final String text, int bufferSize) throws IOException {
> final CharsetEncoder encoder = getHttpEncodingCharset
> ().newEncoder();
>
> final int fullLen = text.length();
> final int byteBufferSize = Math.min(bufferSize, fullLen);
>
> final int en = (int)(byteBufferSize * (double)
> encoder.maxBytesPerChar());
> final byte[] byteArray = new byte[en];
>
> final ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
> final CharBuffer charBuffer = CharBuffer.wrap(text);
> for(int pos=0, len=byteBufferSize;
> pos < fullLen;
> pos=len, len=Math.min(pos+byteBufferSize,
> fullLen)) {
>
> try {
> final CharBuffer window = charBuffer.subSequence(pos,
len);
>
> CoderResult res = encoder.encode(window,
> byteBuffer, true);
>
> if (!res.isUnderflow())
> res.throwException();
>
> res = encoder.flush(byteBuffer);
>
> if (!res.isUnderflow())
> res.throwException();
>
> } catch (final CharacterCodingException x) {
> throw new Error(x);
> }
>
> outputStream.write(byteArray, 0, byteBuffer.position());
>
> byteBuffer.clear();
> encoder.reset();
> }
> }
>
> Usually this code works fine but it there is no data from server to
> client for 1 day and after 24 hours (can be 16 or 12) data appears,
> server cannot send data to the client.
> In the log there is no error. It is written that everything flushed
> but client still waiting for data in “final String line =
reader.readLine();”
> When 2nd portion of data is sent by the server, then during flush I
> see the following error
>
> 2016-01-26 00:00:00,051|INFO |GWNotify-2/50 |ClientAbort
> 2016-01-26 00:00:00,051|TRACE|GWNotify-2/50 |
> ClientAbortException:java.io.IOException: APR error: -32
> org.apache.catalina.connector.ClientAbortException:
> java.io.IOException: APR error: -32
> at org.apache.catalina.connector.OutputBuffer.doFlush
> (OutputBuffer.java:353) ~[catalina.jar:8.0.30]
> at org.apache.catalina.connector.OutputBuffer.flush
> (OutputBuffer.java:317) ~[catalina.jar:8.0.30]
> at
> org.apache.catalina.connector.CoyoteOutputStream.flush
> (CoyoteOutputStream.java:110) ~[catalina.jar:8.0.30]
>
> Caused by: java.io.IOException: APR error: -32
> at
> org.apache.coyote.http11.InternalAprOutputBuffer.writeToSocket
> (InternalAprOutputBuffer.java:291) ~[tomcat-coyote.jar:8.0.30]
> at
> org.apache.coyote.http11.InternalAprOutputBuffer.writeToSocket
> (InternalAprOutputBuffer.java:244) ~[tomcat-coyote.jar:8.0.30]
> at
> org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer
> (InternalAprOutputBuffer.java:213) ~[tomcat-coyote.jar:8.0.30]
> at
> org.apache.coyote.http11.AbstractOutputBuffer.flush
> (AbstractOutputBuffer.java:305) ~[tomcat-coyote.jar:8.0.30]
> at
> org.apache.coyote.http11.AbstractHttp11Processor.action
> (AbstractHttp11Processor.java:765) ~[tomcat-coyote.jar:8.0.30]
> at org.apache.coyote.Response.action(Response.java:
> 177) ~[tomcat-coyote.jar:8.0.30]
> at org.apache.catalina.connector.OutputBuffer.doFlush
> (OutputBuffer.java:349) ~[catalina.jar:8.0.30]
> ... 6 more
>
> Finally after I stop server I see that GetRunnable client thread is
> still alive and waiting data from the server in “final String line =
> reader.readLine();”!!!
Hi,
I would send some keep-alive data (e.g. current timestamp) maybe once per
day or once every 5 hours. The client can just dismiss that data. This
might help in some situations but there is no 100% guarantee that it works
always.
Regards,
Christoph
This Email was scanned by Sophos Anti Virus