Skip to content

Commit e206520

Browse files
committed
Improve docs on SseEmitter onComplete/onError
Issue: SPR-16548
1 parent b14301b commit e206520

File tree

3 files changed

+42
-13
lines changed

3 files changed

+42
-13
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitter.java

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,16 @@ public class ResponseBodyEmitter {
6666
@Nullable
6767
private final Long timeout;
6868

69-
private final Set<DataWithMediaType> earlySendAttempts = new LinkedHashSet<>(8);
70-
7169
@Nullable
7270
private Handler handler;
7371

72+
/** Store send data before handler is initialized */
73+
private final Set<DataWithMediaType> earlySendAttempts = new LinkedHashSet<>(8);
74+
75+
/** Store complete invocation before handler is initialized */
7476
private boolean complete;
7577

78+
/** Store completeWithError invocation before handler is initialized */
7679
@Nullable
7780
private Throwable failure;
7881

@@ -147,6 +150,11 @@ protected void extendResponse(ServerHttpResponse outputMessage) {
147150
* Write the given object to the response.
148151
* <p>If any exception occurs a dispatch is made back to the app server where
149152
* Spring MVC will pass the exception through its exception handling mechanism.
153+
* <p><strong>Note:</strong> if the send fails with an IOException, you do
154+
* not need to call {@link #completeWithError(Throwable)} in order to clean
155+
* up. Instead the Servlet container creates a notification that results in a
156+
* dispatch where Spring MVC invokes exception resolvers and completes
157+
* processing.
150158
* @param object the object to write
151159
* @throws IOException raised when an I/O error occurs
152160
* @throws java.lang.IllegalStateException wraps any other errors
@@ -156,9 +164,8 @@ public void send(Object object) throws IOException {
156164
}
157165

158166
/**
159-
* Write the given object to the response also using a MediaType hint.
160-
* <p>If any exception occurs a dispatch is made back to the app server where
161-
* Spring MVC will pass the exception through its exception handling mechanism.
167+
* Overloaded variant of {@link #send(Object)} that also accepts a MediaType
168+
* hint for how to serialize the given Object.
162169
* @param object the object to write
163170
* @param mediaType a MediaType hint for selecting an HttpMessageConverter
164171
* @throws IOException raised when an I/O error occurs
@@ -187,13 +194,12 @@ private void sendInternal(Object object, @Nullable MediaType mediaType) throws I
187194
}
188195

189196
/**
190-
* Complete request processing.
191-
* <p>A dispatch is made into the app server where Spring MVC completes
192-
* asynchronous request processing.
193-
* <p><strong>Note:</strong> you do not need to call this method after an
194-
* {@link IOException} from any of the {@code send} methods. The Servlet
195-
* container will generate an error notification that Spring MVC will process
196-
* and handle through the exception resolver mechanism and then complete.
197+
* Complete request processing by performing a dispatch into the servlet
198+
* container, where Spring MVC is invoked once more, and completes the
199+
* request processing lifecycle.
200+
* <p><strong>Note:</strong> this method should be called by the application
201+
* to complete request processing. It should not be used after container
202+
* related events such as an error while {@link #send(Object) sending}.
197203
*/
198204
public synchronized void complete() {
199205
this.complete = true;
@@ -205,7 +211,13 @@ public synchronized void complete() {
205211
/**
206212
* Complete request processing with an error.
207213
* <p>A dispatch is made into the app server where Spring MVC will pass the
208-
* exception through its exception handling mechanism.
214+
* exception through its exception handling mechanism. Note however that
215+
* at this stage of request processing, the response is committed and the
216+
* response status can no longer be changed.
217+
* <p><strong>Note:</strong> this method should be called by the application
218+
* to complete request processing with an error. It should not be used after
219+
* container related events such as an error while
220+
* {@link #send(Object) sending}.
209221
*/
210222
public synchronized void completeWithError(Throwable ex) {
211223
this.complete = true;

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/SseEmitter.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ protected void extendResponse(ServerHttpResponse outputMessage) {
8282
* SseEmitter emitter = new SseEmitter();
8383
* emitter.send(event().data(myObject));
8484
* </pre>
85+
*
86+
* <p>Please, see {@link ResponseBodyEmitter#send(Object) parent Javadoc}
87+
* for important notes on exception handling.
88+
*
8589
* @param object the object to write
8690
* @throws IOException raised when an I/O error occurs
8791
* @throws java.lang.IllegalStateException wraps any other errors
@@ -99,6 +103,10 @@ public void send(Object object) throws IOException {
99103
* SseEmitter emitter = new SseEmitter();
100104
* emitter.send(event().data(myObject, MediaType.APPLICATION_JSON));
101105
* </pre>
106+
*
107+
* <p>Please, see {@link ResponseBodyEmitter#send(Object) parent Javadoc}
108+
* for important notes on exception handling.
109+
*
102110
* @param object the object to write
103111
* @param mediaType a MediaType hint for selecting an HttpMessageConverter
104112
* @throws IOException raised when an I/O error occurs

src/docs/asciidoc/web/webmvc.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,6 +3304,13 @@ response. For example:
33043304
`ResponseBodyEmitter` can also be used as the body in a `ResponseEntity` allowing you to
33053305
customize the status and headers of the response.
33063306

3307+
When an `emitter` throws an `IOException` (e.g. if the remote client went away) applications
3308+
are not responsible for cleaning up the connection, and should not invoke `emitter.complete`
3309+
or `emitter.completeWithError`. Instead the servlet container automatically initiates an
3310+
`AsyncListener` error notification in which Spring MVC makes a `completeWithError` call,
3311+
which in turn performs one a final ASYNC dispatch to the application during which Spring MVC
3312+
invokes the configured exception resolvers and completes the request.
3313+
33073314

33083315
[[mvc-ann-async-sse]]
33093316
==== SSE
@@ -3339,6 +3346,8 @@ does not support Server-Sent Events. Consider using Spring's
33393346
<<web.adoc#websocket-fallback,SockJS fallback>> transports (including SSE) that target
33403347
a wide range of browsers.
33413348

3349+
Also see <<mvc-ann-async-objects,previous section>> for notes on exception handling.
3350+
33423351

33433352
[[mvc-ann-async-output-stream]]
33443353
==== Raw data

0 commit comments

Comments
 (0)