Skip to content

Commit 7d50e72

Browse files
committed
Handle NPE observed in unit tests and reported on users list.
NPE occurs after an IOException on a non-container thread during asynchronous processing.
1 parent f3dbb77 commit 7d50e72

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

java/org/apache/catalina/connector/CoyoteOutputStream.java

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ public void write(int i) throws IOException {
8181
try {
8282
ob.writeByte(i);
8383
} catch (IOException ioe) {
84-
ob.setErrorException(ioe);
85-
throw ioe;
84+
handleIOException(ioe);
8685
}
8786
if (nonBlocking) {
8887
checkRegisterForWrite();
@@ -102,8 +101,7 @@ public void write(byte[] b, int off, int len) throws IOException {
102101
try {
103102
ob.write(b, off, len);
104103
} catch (IOException ioe) {
105-
ob.setErrorException(ioe);
106-
throw ioe;
104+
handleIOException(ioe);
107105
}
108106
if (nonBlocking) {
109107
checkRegisterForWrite();
@@ -121,8 +119,7 @@ public void write(ByteBuffer from) throws IOException {
121119
try {
122120
ob.write(from);
123121
} catch (IOException ioe) {
124-
ob.setErrorException(ioe);
125-
throw ioe;
122+
handleIOException(ioe);
126123
}
127124
if (nonBlocking) {
128125
checkRegisterForWrite();
@@ -139,8 +136,7 @@ public void flush() throws IOException {
139136
try {
140137
ob.flush();
141138
} catch (IOException ioe) {
142-
ob.setErrorException(ioe);
143-
throw ioe;
139+
handleIOException(ioe);
144140
}
145141
if (nonBlocking) {
146142
checkRegisterForWrite();
@@ -179,8 +175,7 @@ public void close() throws IOException {
179175
try {
180176
ob.close();
181177
} catch (IOException ioe) {
182-
ob.setErrorException(ioe);
183-
throw ioe;
178+
handleIOException(ioe);
184179
}
185180
}
186181

@@ -197,5 +192,29 @@ public boolean isReady() {
197192
public void setWriteListener(WriteListener listener) {
198193
ob.setWriteListener(listener);
199194
}
195+
196+
197+
private void handleIOException(IOException ioe) throws IOException {
198+
try {
199+
ob.setErrorException(ioe);
200+
} catch (NullPointerException npe) {
201+
/*
202+
* Ignore.
203+
*
204+
* An IOException on a non-container thread during asynchronous Servlet processing will trigger a dispatch
205+
* to a container thread that will complete the asynchronous processing and recycle the request, response
206+
* and associated objects including the OutputBuffer. Depending on timing it is possible that the
207+
* OutputBuffer will have been cleared by the time the call above is made - resulting in an NPE.
208+
*
209+
* If the OutputBuffer is null then there is no need to call setErrorException(). Catching and ignoring the
210+
* NPE is (for now at least) a simpler solution than adding locking to OutputBuffer to ensure it is non-null
211+
* and remains non-null while setErrorException() is called.
212+
*
213+
* The longer term solution is likely a refactoring and clean-up of error handling for asynchronous requests
214+
* but that is potentially a significant piece of work.
215+
*/
216+
}
217+
throw ioe;
218+
}
200219
}
201220

webapps/docs/changelog.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@
199199
control over the handling of the path used to created a
200200
<code>RequestDispatcher</code>. (markt)
201201
</add>
202+
<fix>
203+
Handle a potential <code>NullPointerException</code> after an
204+
<code>IOException</code> occurs on a non-container thread during
205+
asynchronous processing. (markt)
206+
</fix>
202207
</changelog>
203208
</subsection>
204209
<subsection name="Coyote">

0 commit comments

Comments
 (0)