You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by lq...@apache.org on 2017/10/10 12:34:07 UTC

qpid-broker-j git commit: QPID-7955: [Java Broker] Workaround LOGBACK-1027: Stack overflow on recursive exceptions

Repository: qpid-broker-j
Updated Branches:
  refs/heads/master 28c3dc2bc -> 4889eac08


QPID-7955: [Java Broker] Workaround LOGBACK-1027: Stack overflow on recursive exceptions


Project: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/commit/4889eac0
Tree: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/tree/4889eac0
Diff: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/diff/4889eac0

Branch: refs/heads/master
Commit: 4889eac089acd05f6725cbeca23d30eff2f84d63
Parents: 28c3dc2
Author: Lorenz Quack <lq...@apache.org>
Authored: Tue Oct 10 13:25:09 2017 +0100
Committer: Lorenz Quack <lq...@apache.org>
Committed: Tue Oct 10 13:33:59 2017 +0100

----------------------------------------------------------------------
 .../Logback1027WorkaroundTurboFilter.java       | 117 +++++++++
 .../LogbackLoggingSystemLauncherListener.java   |  10 +-
 .../Logback1027WorkaroundTurboFilterTest.java   | 257 +++++++++++++++++++
 3 files changed, 382 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/4889eac0/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilter.java
