You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Daniel Mikusa <dm...@gopivotal.com> on 2013/08/16 18:08:36 UTC
Non-Blocking IO Issue
I have a simple echo servlet which uses the Servlet 3.1 Non-Blocking IO API. It creates a ReadListener, that reads the request's input, buffers it, creates a WriteListener that takes the buffered input and echoes it back to the response.
Most of the time this works OK, but when I send a request with no input data I get the following error.
6-Aug-2013 11:18:09.523 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 1452 ms
java.lang.IllegalStateException: The non-blocking write listener has already been set
at org.apache.coyote.Response.setWriteListener(Response.java:583)
at org.apache.catalina.connector.OutputBuffer.setWriteListener(OutputBuffer.java:665)
at org.apache.catalina.connector.CoyoteOutputStream.setWriteListener(CoyoteOutputStream.java:162)
at com.pivotal.demos.nbio.EchoNbioServlet$1.onAllDataRead(EchoNbioServlet.java:77)
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:384)
at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1607)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:622)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1592)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1550)
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:724)
16-Aug-2013 11:20:13.205 SEVERE [http-nio-8080-exec-7] org.apache.catalina.connector.CoyoteAdapter.asyncDispatch Exception while processing an asynchronous request
java.lang.NullPointerException
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:429)
at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1607)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:622)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1592)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1550)
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:724)
Looking into it, what appears to be happening is that the "ReadListener.onAllDataRead()" method is being called twice. Because I'm setting the WriteListener in the "onAllDataRead" method, the second invocation of "onAllDataRead" causes the IllegalStateException. I was assuming that "onAllDataRead" should only be called once and so it would be OK to set the WriteListener in that method. Can "onAllDataRead" be legitimately called multiple times?
Thanks
Dan
Code below…
Servlet
----------
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. Start Async
final AsyncContext asyncContext = req.startAsync();
final ServletInputStream servletInputStream = asyncContext.getRequest().getInputStream();
// 2. Add Read Listener to get user's input
ReadListener listener = new ReadListener() {
private StringBuilder sb = new StringBuilder(); // buffer user's request
public void onDataAvailable() throws IOException {
// 4. Read all the data that is available, may be called multiple times
try {
byte[] b = new byte[8192];
int read = 0;
do {
read = servletInputStream.read(b);
if (read == -1) {
break;
}
sb.append(new String(b, 0, read));
System.out.println("Buffer is now [" + sb.length() + "] characters");
} while (servletInputStream.isReady());
} catch (Exception ex) {
ex.printStackTrace(System.err);
asyncContext.complete();
}
}
// 5. Called when all data has been read
public void onAllDataRead() throws IOException {
final ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream();
final String output = sb.toString();
// 6. Configure a write listener to echo the response
WriteListener listener = new WriteListener() {
public void onWritePossible() throws IOException {
// 7. Write output
if (outputStream.isReady()) {
outputStream.print(output);
}
// 8. Call complete, to signal we are done
asyncContext.complete();
}
public void onError(Throwable throwable) {
throwable.printStackTrace(System.err);
asyncContext.complete();
}
};
outputStream.setWriteListener(listener);
}
public void onError(Throwable throwable) {
throwable.printStackTrace(System.err);
asyncContext.complete();
}
};
// 3. Add listener, starts Non-blocking IO support
servletInputStream.setReadListener(listener);
}
Client
--------
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:8080/tomcat-8-demos/non-blocking-io/EchoNbioServlet");
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setReadTimeout(1000000);
connection.connect();
OutputStream os = null;
try {
os = connection.getOutputStream();
// do nothing
} finally {
if (os != null) {
try {
os.close();
} catch (IOException ioe) {
// Ignore
}
}
}
int rc = connection.getResponseCode();
InputStream is;
if (rc < 400) {
is = connection.getInputStream();
} else {
is = connection.getErrorStream();
}
StringBuilder sb = new StringBuilder();
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(is);
byte[] buf = new byte[2048];
int rd = 0;
while((rd = bis.read(buf)) > 0) {
sb.append(new String(buf, "utf-8"));
}
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
// Ignore
}
}
}
System.out.println("Resp: [" + sb.toString() + "]");
}
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org
Re: Non-Blocking IO Issue
Posted by Daniel Mikusa <dm...@gopivotal.com>.
On Aug 16, 2013, at 12:32 PM, Mark Thomas <ma...@apache.org> wrote:
> On 16/08/2013 17:08, Daniel Mikusa wrote:
>> I have a simple echo servlet which uses the Servlet 3.1 Non-Blocking IO API. It creates a ReadListener, that reads the request's input, buffers it, creates a WriteListener that takes the buffered input and echoes it back to the response.
>>
>> Most of the time this works OK, but when I send a request with no input data I get the following error.
>>
>> 6-Aug-2013 11:18:09.523 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 1452 ms
>> java.lang.IllegalStateException: The non-blocking write listener has already been set
>> at org.apache.coyote.Response.setWriteListener(Response.java:583)
>> at org.apache.catalina.connector.OutputBuffer.setWriteListener(OutputBuffer.java:665)
>> at org.apache.catalina.connector.CoyoteOutputStream.setWriteListener(CoyoteOutputStream.java:162)
>> at com.pivotal.demos.nbio.EchoNbioServlet$1.onAllDataRead(EchoNbioServlet.java:77)
>> at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:384)
>> at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1607)
>> at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:622)
>> at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
>> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1592)
>> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1550)
>> 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:724)
>> 16-Aug-2013 11:20:13.205 SEVERE [http-nio-8080-exec-7] org.apache.catalina.connector.CoyoteAdapter.asyncDispatch Exception while processing an asynchronous request
>> java.lang.NullPointerException
>> at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:429)
>> at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1607)
>> at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:622)
>> at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
>> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1592)
>> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1550)
>> 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:724)
>>
>> Looking into it, what appears to be happening is that the "ReadListener.onAllDataRead()" method is being called twice. Because I'm setting the WriteListener in the "onAllDataRead" method, the second invocation of "onAllDataRead" causes the IllegalStateException. I was assuming that "onAllDataRead" should only be called once and so it would be OK to set the WriteListener in that method. Can "onAllDataRead" be legitimately called multiple times?
>
> No. Any chance you could convert the code below into a Tomcat unit test?
Sure. I've opened a bug report and attached it there. Here's the link.
https://issues.apache.org/bugzilla/show_bug.cgi?id=55438
Dan
>
> Mark
>
>>
>> Thanks
>>
>> Dan
>>
>> Code below…
>>
>> Servlet
>> ----------
>>
>> protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
>> // 1. Start Async
>> final AsyncContext asyncContext = req.startAsync();
>> final ServletInputStream servletInputStream = asyncContext.getRequest().getInputStream();
>>
>> // 2. Add Read Listener to get user's input
>> ReadListener listener = new ReadListener() {
>>
>> private StringBuilder sb = new StringBuilder(); // buffer user's request
>>
>> public void onDataAvailable() throws IOException {
>> // 4. Read all the data that is available, may be called multiple times
>> try {
>> byte[] b = new byte[8192];
>> int read = 0;
>> do {
>> read = servletInputStream.read(b);
>> if (read == -1) {
>> break;
>> }
>> sb.append(new String(b, 0, read));
>> System.out.println("Buffer is now [" + sb.length() + "] characters");
>> } while (servletInputStream.isReady());
>> } catch (Exception ex) {
>> ex.printStackTrace(System.err);
>> asyncContext.complete();
>> }
>> }
>>
>> // 5. Called when all data has been read
>> public void onAllDataRead() throws IOException {
>> final ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream();
>>
>> final String output = sb.toString();
>>
>> // 6. Configure a write listener to echo the response
>> WriteListener listener = new WriteListener() {
>> public void onWritePossible() throws IOException {
>> // 7. Write output
>> if (outputStream.isReady()) {
>> outputStream.print(output);
>> }
>>
>> // 8. Call complete, to signal we are done
>> asyncContext.complete();
>> }
>>
>> public void onError(Throwable throwable) {
>> throwable.printStackTrace(System.err);
>> asyncContext.complete();
>> }
>> };
>> outputStream.setWriteListener(listener);
>> }
>>
>> public void onError(Throwable throwable) {
>> throwable.printStackTrace(System.err);
>> asyncContext.complete();
>> }
>> };
>>
>> // 3. Add listener, starts Non-blocking IO support
>> servletInputStream.setReadListener(listener);
>> }
>>
>> Client
>> --------
>>
>> public static void main(String[] args) throws Exception {
>> URL url = new URL("http://localhost:8080/tomcat-8-demos/non-blocking-io/EchoNbioServlet");
>> HttpURLConnection connection =
>> (HttpURLConnection) url.openConnection();
>> connection.setDoOutput(true);
>> connection.setReadTimeout(1000000);
>> connection.connect();
>> OutputStream os = null;
>> try {
>> os = connection.getOutputStream();
>> // do nothing
>> } finally {
>> if (os != null) {
>> try {
>> os.close();
>> } catch (IOException ioe) {
>> // Ignore
>> }
>> }
>> }
>>
>> int rc = connection.getResponseCode();
>> InputStream is;
>> if (rc < 400) {
>> is = connection.getInputStream();
>> } else {
>> is = connection.getErrorStream();
>> }
>>
>> StringBuilder sb = new StringBuilder();
>> BufferedInputStream bis = null;
>> try {
>> bis = new BufferedInputStream(is);
>> byte[] buf = new byte[2048];
>> int rd = 0;
>> while((rd = bis.read(buf)) > 0) {
>> sb.append(new String(buf, "utf-8"));
>> }
>> } finally {
>> if (bis != null) {
>> try {
>> bis.close();
>> } catch (IOException e) {
>> // Ignore
>> }
>> }
>> }
>> System.out.println("Resp: [" + sb.toString() + "]");
>> }
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
>> For additional commands, e-mail: users-help@tomcat.apache.org
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org
Re: Non-Blocking IO Issue
Posted by Mark Thomas <ma...@apache.org>.
On 16/08/2013 17:08, Daniel Mikusa wrote:
> I have a simple echo servlet which uses the Servlet 3.1 Non-Blocking IO API. It creates a ReadListener, that reads the request's input, buffers it, creates a WriteListener that takes the buffered input and echoes it back to the response.
>
> Most of the time this works OK, but when I send a request with no input data I get the following error.
>
> 6-Aug-2013 11:18:09.523 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 1452 ms
> java.lang.IllegalStateException: The non-blocking write listener has already been set
> at org.apache.coyote.Response.setWriteListener(Response.java:583)
> at org.apache.catalina.connector.OutputBuffer.setWriteListener(OutputBuffer.java:665)
> at org.apache.catalina.connector.CoyoteOutputStream.setWriteListener(CoyoteOutputStream.java:162)
> at com.pivotal.demos.nbio.EchoNbioServlet$1.onAllDataRead(EchoNbioServlet.java:77)
> at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:384)
> at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1607)
> at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:622)
> at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1592)
> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1550)
> 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:724)
> 16-Aug-2013 11:20:13.205 SEVERE [http-nio-8080-exec-7] org.apache.catalina.connector.CoyoteAdapter.asyncDispatch Exception while processing an asynchronous request
> java.lang.NullPointerException
> at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:429)
> at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1607)
> at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:622)
> at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1592)
> at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1550)
> 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:724)
>
> Looking into it, what appears to be happening is that the "ReadListener.onAllDataRead()" method is being called twice. Because I'm setting the WriteListener in the "onAllDataRead" method, the second invocation of "onAllDataRead" causes the IllegalStateException. I was assuming that "onAllDataRead" should only be called once and so it would be OK to set the WriteListener in that method. Can "onAllDataRead" be legitimately called multiple times?
No. Any chance you could convert the code below into a Tomcat unit test?
Mark
>
> Thanks
>
> Dan
>
> Code below…
>
> Servlet
> ----------
>
> protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
> // 1. Start Async
> final AsyncContext asyncContext = req.startAsync();
> final ServletInputStream servletInputStream = asyncContext.getRequest().getInputStream();
>
> // 2. Add Read Listener to get user's input
> ReadListener listener = new ReadListener() {
>
> private StringBuilder sb = new StringBuilder(); // buffer user's request
>
> public void onDataAvailable() throws IOException {
> // 4. Read all the data that is available, may be called multiple times
> try {
> byte[] b = new byte[8192];
> int read = 0;
> do {
> read = servletInputStream.read(b);
> if (read == -1) {
> break;
> }
> sb.append(new String(b, 0, read));
> System.out.println("Buffer is now [" + sb.length() + "] characters");
> } while (servletInputStream.isReady());
> } catch (Exception ex) {
> ex.printStackTrace(System.err);
> asyncContext.complete();
> }
> }
>
> // 5. Called when all data has been read
> public void onAllDataRead() throws IOException {
> final ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream();
>
> final String output = sb.toString();
>
> // 6. Configure a write listener to echo the response
> WriteListener listener = new WriteListener() {
> public void onWritePossible() throws IOException {
> // 7. Write output
> if (outputStream.isReady()) {
> outputStream.print(output);
> }
>
> // 8. Call complete, to signal we are done
> asyncContext.complete();
> }
>
> public void onError(Throwable throwable) {
> throwable.printStackTrace(System.err);
> asyncContext.complete();
> }
> };
> outputStream.setWriteListener(listener);
> }
>
> public void onError(Throwable throwable) {
> throwable.printStackTrace(System.err);
> asyncContext.complete();
> }
> };
>
> // 3. Add listener, starts Non-blocking IO support
> servletInputStream.setReadListener(listener);
> }
>
> Client
> --------
>
> public static void main(String[] args) throws Exception {
> URL url = new URL("http://localhost:8080/tomcat-8-demos/non-blocking-io/EchoNbioServlet");
> HttpURLConnection connection =
> (HttpURLConnection) url.openConnection();
> connection.setDoOutput(true);
> connection.setReadTimeout(1000000);
> connection.connect();
> OutputStream os = null;
> try {
> os = connection.getOutputStream();
> // do nothing
> } finally {
> if (os != null) {
> try {
> os.close();
> } catch (IOException ioe) {
> // Ignore
> }
> }
> }
>
> int rc = connection.getResponseCode();
> InputStream is;
> if (rc < 400) {
> is = connection.getInputStream();
> } else {
> is = connection.getErrorStream();
> }
>
> StringBuilder sb = new StringBuilder();
> BufferedInputStream bis = null;
> try {
> bis = new BufferedInputStream(is);
> byte[] buf = new byte[2048];
> int rd = 0;
> while((rd = bis.read(buf)) > 0) {
> sb.append(new String(buf, "utf-8"));
> }
> } finally {
> if (bis != null) {
> try {
> bis.close();
> } catch (IOException e) {
> // Ignore
> }
> }
> }
> System.out.println("Resp: [" + sb.toString() + "]");
> }
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org