|
19 | 19 | import java.io.IOException;
|
20 | 20 | import java.util.ArrayList;
|
21 | 21 | import java.util.Arrays;
|
22 |
| -import java.util.Collections; |
23 | 22 | import java.util.Date;
|
24 | 23 | import java.util.HashSet;
|
25 | 24 | import java.util.List;
|
|
31 | 30 | import org.apache.commons.logging.Log;
|
32 | 31 | import org.apache.commons.logging.LogFactory;
|
33 | 32 |
|
34 |
| -import org.springframework.core.NestedCheckedException; |
| 33 | +import org.springframework.core.NestedExceptionUtils; |
35 | 34 | import org.springframework.util.Assert;
|
36 | 35 | import org.springframework.web.socket.CloseStatus;
|
37 | 36 | import org.springframework.web.socket.TextMessage;
|
@@ -70,25 +69,22 @@ private enum State {NEW, OPEN, CLOSED}
|
70 | 69 | public static final String DISCONNECTED_CLIENT_LOG_CATEGORY =
|
71 | 70 | "org.springframework.web.socket.sockjs.DisconnectedClient";
|
72 | 71 |
|
| 72 | + /** |
| 73 | + * Tomcat: ClientAbortException or EOFException |
| 74 | + * Jetty: EofException |
| 75 | + * WildFly, GlassFish: java.io.IOException "Broken pipe" (already covered) |
| 76 | + * @see #indicatesDisconnectedClient(Throwable) |
| 77 | + */ |
| 78 | + private static final Set<String> DISCONNECTED_CLIENT_EXCEPTIONS = |
| 79 | + new HashSet<String>(Arrays.asList("ClientAbortException", "EOFException", "EofException")); |
| 80 | + |
| 81 | + |
73 | 82 | /**
|
74 | 83 | * Separate logger to use on network IO failure after a client has gone away.
|
75 | 84 | * @see #DISCONNECTED_CLIENT_LOG_CATEGORY
|
76 | 85 | */
|
77 | 86 | protected static final Log disconnectedClientLogger = LogFactory.getLog(DISCONNECTED_CLIENT_LOG_CATEGORY);
|
78 | 87 |
|
79 |
| - |
80 |
| - private static final Set<String> disconnectedClientExceptions; |
81 |
| - |
82 |
| - static { |
83 |
| - Set<String> set = new HashSet<String>(4); |
84 |
| - set.add("ClientAbortException"); // Tomcat |
85 |
| - set.add("EOFException"); // Tomcat |
86 |
| - set.add("EofException"); // Jetty |
87 |
| - // java.io.IOException "Broken pipe" on WildFly, Glassfish (already covered) |
88 |
| - disconnectedClientExceptions = Collections.unmodifiableSet(set); |
89 |
| - } |
90 |
| - |
91 |
| - |
92 | 88 | protected final Log logger = LogFactory.getLog(getClass());
|
93 | 89 |
|
94 | 90 | protected final Object responseLock = new Object();
|
@@ -340,28 +336,28 @@ protected void writeFrame(SockJsFrame frame) throws SockJsTransportFailureExcept
|
340 | 336 | }
|
341 | 337 | }
|
342 | 338 |
|
343 |
| - private void logWriteFrameFailure(Throwable failure) { |
344 |
| - @SuppressWarnings("serial") |
345 |
| - NestedCheckedException nestedException = new NestedCheckedException("", failure) {}; |
346 |
| - |
347 |
| - if ("Broken pipe".equalsIgnoreCase(nestedException.getMostSpecificCause().getMessage()) || |
348 |
| - disconnectedClientExceptions.contains(failure.getClass().getSimpleName())) { |
| 339 | + protected abstract void writeFrameInternal(SockJsFrame frame) throws IOException; |
349 | 340 |
|
| 341 | + private void logWriteFrameFailure(Throwable ex) { |
| 342 | + if (indicatesDisconnectedClient(ex)) { |
350 | 343 | if (disconnectedClientLogger.isTraceEnabled()) {
|
351 |
| - disconnectedClientLogger.trace("Looks like the client has gone away", failure); |
| 344 | + disconnectedClientLogger.trace("Looks like the client has gone away", ex); |
352 | 345 | }
|
353 | 346 | else if (disconnectedClientLogger.isDebugEnabled()) {
|
354 |
| - disconnectedClientLogger.debug("Looks like the client has gone away: " + |
355 |
| - nestedException.getMessage() + " (For full stack trace, set the '" + |
356 |
| - DISCONNECTED_CLIENT_LOG_CATEGORY + "' log category to TRACE level)"); |
| 347 | + disconnectedClientLogger.debug("Looks like the client has gone away: " + ex + |
| 348 | + " (For a full stack trace, set the log category '" + DISCONNECTED_CLIENT_LOG_CATEGORY + |
| 349 | + "' to TRACE level.)"); |
357 | 350 | }
|
358 | 351 | }
|
359 | 352 | else {
|
360 |
| - logger.debug("Terminating connection after failure to send message to client", failure); |
| 353 | + logger.debug("Terminating connection after failure to send message to client", ex); |
361 | 354 | }
|
362 | 355 | }
|
363 | 356 |
|
364 |
| - protected abstract void writeFrameInternal(SockJsFrame frame) throws IOException; |
| 357 | + private boolean indicatesDisconnectedClient(Throwable ex) { |
| 358 | + return ("Broken pipe".equalsIgnoreCase(NestedExceptionUtils.getMostSpecificCause(ex).getMessage()) || |
| 359 | + DISCONNECTED_CLIENT_EXCEPTIONS.contains(ex.getClass().getSimpleName())); |
| 360 | + } |
365 | 361 |
|
366 | 362 |
|
367 | 363 | // Delegation methods
|
@@ -421,7 +417,8 @@ public void tryCloseWithSockJsTransportError(Throwable error, CloseStatus closeS
|
421 | 417 | delegateError(error);
|
422 | 418 | }
|
423 | 419 | catch (Throwable delegateException) {
|
424 |
| - // ignore |
| 420 | + // Ignore |
| 421 | + logger.debug("Exception from error handling delegate", delegateException); |
425 | 422 | }
|
426 | 423 | try {
|
427 | 424 | close(closeStatus);
|
|
0 commit comments