Common root cause introspection algorithm in NestedExceptionUtils
Issue: SPR-15510
This commit is contained in:
@@ -19,7 +19,6 @@ package org.springframework.web.socket.sockjs.transport.session;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -31,7 +30,7 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.NestedCheckedException;
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
@@ -70,25 +69,25 @@ public abstract class AbstractSockJsSession implements SockJsSession {
|
||||
public static final String DISCONNECTED_CLIENT_LOG_CATEGORY =
|
||||
"org.springframework.web.socket.sockjs.DisconnectedClient";
|
||||
|
||||
/**
|
||||
* Tomcat: ClientAbortException or EOFException
|
||||
* Jetty: EofException
|
||||
* WildFly, GlassFish: java.io.IOException "Broken pipe" (already covered)
|
||||
* <p>TODO:
|
||||
* This definition is currently duplicated between HttpWebHandlerAdapter
|
||||
* and AbstractSockJsSession. It is a candidate for a common utility class.
|
||||
* @see #indicatesDisconnectedClient(Throwable)
|
||||
*/
|
||||
private static final Set<String> DISCONNECTED_CLIENT_EXCEPTIONS =
|
||||
new HashSet<>(Arrays.asList("ClientAbortException", "EOFException", "EofException"));
|
||||
|
||||
|
||||
/**
|
||||
* Separate logger to use on network IO failure after a client has gone away.
|
||||
* @see #DISCONNECTED_CLIENT_LOG_CATEGORY
|
||||
*/
|
||||
protected static final Log disconnectedClientLogger = LogFactory.getLog(DISCONNECTED_CLIENT_LOG_CATEGORY);
|
||||
|
||||
|
||||
private static final Set<String> disconnectedClientExceptions;
|
||||
|
||||
static {
|
||||
Set<String> set = new HashSet<String>(4);
|
||||
set.add("ClientAbortException"); // Tomcat
|
||||
set.add("EOFException"); // Tomcat
|
||||
set.add("EofException"); // Jetty
|
||||
// java.io.IOException "Broken pipe" on WildFly, Glassfish (already covered)
|
||||
disconnectedClientExceptions = Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected final Object responseLock = new Object();
|
||||
@@ -340,28 +339,28 @@ public abstract class AbstractSockJsSession implements SockJsSession {
|
||||
}
|
||||
}
|
||||
|
||||
private void logWriteFrameFailure(Throwable failure) {
|
||||
@SuppressWarnings("serial")
|
||||
NestedCheckedException nestedException = new NestedCheckedException("", failure) {};
|
||||
|
||||
if ("Broken pipe".equalsIgnoreCase(nestedException.getMostSpecificCause().getMessage()) ||
|
||||
disconnectedClientExceptions.contains(failure.getClass().getSimpleName())) {
|
||||
protected abstract void writeFrameInternal(SockJsFrame frame) throws IOException;
|
||||
|
||||
private void logWriteFrameFailure(Throwable ex) {
|
||||
if (indicatesDisconnectedClient(ex)) {
|
||||
if (disconnectedClientLogger.isTraceEnabled()) {
|
||||
disconnectedClientLogger.trace("Looks like the client has gone away", failure);
|
||||
disconnectedClientLogger.trace("Looks like the client has gone away", ex);
|
||||
}
|
||||
else if (disconnectedClientLogger.isDebugEnabled()) {
|
||||
disconnectedClientLogger.debug("Looks like the client has gone away: " +
|
||||
nestedException.getMessage() + " (For full stack trace, set the '" +
|
||||
DISCONNECTED_CLIENT_LOG_CATEGORY + "' log category to TRACE level)");
|
||||
disconnectedClientLogger.debug("Looks like the client has gone away: " + ex +
|
||||
" (For a full stack trace, set the log category '" + DISCONNECTED_CLIENT_LOG_CATEGORY +
|
||||
"' to TRACE level.)");
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.debug("Terminating connection after failure to send message to client", failure);
|
||||
logger.debug("Terminating connection after failure to send message to client", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void writeFrameInternal(SockJsFrame frame) throws IOException;
|
||||
private boolean indicatesDisconnectedClient(Throwable ex) {
|
||||
return ("Broken pipe".equalsIgnoreCase(NestedExceptionUtils.getMostSpecificCause(ex).getMessage()) ||
|
||||
DISCONNECTED_CLIENT_EXCEPTIONS.contains(ex.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
|
||||
// Delegation methods
|
||||
@@ -421,7 +420,8 @@ public abstract class AbstractSockJsSession implements SockJsSession {
|
||||
delegateError(error);
|
||||
}
|
||||
catch (Throwable delegateException) {
|
||||
// ignore
|
||||
// Ignore
|
||||
logger.debug("Exception from error handling delegate", delegateException);
|
||||
}
|
||||
try {
|
||||
close(closeStatus);
|
||||
|
||||
Reference in New Issue
Block a user