From 51b738053f00bc9239f5e34275a26405d0feac2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Tue, 2 Apr 2019 20:29:58 +0200 Subject: [PATCH] Make it work even if Tomcat-specific exceptions are not available When Tomcat is not available (for example, when using Jetty), the ClientAbortException is not available either, causing an error when starting the server. This commit fixes that, and instead catches that exception (or its Jetty equivalent) via reflection. --- .../player/controller/StreamController.java | 20 +++++++++++++++---- .../spring/LoggingExceptionResolver.java | 14 ++++++++++--- .../java/org/airsonic/player/util/Util.java | 12 +++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java b/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java index f804dd78..83f0e6c7 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java +++ b/airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java @@ -29,7 +29,6 @@ import org.airsonic.player.service.sonos.SonosHelper; import org.airsonic.player.util.HttpRange; import org.airsonic.player.util.StringUtil; import org.airsonic.player.util.Util; -import org.apache.catalina.connector.ClientAbortException; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -249,9 +248,22 @@ public class StreamController { } } } - } catch (ClientAbortException e) { - LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); - return; + } catch (IOException e) { + + // This happens often and outside of the control of the server, so + // we catch Tomcat/Jetty "connection aborted by client" exceptions + // and display a short error message. + boolean shouldCatch = false; + shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException"); + shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException"); + if (shouldCatch) { + LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); + return; + } + + // Rethrow the exception in all other cases + throw e; + } finally { if (status != null) { securityService.updateUserByteCounts(user, status.getBytesTransfered(), 0L, 0L); diff --git a/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java b/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java index 6ee5b55a..0dcb193d 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java +++ b/airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java @@ -18,11 +18,19 @@ public class LoggingExceptionResolver implements HandlerExceptionResolver, Order public ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object o, Exception e ) { - if (e instanceof org.apache.catalina.connector.ClientAbortException) { + // This happens often and outside of the control of the server, so + // we catch Tomcat/Jetty "connection aborted by client" exceptions + // and display a short error message. + boolean shouldCatch = false; + shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException"); + shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException"); + if (shouldCatch) { LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); - } else { - LOG.error("{}: An exception occurred while loading {}", request.getRemoteAddr(), Util.getURLForRequest(request), e); + return null; } + + // Display a full stack trace in all other cases + LOG.error("{}: An exception occurred while loading {}", request.getRemoteAddr(), Util.getURLForRequest(request), e); return null; } diff --git a/airsonic-main/src/main/java/org/airsonic/player/util/Util.java b/airsonic-main/src/main/java/org/airsonic/player/util/Util.java index b5c07da9..0a758f03 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/util/Util.java +++ b/airsonic-main/src/main/java/org/airsonic/player/util/Util.java @@ -132,4 +132,16 @@ public final class Util { if (queryString != null && queryString.length() > 0) url += "?" + queryString; return url; } + + /** + * Return true if the given object is an instance of the class name in argument. + * If the class doesn't exist, returns false. + */ + public static boolean isInstanceOfClassName(Object o, String className) { + try { + return Class.forName(className).isInstance(o); + } catch (ClassNotFoundException e) { + return false; + } + } }