Common root cause introspection algorithm in NestedExceptionUtils

Issue: SPR-15510
This commit is contained in:
Juergen Hoeller
2017-05-03 11:31:28 +02:00
parent 16901b1497
commit 9d8e9cf243
5 changed files with 97 additions and 70 deletions

View File

@@ -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);