|
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