----------------------------------------------------------------------
diff --git a/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilter.java b/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilter.java
new file mode 100644
index 0000000..9787fbb
--- /dev/null
+++ b/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilter.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.logging.logback;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Set;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.turbo.TurboFilter;
+import ch.qos.logback.core.spi.FilterReply;
+import org.slf4j.Marker;
+
+public class Logback1027WorkaroundTurboFilter extends TurboFilter
+{
+    @Override
+    public FilterReply decide(final Marker marker,
+                              final Logger logger,
+                              final Level level,
+                              final String format,
+                              final Object[] params,
+                              final Throwable t)
+    {
+
+        Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
+
+
+        if (t != null && hasRecursiveThrowableReference(t, seen))
+        {
+            final int locationAwareLoggerInteger = Level.toLocationAwareLoggerInteger(level);
+            logger.log(marker, logger.getName(), locationAwareLoggerInteger, format, params, new StringifiedException(t));
+            return FilterReply.DENY;
+        }
+
+        return FilterReply.NEUTRAL;
+    }
+
+    private boolean hasRecursiveThrowableReference(Throwable t, Set<Throwable> seen)
+    {
+        if (t == null)
+        {
+            return false;
+        }
+        if (!seen.add(t))
+        {
+            return true;
+        }
+        else
+        {
+            final Throwable[] allSuppressed = t.getSuppressed();
+            int allSuppressedLength = allSuppressed.length;
+            if (allSuppressedLength > 0)
+            {
+                Set<Throwable> seenCopy = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>(seen.size()));
+                seenCopy.addAll(seen);
+                for (int i = 0; i < allSuppressedLength; ++i)
+                {
+                    if (hasRecursiveThrowableReference(allSuppressed[i], seenCopy))
+                    {
+                        return true;
+                    }
+                }
+            }
+            if (hasRecursiveThrowableReference(t.getCause(), seen))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static class StringifiedException extends RuntimeException
+    {
+        public StringifiedException(final Throwable t)
+        {
+            super(stringifyStacktrace(t));
+        }
+
+        private static String stringifyStacktrace(final Throwable e)
+        {
+            try (StringWriter sw = new StringWriter();
+                 PrintWriter pw = new PrintWriter(sw))
+            {
+                e.printStackTrace(pw);
+                pw.println("End of stringified Stacktrace");
+                return sw.toString();
+            }
+            catch (IOException e1)
+            {
+                throw new RuntimeException("Unexpected exception stringifying stacktrace", e1);
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/4889eac0/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/LogbackLoggingSystemLauncherListener.java
----------------------------------------------------------------------
diff --git a/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/LogbackLoggingSystemLauncherListener.java b/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/LogbackLoggingSystemLauncherListener.java
index fc676ff..fbbf3e5 100644
--- a/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/LogbackLoggingSystemLauncherListener.java
+++ b/broker-plugins/logging-logback/src/main/java/org/apache/qpid/server/logging/logback/LogbackLoggingSystemLauncherListener.java
@@ -21,6 +21,7 @@
 package org.apache.qpid.server.logging.logback;
 
 import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,6 +32,7 @@ public class LogbackLoggingSystemLauncherListener implements SystemLauncherListe
 {
     private StartupAppender _startupAppender;
     private ch.qos.logback.classic.Logger _logger;
+    private Logback1027WorkaroundTurboFilter _logback1027WorkaroundTurboFilter;
 
     @Override
     public void beforeStartup()
@@ -42,11 +44,14 @@ public class LogbackLoggingSystemLauncherListener implements SystemLauncherListe
             _logger.setAdditive(true);
         }
 
+        final LoggerContext loggerContext = _logger.getLoggerContext();
+        _logback1027WorkaroundTurboFilter = new Logback1027WorkaroundTurboFilter();
+        loggerContext.addTurboFilter(_logback1027WorkaroundTurboFilter);
+
         _startupAppender = new StartupAppender();
-        _startupAppender.setContext(_logger.getLoggerContext());
+        _startupAppender.setContext(loggerContext);
         _startupAppender.start();
         _logger.addAppender(_startupAppender);
-
     }
 
     @Override
@@ -80,6 +85,7 @@ public class LogbackLoggingSystemLauncherListener implements SystemLauncherListe
     public void onContainerClose(final SystemConfig<?> systemConfig)
     {
         QpidLoggerTurboFilter.uninstallFromRootContext();
+        _logger.getLoggerContext().getTurboFilterList().remove(_logback1027WorkaroundTurboFilter);
     }
 
 

http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/4889eac0/broker-plugins/logging-logback/src/test/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilterTest.java
----------------------------------------------------------------------
diff --git a/broker-plugins/logging-logback/src/test/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilterTest.java b/broker-plugins/logging-logback/src/test/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilterTest.java
new file mode 100644
index 0000000..84ab2f3
--- /dev/null
+++ b/broker-plugins/logging-logback/src/test/java/org/apache/qpid/server/logging/logback/Logback1027WorkaroundTurboFilterTest.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.logging.logback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.LogbackException;
+import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.spi.FilterReply;
+import ch.qos.logback.core.status.Status;
+
+import org.apache.qpid.test.utils.QpidTestCase;
+
+public class Logback1027WorkaroundTurboFilterTest extends QpidTestCase
+{
+    private static final String TEST_LOG_MESSAGE = "hello";
+    private Logback1027WorkaroundTurboFilter _filter = new Logback1027WorkaroundTurboFilter();
+    private Logger _logger;
+    private SnoopingAppender _snoopingAppender;
+
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        LoggerContext context = new LoggerContext();
+        _logger = context.getLogger(Logback1027WorkaroundTurboFilterTest.class);
+        _snoopingAppender = new SnoopingAppender();
+        _logger.addAppender(_snoopingAppender);
+    }
+
+    public void testOneException()
+    {
+        Exception e = new Exception();
+        final FilterReply reply = doDecide(e);
+        assertEquals(FilterReply.NEUTRAL, reply);
+
+        assertEquals(0, _snoopingAppender.getEvents().size());
+    }
+
+    public void testSuppressedExceptionRecursion()
+    {
+        Exception e1 = new Exception();
+        Exception e2 = new Exception();
+        e2.addSuppressed(e1);
+        e1.addSuppressed(e2);
+
+        final FilterReply reply = doDecide(e1);
+        assertEquals(FilterReply.DENY, reply);
+
+        final List<ILoggingEvent> events = _snoopingAppender.getEvents();
+        assertEquals(1, events.size());
+
+        assertLoggingEvent(events.get(0));
+    }
+
+    private void assertLoggingEvent(final ILoggingEvent loggingEvent)
+    {
+        assertEquals(Level.INFO, loggingEvent.getLevel());
+        assertEquals(TEST_LOG_MESSAGE, loggingEvent.getMessage());
+        assertNull(loggingEvent.getArgumentArray());
+        IThrowableProxy thing = loggingEvent.getThrowableProxy();
+        assertEquals(Logback1027WorkaroundTurboFilter.StringifiedException.class.getName(), thing.getClassName());
+    }
+
+    public void testInitCauseRecursion() throws Exception
+    {
+        Exception e1 = new Exception();
+        Exception e2 = new Exception();
+        e2.initCause(e1);
+        e1.initCause(e2);
+
+        final FilterReply reply = doDecide(e1);
+        assertEquals(FilterReply.DENY, reply);
+        assertEquals(1, _snoopingAppender.getEvents().size());
+    }
+
+    public void testNoRecursion()
+    {
+        Exception e1 = new Exception();
+        Exception e2 = new Exception();
+        Exception e3 = new Exception();
+
+        e2.addSuppressed(e3);
+        e1.addSuppressed(e2);
+        e1.initCause(e3);
+
+        final FilterReply reply = doDecide(e1);
+        assertEquals(FilterReply.NEUTRAL, reply);
+        assertEquals(0, _snoopingAppender.getEvents().size());
+    }
+
+    public void testNoRecursion2()
+    {
+        Exception e1 = new Exception();
+        Exception e2 = new Exception();
+        Exception e3 = new Exception();
+
+        e2.initCause(e3);
+        e1.initCause(e2);
+        e1.addSuppressed(e3);
+
+        final FilterReply reply = doDecide(e1);
+        assertEquals(FilterReply.NEUTRAL, reply);
+        assertEquals(0, _snoopingAppender.getEvents().size());
+    }
+
+    private FilterReply doDecide(final Exception e1)
+    {
+        return _filter.decide(null, _logger, Level.INFO, "hello", null, e1);
+    }
+
+    private static class SnoopingAppender implements Appender<ILoggingEvent>
+    {
+
+        private List<ILoggingEvent> _events = new ArrayList<>();
+
+        List<ILoggingEvent> getEvents()
+        {
+            return _events;
+        }
+
+        @Override
+        public void doAppend(final ILoggingEvent event) throws LogbackException
+        {
+            _events.add(event);
+        }
+
+        @Override
+        public String getName()
+        {
+            return null;
+        }
+
+        @Override
+        public void setName(final String name)
+        {
+        }
+
+        @Override
+        public void setContext(final Context context)
+        {
+        }
+
+        @Override
+        public Context getContext()
+        {
+            return null;
+        }
+
+        @Override
+        public void addStatus(final Status status)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addInfo(final String msg)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addInfo(final String msg, final Throwable ex)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addWarn(final String msg)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addWarn(final String msg, final Throwable ex)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addError(final String msg)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addError(final String msg, final Throwable ex)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addFilter(final Filter<ILoggingEvent> newFilter)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clearAllFilters()
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public List<Filter<ILoggingEvent>> getCopyOfAttachedFiltersList()
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public FilterReply getFilterChainDecision(final ILoggingEvent event)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void start()
+        {
+        }
+
+        @Override
+        public void stop()
+        {
+        }
+
+        @Override
+        public boolean isStarted()
+        {
+            return true;
+        }
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org