You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2015/06/29 12:55:25 UTC

svn commit: r1688178 - in /qpid/java/trunk: broker-core/src/main/java/org/apache/qpid/server/logging/ broker-core/src/main/java/org/apache/qpid/server/logging/logback/ broker-core/src/main/java/org/apache/qpid/server/model/ broker-core/src/main/java/or...

Author: orudyy
Date: Mon Jun 29 10:55:24 2015
New Revision: 1688178

URL: http://svn.apache.org/r1688178
Log:
QPID-6614: [Java Broker] Make BrokerFileLogger and VirtualHostFileLogger implementations expose content of the current file and the names/content of any previously rolled files

Added:
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/PathTypedContent.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RollingPolicyDecorator.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RolloverWatcher.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RollingPolicyDecoratorTest.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RolloverWatcherTest.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/util/LoggerTestHelper.java
Modified:
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLogger.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLoggerImpl.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/FileLoggerSettings.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/RollingFileAppenderFactory.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLogger.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLoggerImpl.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/TypedContent.java
    qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/BrokerLoggerTest.java
    qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/RollingFileAppenderFactoryTest.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLogger.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLogger.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLogger.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLogger.java Mon Jun 29 10:55:24 2015
@@ -20,9 +20,18 @@
  */
 package org.apache.qpid.server.logging;
 
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.Collection;
+
 import org.apache.qpid.server.model.BrokerLogger;
+import org.apache.qpid.server.model.DerivedAttribute;
 import org.apache.qpid.server.model.ManagedAttribute;
 import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.ManagedOperation;
+import org.apache.qpid.server.model.Param;
+import org.apache.qpid.server.model.TypedContent;
 
 @ManagedObject( category = false, type = BrokerFileLogger.TYPE)
 public interface BrokerFileLogger<X extends BrokerFileLogger<X>> extends BrokerLogger<X>
@@ -49,4 +58,10 @@ public interface BrokerFileLogger<X exte
 
     @ManagedAttribute(defaultValue = "%d %-5p [%t] \\(%c{2}\\) - %m%n")
     String getLayout();
+
+    @DerivedAttribute
+    Collection<String> getRolledFiles();
+
+    @ManagedOperation(nonModifying = true)
+    TypedContent getFile(@Param(name = "fileName") String fileName);
 }

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLoggerImpl.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLoggerImpl.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLoggerImpl.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/BrokerFileLoggerImpl.java Mon Jun 29 10:55:24 2015
@@ -20,18 +20,38 @@
  */
 package org.apache.qpid.server.logging;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import javax.activation.MimetypesFileTypeMap;
 
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.Context;
 
+import org.apache.qpid.server.logging.logback.RollingPolicyDecorator;
+import org.apache.qpid.server.logging.logback.RolloverWatcher;
 import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.DerivedAttribute;
 import org.apache.qpid.server.model.ManagedAttributeField;
 import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
+import org.apache.qpid.server.model.TypedContent;
+import org.apache.qpid.server.util.DaemonThreadFactory;
 
 public class BrokerFileLoggerImpl extends AbstractBrokerLogger<BrokerFileLoggerImpl> implements BrokerFileLogger<BrokerFileLoggerImpl>, FileLoggerSettings
 {
+    private final RolloverWatcher _rolloverWatcher;
+    private final ScheduledExecutorService _rolledPolicyExecutor;
+
     @ManagedAttributeField
     private String _layout;
     @ManagedAttributeField
@@ -46,11 +66,15 @@ public class BrokerFileLoggerImpl extend
     private int _maxHistory;
     @ManagedAttributeField
     private String _maxFileSize;
+    private Collection<String> _rolledFiles;
+    private Path _baseFolder;
 
     @ManagedObjectFactoryConstructor
     protected BrokerFileLoggerImpl(final Map<String, Object> attributes, Broker<?> broker)
     {
         super(attributes, broker);
+        _rolloverWatcher = new RolloverWatcher();
+        _rolledPolicyExecutor = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory("RolledFileScanner-" + getName()));
     }
 
     @Override
@@ -94,6 +118,36 @@ public class BrokerFileLoggerImpl extend
     {
         return _layout;
     }
+    @Override
+    public Collection<String> getRolledFiles()
+    {
+        return _rolloverWatcher.getRolledFiles();
+    }
+
+    @Override
+    public TypedContent getFile(final String fileName)
+    {
+        return _rolloverWatcher.getTypedContent(fileName, _fileName.equals(fileName));
+    }
+
+    @Override
+    public void stopLogging()
+    {
+        super.stopLogging();
+        _rolledPolicyExecutor.shutdown();
+    }
+
+    @Override
+    public RollingPolicyDecorator.RolloverListener getRolloverListener()
+    {
+        return _rolloverWatcher;
+    }
+
+    @Override
+    public ScheduledExecutorService getExecutorService()
+    {
+        return _rolledPolicyExecutor;
+    }
 
     @Override
     protected Appender<ILoggingEvent> createAppenderInstance(Context loggerContext)

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/FileLoggerSettings.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/FileLoggerSettings.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/FileLoggerSettings.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/FileLoggerSettings.java Mon Jun 29 10:55:24 2015
@@ -20,6 +20,10 @@
  */
 package org.apache.qpid.server.logging;
 
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.apache.qpid.server.logging.logback.RollingPolicyDecorator;
+
 public interface FileLoggerSettings
 {
     String getFileName();
@@ -35,4 +39,8 @@ public interface FileLoggerSettings
     String getMaxFileSize();
 
     String getLayout();
+
+    RollingPolicyDecorator.RolloverListener getRolloverListener();
+
+    ScheduledExecutorService getExecutorService();
 }

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/RollingFileAppenderFactory.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/RollingFileAppenderFactory.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/RollingFileAppenderFactory.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/RollingFileAppenderFactory.java Mon Jun 29 10:55:24 2015
@@ -27,9 +27,11 @@ import ch.qos.logback.classic.spi.ILoggi
 import ch.qos.logback.core.Context;
 import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
 import ch.qos.logback.core.rolling.RollingFileAppender;
+import ch.qos.logback.core.rolling.RollingPolicyBase;
 import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
 import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
 import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
+import org.apache.qpid.server.logging.logback.RollingPolicyDecorator;
 
 public class RollingFileAppenderFactory
 {
@@ -40,34 +42,35 @@ public class RollingFileAppenderFactory
         appender.setAppend(true);
         appender.setContext(loggerContext);
 
+        RollingPolicyBase policy;
         if(fileLoggerSettings.isRollDaily())
         {
             DailyTriggeringPolicy triggeringPolicy = new DailyTriggeringPolicy(fileLoggerSettings.isRollOnRestart(), fileLoggerSettings.getMaxFileSize());
             triggeringPolicy.setContext(loggerContext);
             TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
-            rollingPolicy.setContext(loggerContext);
             rollingPolicy.setMaxHistory(fileLoggerSettings.getMaxHistory());
             rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(triggeringPolicy);
             rollingPolicy.setFileNamePattern(fileLoggerSettings.getFileName() + ".%d{yyyy-MM-dd}.%i" + (fileLoggerSettings.isCompressOldFiles()
                     ? ".gz"
                     : ""));
-            appender.setRollingPolicy(rollingPolicy);
-            rollingPolicy.setParent(appender);
-            rollingPolicy.start();
+            policy = rollingPolicy;
         }
         else
         {
             SizeTriggeringPolicy sizeTriggeringPolicy = new SizeTriggeringPolicy(fileLoggerSettings.isRollOnRestart(), fileLoggerSettings.getMaxFileSize());
             sizeTriggeringPolicy.setContext(loggerContext);
             SimpleRollingPolicy rollingPolicy = new SimpleRollingPolicy(fileLoggerSettings.getMaxHistory());
-            rollingPolicy.setContext(loggerContext);
             rollingPolicy.setFileNamePattern(fileLoggerSettings.getFileName() + ".%i" + (fileLoggerSettings.isCompressOldFiles() ? ".gz" : ""));
-            appender.setRollingPolicy(rollingPolicy);
             appender.setTriggeringPolicy(sizeTriggeringPolicy);
-            rollingPolicy.setParent(appender);
-            rollingPolicy.start();
             sizeTriggeringPolicy.start();
+            policy = rollingPolicy;
         }
+        policy.setContext(loggerContext);
+        RollingPolicyDecorator decorator = new RollingPolicyDecorator(policy, fileLoggerSettings.getRolloverListener(), fileLoggerSettings.getExecutorService());
+        decorator.setParent(appender);
+        appender.setRollingPolicy(decorator);
+        decorator.start();
+
         final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
         encoder.setPattern(fileLoggerSettings.getLayout());
         encoder.setContext(loggerContext);

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLogger.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLogger.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLogger.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLogger.java Mon Jun 29 10:55:24 2015
@@ -21,8 +21,15 @@
 package org.apache.qpid.server.logging;
 
 
+import java.nio.file.Path;
+import java.util.Collection;
+
+import org.apache.qpid.server.model.DerivedAttribute;
 import org.apache.qpid.server.model.ManagedAttribute;
 import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.ManagedOperation;
+import org.apache.qpid.server.model.Param;
+import org.apache.qpid.server.model.TypedContent;
 import org.apache.qpid.server.model.VirtualHostLogger;
 
 @ManagedObject( category = false, type = VirtualHostFileLogger.TYPE)
@@ -51,4 +58,10 @@ public interface VirtualHostFileLogger<X
 
     @ManagedAttribute(defaultValue = "%d %-5p [%t] \\(%c{2}\\) - %m%n")
     String getLayout();
+
+    @DerivedAttribute
+    Collection<String> getRolledFiles();
+
+    @ManagedOperation(nonModifying = true)
+    TypedContent getFile(@Param(name = "fileName") String fileName);
 }

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLoggerImpl.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLoggerImpl.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLoggerImpl.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/VirtualHostFileLoggerImpl.java Mon Jun 29 10:55:24 2015
@@ -21,19 +21,28 @@
 package org.apache.qpid.server.logging;
 
 import java.security.Principal;
+import java.util.Collection;
 import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.Context;
 
+import org.apache.qpid.server.logging.logback.RollingPolicyDecorator;
+import org.apache.qpid.server.logging.logback.RolloverWatcher;
 import org.apache.qpid.server.model.ManagedAttributeField;
 import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
+import org.apache.qpid.server.model.TypedContent;
 import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.util.DaemonThreadFactory;
 
 public class VirtualHostFileLoggerImpl extends AbstractVirtualHostLogger<VirtualHostFileLoggerImpl> implements VirtualHostFileLogger<VirtualHostFileLoggerImpl>, FileLoggerSettings
 {
     private final Principal _principal;
+    private final RolloverWatcher _rolloverWatcher;
+    private final ScheduledExecutorService _rolledPolicyExecutor;
 
     @ManagedAttributeField
     private String _layout;
@@ -57,6 +66,8 @@ public class VirtualHostFileLoggerImpl e
     {
         super(attributes, virtualHost);
         _principal = virtualHost.getPrincipal();
+        _rolloverWatcher = new RolloverWatcher();
+        _rolledPolicyExecutor = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory("RolledFileScanner-" + getName()));
     }
 
     @Override
@@ -102,6 +113,37 @@ public class VirtualHostFileLoggerImpl e
     }
 
     @Override
+    public Collection<String> getRolledFiles()
+    {
+        return _rolloverWatcher.getRolledFiles();
+    }
+
+    @Override
+    public TypedContent getFile(final String fileName)
+    {
+        return _rolloverWatcher.getTypedContent(fileName, _fileName.equals(fileName));
+    }
+
+    @Override
+    public void stopLogging()
+    {
+        super.stopLogging();
+        _rolledPolicyExecutor.shutdown();
+    }
+
+    @Override
+    public RollingPolicyDecorator.RolloverListener getRolloverListener()
+    {
+        return _rolloverWatcher;
+    }
+
+    @Override
+    public ScheduledExecutorService getExecutorService()
+    {
+        return _rolledPolicyExecutor;
+    }
+
+    @Override
     protected Appender<ILoggingEvent> createAppenderInstance(Context loggerContext)
     {
         return new RollingFileAppenderFactory().createRollingFileAppender(this, loggerContext);

Added: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/PathTypedContent.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/PathTypedContent.java?rev=1688178&view=auto
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/PathTypedContent.java (added)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/PathTypedContent.java Mon Jun 29 10:55:24 2015
@@ -0,0 +1,64 @@
+/*
+ *
+ * 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.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+
+import org.apache.qpid.server.model.TypedContent;
+
+public class PathTypedContent implements TypedContent
+{
+    private final Path _path;
+    private final String _contentType;
+
+    public PathTypedContent(Path path,  String contentType)
+    {
+        _path = path;
+        _contentType = contentType;
+    }
+
+    @Override
+    public String getContentType()
+    {
+        return _contentType;
+    }
+
+    @Override
+    public InputStream openInputStream() throws IOException
+    {
+        return _path == null ? null : new FileInputStream(_path.toFile());
+    }
+
+    @Override
+    public long getSize()
+    {
+        return _path == null ? 0 : _path.toFile().length();
+    }
+
+    @Override
+    public String getFileName()
+    {
+        return _path == null ? null : _path.getFileName().toString();
+    }
+}

Added: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RollingPolicyDecorator.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RollingPolicyDecorator.java?rev=1688178&view=auto
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RollingPolicyDecorator.java (added)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RollingPolicyDecorator.java Mon Jun 29 10:55:24 2015
@@ -0,0 +1,312 @@
+/*
+ *
+ * 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.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Pattern;
+
+import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.rolling.RollingPolicy;
+import ch.qos.logback.core.rolling.RollingPolicyBase;
+import ch.qos.logback.core.rolling.RolloverFailure;
+import ch.qos.logback.core.rolling.helper.CompressionMode;
+import ch.qos.logback.core.rolling.helper.FileNamePattern;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RollingPolicyDecorator implements RollingPolicy
+{
+    public static final int DEFAULT_RESCAN_DELAY = 5000;
+    public static final String ROLLOVER_RESCAN_DELAY_MS_PROPERTY_NAME = "qpid.logger_rollover_rescan_delay_ms";
+    public static final int DEFAULT_RESCAN_LIMIT = 60;
+    public static final String ROLLOVER_RESCAN_LIMIT_PROPERTY_NAME = "qpid.logger_rollover_rescan_limit";
+    private static final Logger LOGGER = LoggerFactory.getLogger(RollingPolicyDecorator.class);
+    public static final String WARNING_MESSAGE = "Exceeded maximum number of rescans without detecting rolled over log file.";
+
+    private final RollingPolicyBase _decorated;
+    private final RolloverListener _listener;
+    private final Path _rolledFilesBaseFolder;
+    private final Pattern _rolledFileRegExp;
+    private final ScheduledExecutorService _executorService;
+    private final long _rescanDelayMillis = Long.getLong(ROLLOVER_RESCAN_DELAY_MS_PROPERTY_NAME, DEFAULT_RESCAN_DELAY);
+    private final long _rescanLimit = Long.getLong(ROLLOVER_RESCAN_LIMIT_PROPERTY_NAME, DEFAULT_RESCAN_LIMIT);
+    private final Lock _publishResultsLock = new ReentrantLock();
+    private ScanTask _currentScanTask;
+    private String[] _previousScanResults;
+
+    public RollingPolicyDecorator(RollingPolicyBase decorated, RolloverListener listener, ScheduledExecutorService executorService)
+    {
+        _decorated = decorated;
+        _listener = listener;
+        _executorService = executorService;
+
+        String filePathPattern = _decorated.getFileNamePattern();
+        String filePathRegExp = new FileNamePattern(filePathPattern, _decorated.getContext()).toRegex();
+        _rolledFilesBaseFolder = getRolledFilesBaseFolderFromRegExp(filePathRegExp);
+        _rolledFileRegExp = Pattern.compile(filePathRegExp);
+        _currentScanTask = null;
+    }
+
+    @Override
+    public void rollover() throws RolloverFailure
+    {
+        ScanTask task = createScanTaskAndCancelInProgress();
+        _decorated.rollover();
+        _executorService.execute(task);
+    }
+
+    @Override
+    public String getActiveFileName()
+    {
+        return _decorated.getActiveFileName();
+    }
+
+    @Override
+    public CompressionMode getCompressionMode()
+    {
+        return _decorated.getCompressionMode();
+    }
+
+    @Override
+    public void setParent(FileAppender appender)
+    {
+        _decorated.setParent(appender);
+    }
+
+    @Override
+    public void start()
+    {
+        ScanTask task = createScanTaskAndCancelInProgress();
+        _decorated.start();
+        _executorService.execute(task);
+    }
+
+    @Override
+    public void stop()
+    {
+        _decorated.stop();
+        synchronized (_publishResultsLock)
+        {
+            if (_currentScanTask != null)
+            {
+                _currentScanTask.cancel();
+            }
+            _previousScanResults = null;
+        }
+    }
+
+    @Override
+    public boolean isStarted()
+    {
+        return _decorated.isStarted();
+    }
+
+
+    public interface RolloverListener
+    {
+        void onRollover(Path baseFolder, String[] relativeFileNames);
+    }
+
+    public RollingPolicyBase getDecorated()
+    {
+        return _decorated;
+    }
+
+    private ScanTask createScanTaskAndCancelInProgress()
+    {
+        ScanTask task = new ScanTask();
+        synchronized (_publishResultsLock)
+        {
+            if (_currentScanTask != null)
+            {
+                _currentScanTask.cancel();
+            }
+            _currentScanTask = task;
+        }
+        return task;
+    }
+
+    private Path getRolledFilesBaseFolderFromRegExp(String fileNamePattern)
+    {
+        int firstDigitPatternPosition= fileNamePattern.indexOf("\\d");
+        if (firstDigitPatternPosition == -1)
+        {
+            throw new RuntimeException("Rolling policy file pattern does not seem to contain date or integer token");
+        }
+        int slashBeforeDigitPatternPosition = fileNamePattern.lastIndexOf("/", firstDigitPatternPosition);
+        if (slashBeforeDigitPatternPosition != -1)
+        {
+            return new File(fileNamePattern.substring(0, slashBeforeDigitPatternPosition)).toPath().toAbsolutePath();
+        }
+        else
+        {
+            return new File(System.getProperty("user.dir")).toPath().toAbsolutePath();
+        }
+    }
+
+    private class ScanTask implements Runnable
+    {
+        private int _rescanCounter;
+        private volatile boolean _canceled;
+
+        @Override
+        public void run()
+        {
+            if (!isCanceled() )
+            {
+                String[] rolloverFiles = scan();
+
+                if (!publishScanResults(rolloverFiles) && !isCanceled())
+                {
+                    if (_rescanCounter < _rescanLimit)
+                    {
+                        ++_rescanCounter;
+                        _executorService.schedule(this, _rescanDelayMillis, TimeUnit.MILLISECONDS);
+                    }
+                    else
+                    {
+                        LOGGER.warn(WARNING_MESSAGE);
+                    }
+                }
+            }
+        }
+
+        private boolean publishScanResults(String[] rolloverFiles)
+        {
+            boolean published = false;
+
+            if (rolloverFiles != null && !isCanceled() )
+            {
+                synchronized (_publishResultsLock)
+                {
+                    if (!isCanceled() && (_previousScanResults == null || !Arrays.equals(rolloverFiles, _previousScanResults)))
+                    {
+                        _previousScanResults = rolloverFiles;
+                        published = true;
+                    }
+                }
+            }
+
+            if (published)
+            {
+                _listener.onRollover(_rolledFilesBaseFolder, rolloverFiles);
+            }
+
+            return published;
+        }
+
+        public String[] scan()
+        {
+            final List<Path> rolledFiles = new ArrayList<>();
+            try
+            {
+                Files.walkFileTree(_rolledFilesBaseFolder, new FileVisitor<Path>()
+                {
+                    @Override
+                    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+                    {
+                        return (isCanceled() ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE);
+                    }
+
+                    @Override
+                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                    {
+                        String absolutePath = file.toAbsolutePath().toString();
+                        if (File.separatorChar == '\\')
+                        {
+                            absolutePath = absolutePath.replace('\\', '/');
+                        }
+
+                        if (_rolledFileRegExp.matcher(absolutePath).matches())
+                        {
+                            rolledFiles.add(file);
+                        }
+                        return (isCanceled() ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE);
+                    }
+
+                    @Override
+                    public FileVisitResult visitFileFailed(Path file, IOException exc)
+                    {
+                        return (isCanceled() ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE);
+                    }
+
+                    @Override
+                    public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+                    {
+                        return (isCanceled() ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE);
+                    }
+                });
+            }
+            catch(IOException e)
+            {
+                LOGGER.warn("Unexpected IOException while scanning for rollover files.", e);
+            }
+            return (isCanceled() ? null : relativizeAndSort(_rolledFilesBaseFolder, rolledFiles));
+        }
+
+        public void cancel()
+        {
+            _canceled = true;
+        }
+
+
+        private String[] relativizeAndSort(Path parent, List<Path> rolledFiles)
+        {
+            String[] results = new String[rolledFiles.size()];
+            int i = 0;
+            for (Path f : rolledFiles)
+            {
+                results[i++] = parent.relativize(f).toString();
+            }
+
+            Arrays.sort(results, new Comparator<String>()
+            {
+                @Override
+                public int compare(String o1, String o2)
+                {
+                    return o1.compareTo(o2);
+                }
+            });
+            return results;
+        }
+
+        public boolean isCanceled()
+        {
+            return _canceled || Thread.currentThread().isInterrupted();
+        }
+
+    }
+}

Added: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RolloverWatcher.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RolloverWatcher.java?rev=1688178&view=auto
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RolloverWatcher.java (added)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/logging/logback/RolloverWatcher.java Mon Jun 29 10:55:24 2015
@@ -0,0 +1,86 @@
+/*
+ *
+ * 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.File;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+public class RolloverWatcher implements RollingPolicyDecorator.RolloverListener
+{
+    private volatile Collection<String> _rolledFiles;
+    private volatile Path _baseFolder;
+
+    public RolloverWatcher()
+    {
+        _rolledFiles = Collections.emptyList();
+    }
+
+    @Override
+    public void onRollover(Path baseFolder, String[] relativeFileNames)
+    {
+        _rolledFiles = Collections.unmodifiableCollection(Arrays.asList(relativeFileNames));
+        _baseFolder = baseFolder;
+    }
+
+    public PathTypedContent getTypedContent(String fileName, boolean activeFile)
+    {
+        if (fileName == null)
+        {
+            throw new IllegalArgumentException("File name cannot be null");
+        }
+
+        Path path = null;
+        if (activeFile)
+        {
+            path =  new File(fileName).toPath();
+        }
+        else if (_rolledFiles.contains(fileName))
+        {
+            path = _baseFolder.resolve(fileName);
+        }
+        return new PathTypedContent(path, getContentType(fileName));
+    }
+
+    public Collection<String> getRolledFiles()
+    {
+        return _rolledFiles;
+    }
+
+    public String getContentType(String fileName)
+    {
+        String fileNameLower = fileName.toLowerCase();
+        if (fileNameLower.endsWith(".gz"))
+        {
+            return "application/x-gzip";
+        }
+        else if (fileNameLower.endsWith(".zip"))
+        {
+            return "application/x-zip";
+        }
+        else
+        {
+            return "text/plain";
+        }
+    }
+}

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/TypedContent.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/TypedContent.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/TypedContent.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/model/TypedContent.java Mon Jun 29 10:55:24 2015
@@ -20,9 +20,17 @@
  */
 package org.apache.qpid.server.model;
 
+import java.io.IOException;
+import java.io.InputStream;
+
 @ManagedAttributeValueType
 public interface TypedContent
 {
     String getContentType();
-    byte[] getData();
+
+    InputStream openInputStream() throws IOException;
+
+    long getSize();
+
+    String getFileName();
 }

Modified: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java (original)
+++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/queue/AbstractQueue.java Mon Jun 29 10:55:24 2015
@@ -18,6 +18,8 @@
  */
 package org.apache.qpid.server.queue;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.security.AccessControlException;
 import java.security.AccessController;
@@ -3267,11 +3269,23 @@ public abstract class AbstractQueue<X ex
                         }
 
                         @Override
-                        public byte[] getData()
+                        public InputStream openInputStream()
                         {
-                            return messageFinder.getContent();
+                            return new ByteArrayInputStream(messageFinder.getContent());
                         }
-                    };
+
+                        @Override
+                        public long getSize()
+                        {
+                            return messageFinder.getContent().length;
+                        }
+
+                        @Override
+                        public String getFileName()
+                        {
+                            return null;
+                        }
+            };
         }
         else
         {

Modified: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/BrokerLoggerTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/BrokerLoggerTest.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/BrokerLoggerTest.java (original)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/BrokerLoggerTest.java Mon Jun 29 10:55:24 2015
@@ -20,6 +20,7 @@
  */
 package org.apache.qpid.server.logging;
 
+import static org.apache.qpid.server.util.LoggerTestHelper.assertLoggedEvent;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -125,8 +126,8 @@ public class BrokerLoggerTest extends Qp
 
         logger.debug("Test2");
         logger.info("Test3");
-        assertLoggedEvent(false, "Test2", logger.getName(), Level.DEBUG);
-        assertLoggedEvent(true, "Test3", logger.getName(), Level.INFO);
+        assertLoggedEvent(_loggerAppender, false, "Test2", logger.getName(), Level.DEBUG);
+        assertLoggedEvent(_loggerAppender, true, "Test3", logger.getName(), Level.INFO);
     }
 
     public void testRemoveExistingFilter()
@@ -140,12 +141,12 @@ public class BrokerLoggerTest extends Qp
         Logger logger = LoggerFactory.getLogger("org.apache.qpid");
 
         logger.info("Test1");
-        assertLoggedEvent(true, "Test1", logger.getName(), Level.INFO);
+        assertLoggedEvent(_loggerAppender, true, "Test1", logger.getName(), Level.INFO);
 
         createdFilter.delete();
 
         logger.info("Test2");
-        assertLoggedEvent(false, "Test2", logger.getName(), Level.INFO);
+        assertLoggedEvent(_loggerAppender, false, "Test2", logger.getName(), Level.INFO);
     }
 
     public void testDeleteLogger()
@@ -158,24 +159,4 @@ public class BrokerLoggerTest extends Qp
         assertNull("Appender found when it should have been deleted", rootLogger.getAppender(_brokerLogger.getName()));
     }
 
-    private void assertLoggedEvent(boolean exists, String message, String loggerName, Level level)
-    {
-        List<ILoggingEvent> events;
-        synchronized(_loggerAppender)
-        {
-            events = new ArrayList<>(_loggerAppender.list);
-        }
-
-        boolean logged = false;
-        for (ILoggingEvent event: events)
-        {
-            if (event.getFormattedMessage().equals(message) && event.getLoggerName().equals(loggerName) && event.getLevel() == level)
-            {
-                logged = true;
-                break;
-            }
-        }
-        assertEquals("Event " + message + " from logger " + loggerName + " of log level " + level
-                + " is " + (exists ? "not" : "") + " found", exists, logged);
-    }
 }

Modified: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/RollingFileAppenderFactoryTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/RollingFileAppenderFactoryTest.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/RollingFileAppenderFactoryTest.java (original)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/RollingFileAppenderFactoryTest.java Mon Jun 29 10:55:24 2015
@@ -23,6 +23,8 @@ package org.apache.qpid.server.logging;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import java.util.concurrent.ScheduledExecutorService;
+
 import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Context;
@@ -32,6 +34,7 @@ import ch.qos.logback.core.rolling.Rolli
 import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
 import ch.qos.logback.core.rolling.TriggeringPolicy;
 import ch.qos.logback.core.rolling.helper.CompressionMode;
+import org.apache.qpid.server.logging.logback.RollingPolicyDecorator;
 import org.apache.qpid.test.utils.QpidTestCase;
 
 public class RollingFileAppenderFactoryTest extends QpidTestCase
@@ -57,6 +60,7 @@ public class RollingFileAppenderFactoryT
         when(_settings.isRollDaily()).thenReturn(Boolean.TRUE);
         when(_settings.isRollOnRestart()).thenReturn(Boolean.TRUE);
         when(_settings.getMaxHistory()).thenReturn(MAX_HISTORY);
+        when(_settings.getExecutorService()).thenReturn(mock(ScheduledExecutorService.class));
     }
 
     public void testCreateRollingFileAppenderDailyRolling()
@@ -66,7 +70,9 @@ public class RollingFileAppenderFactoryT
         assertEquals("Unexpected appender file name", FILE_NAME, appender.getFile());
 
         RollingPolicy rollingPolicy = appender.getRollingPolicy();
-        assertTrue("Unexpected rolling policy", rollingPolicy instanceof TimeBasedRollingPolicy);
+        assertTrue("Unexpected rolling policy", rollingPolicy instanceof RollingPolicyDecorator);
+        rollingPolicy = ((RollingPolicyDecorator)rollingPolicy).getDecorated();
+        assertTrue("Unexpected decorated rolling policy", rollingPolicy instanceof TimeBasedRollingPolicy);
         assertEquals("Unexpected max history", MAX_HISTORY, ((TimeBasedRollingPolicy) rollingPolicy).getMaxHistory());
         assertEquals("Unexpected file name pattern", FILE_NAME + ".%d{yyyy-MM-dd}.%i.gz", ((TimeBasedRollingPolicy) rollingPolicy).getFileNamePattern());
         assertEquals("Unexpected compression mode", CompressionMode.GZ, rollingPolicy.getCompressionMode());
@@ -87,7 +93,9 @@ public class RollingFileAppenderFactoryT
         assertEquals("Unexpected appender file name", FILE_NAME, appender.getFile());
 
         RollingPolicy rollingPolicy = appender.getRollingPolicy();
-        assertTrue("Unexpected rolling policy", rollingPolicy instanceof RollingFileAppenderFactory.SimpleRollingPolicy);
+        assertTrue("Unexpected rolling policy", rollingPolicy instanceof RollingPolicyDecorator);
+        rollingPolicy = ((RollingPolicyDecorator)rollingPolicy).getDecorated();
+        assertTrue("Unexpected decorated rolling policy", rollingPolicy instanceof RollingFileAppenderFactory.SimpleRollingPolicy);
         assertEquals("Unexpected max history", MAX_HISTORY, ((RollingFileAppenderFactory.SimpleRollingPolicy) rollingPolicy).getMaxIndex());
         assertEquals("Unexpected file name pattern", FILE_NAME + ".%i", ((RollingFileAppenderFactory.SimpleRollingPolicy) rollingPolicy).getFileNamePattern());
         assertEquals("Unexpected compression mode", CompressionMode.NONE, rollingPolicy.getCompressionMode());

Added: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RollingPolicyDecoratorTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RollingPolicyDecoratorTest.java?rev=1688178&view=auto
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RollingPolicyDecoratorTest.java (added)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RollingPolicyDecoratorTest.java Mon Jun 29 10:55:24 2015
@@ -0,0 +1,216 @@
+/*
+ *
+ * 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 static org.apache.qpid.server.util.LoggerTestHelper.assertLoggedEvent;
+import static org.apache.qpid.server.util.LoggerTestHelper.createAndRegisterAppender;
+import static org.apache.qpid.server.util.LoggerTestHelper.deleteAndUnregisterAppender;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.concurrent.*;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.read.ListAppender;
+import ch.qos.logback.core.rolling.RollingPolicyBase;
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.test.utils.TestFileUtils;
+import org.apache.qpid.util.FileUtils;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class RollingPolicyDecoratorTest extends QpidTestCase
+{
+    private RollingPolicyBase _delegate;
+    private RollingPolicyDecorator _policy;
+    private RollingPolicyDecorator.RolloverListener _listener;
+    private File _baseFolder;
+    private File _testFile;
+
+    @Override
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        _baseFolder = TestFileUtils.createTestDirectory("rollover", true);
+        _testFile = createTestFile("test.2015-06-25.0.gz");
+        Context mockContext = mock(Context.class);
+        _delegate = mock(RollingPolicyBase.class);
+        when(_delegate.getFileNamePattern()).thenReturn( _baseFolder + File.separator + "test.%d{yyyy-MM-dd}.%i.gz");
+        when(_delegate.getContext()).thenReturn(mockContext);
+        _listener = mock(RollingPolicyDecorator.RolloverListener.class);
+
+        _policy = new RollingPolicyDecorator(_delegate, _listener, createMockExecutorService());
+    }
+
+    @Override
+    public void tearDown() throws Exception
+    {
+        super.tearDown();
+        if (_baseFolder.exists())
+        {
+            FileUtils.delete(_baseFolder, true);
+        }
+    }
+
+    public File createTestFile(String fileName) throws IOException
+    {
+        File testFile = new File(_baseFolder, fileName);
+        testFile.createNewFile();
+        return testFile;
+    }
+
+    private ScheduledExecutorService createMockExecutorService()
+    {
+        ScheduledExecutorService executorService = mock(ScheduledExecutorService.class);
+        doAnswer(new Answer() {
+            public Object answer(InvocationOnMock invocation) {
+                Object[] args = invocation.getArguments();
+                ((Runnable)args[0]).run();
+                return null;
+            }}).when(executorService).schedule(any(Runnable.class), any(long.class), any(TimeUnit.class));
+        doAnswer(new Answer() {
+            public Object answer(InvocationOnMock invocation) {
+                Object[] args = invocation.getArguments();
+                ((Runnable)args[0]).run();
+                return null;
+            }}).when(executorService).execute(any(Runnable.class));
+        return executorService;
+    }
+
+    public void testRollover()
+    {
+        _policy.rollover();
+        verify(_delegate).rollover();
+    }
+
+    public void testRolloverListener() throws InterruptedException
+    {
+        _policy.rollover();
+        verify(_listener).onRollover(any(Path.class), any(String[].class));
+    }
+
+    public void testRolloverWithFile() throws IOException
+    {
+        _policy.rollover();
+        verify(_delegate).rollover();
+
+        Matcher<String[]> matcher = getMatcher(new String[]{_testFile.getName()});
+        verify(_listener).onRollover(eq(_baseFolder.toPath()), argThat(matcher));
+    }
+
+    public void testRolloverRescanLimit() throws IOException
+    {
+        ListAppender loggerAppender = createAndRegisterAppender("testAppender");
+        try
+        {
+            _policy.rollover();
+            verify(_delegate).rollover();
+            _policy.rollover();
+            assertLoggedEvent(loggerAppender, true, RollingPolicyDecorator.WARNING_MESSAGE, RollingPolicyDecorator.class.getName(), Level.WARN);
+        }
+        finally
+        {
+            deleteAndUnregisterAppender(loggerAppender);
+        }
+    }
+
+    public void testSequentialRollover() throws IOException
+    {
+        _policy.rollover();
+        verify(_delegate).rollover();
+
+        Matcher<String[]> matcher = getMatcher(new String[]{ _testFile.getName() });
+        verify(_listener).onRollover(eq(_baseFolder.toPath()), argThat(matcher));
+
+        File secondFile = createTestFile("test.2015-06-25.1.gz");
+        _policy.rollover();
+        verify(_delegate, times(2)).rollover();
+        Matcher<String[]> matcher2 = getMatcher(new String[]{_testFile.getName(), secondFile.getName()});
+        verify(_listener).onRollover(eq(_baseFolder.toPath()), argThat(matcher2));
+    }
+
+    private Matcher<String[]> getMatcher(final String[] expected)
+    {
+        return new BaseMatcher<String[]>()
+        {
+            @Override
+            public boolean matches(Object item)
+            {
+                return Arrays.equals(expected, (String[]) item);
+            }
+            @Override
+            public void describeTo(Description description)
+            {
+                description.appendValueList("[", ",", "]", expected);
+            }
+        };
+    }
+
+    public void testGetActiveFileName()
+    {
+        _policy.getActiveFileName();
+        verify(_delegate).getActiveFileName();
+    }
+
+    public void testGetCompressionMode()
+    {
+        _policy.getCompressionMode();
+        verify(_delegate).getCompressionMode();
+    }
+
+    public void testSetParent()
+    {
+        FileAppender appender = mock(FileAppender.class);
+        _policy.setParent(appender);
+        verify(_delegate).setParent(appender);
+    }
+
+    public void testStart()
+    {
+        _policy.start();
+        verify(_delegate).start();
+    }
+
+    public void testStop()
+    {
+        _policy.stop();
+        verify(_delegate).stop();
+    }
+
+    public void testIsStarted()
+    {
+        _policy.isStarted();
+        verify(_delegate).isStarted();
+    }
+
+}

Added: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RolloverWatcherTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RolloverWatcherTest.java?rev=1688178&view=auto
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RolloverWatcherTest.java (added)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/logging/logback/RolloverWatcherTest.java Mon Jun 29 10:55:24 2015
@@ -0,0 +1,140 @@
+/*
+ *
+ * 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.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.apache.qpid.server.model.TypedContent;
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.test.utils.TestFileUtils;
+import org.apache.qpid.util.FileUtils;
+
+public class RolloverWatcherTest extends QpidTestCase
+{
+    private RolloverWatcher _rolloverWatcher;
+    private File _baseFolder;
+
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        _rolloverWatcher = new RolloverWatcher();
+        _baseFolder = TestFileUtils.createTestDirectory("rollover", true);
+    }
+
+    @Override
+    public void tearDown() throws Exception
+    {
+        super.tearDown();
+        if (_baseFolder.exists())
+        {
+            FileUtils.delete(_baseFolder, true);
+        }
+    }
+
+    public void testOnRollover() throws Exception
+    {
+        String[] files = {"test1", "test2"};
+        _rolloverWatcher.onRollover(_baseFolder.toPath(), files);
+
+        assertEquals("Unexpected rolled files. Expected " + Arrays.toString(files) + " but got "
+                        + _rolloverWatcher.getRolledFiles(), new HashSet<>(Arrays.asList(files)),
+                new HashSet<>(_rolloverWatcher.getRolledFiles()));
+    }
+
+    public void testGetTypedContentForActiveFile() throws Exception
+    {
+        _rolloverWatcher.onRollover(_baseFolder.toPath(), new String[]{});
+
+        final File activeFile = new File(_baseFolder, "test.log");
+        TestFileUtils.saveTextContentInFile("test", activeFile);
+        TypedContent content = _rolloverWatcher.getTypedContent(activeFile.getAbsolutePath(), true);
+
+        assertEquals("Unexpected content type", "text/plain", content.getContentType());
+        assertEquals("Unexpected data", "test", readStream(content.openInputStream()));
+        assertEquals("Unexpected size", 4, content.getSize());
+    }
+
+    public void testGetTypedForNullFile() throws Exception
+    {
+        try
+        {
+            _rolloverWatcher.getTypedContent(null, true);
+            fail("IllegalArgumentException is expected for null file name");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // pass
+        }
+    }
+
+    public void testGetTypedContentForExistingRolledFile() throws Exception
+    {
+        String[] files = {"test1.gz", "test2.gz"};
+        _rolloverWatcher.onRollover(_baseFolder.toPath(), files);
+
+        TestFileUtils.saveTextContentInFile("test.gz", new File(_baseFolder, "test1.gz"));
+
+        TypedContent content = _rolloverWatcher.getTypedContent("test1.gz", false);
+
+        assertEquals("Unexpected content type", "application/x-gzip", content.getContentType());
+        assertEquals("Unexpected data", "test.gz", readStream(content.openInputStream()));
+        assertEquals("Unexpected size", 7, content.getSize());
+    }
+
+    public void testGetTypedContentForNonExistingRolledFile() throws Exception
+    {
+        String[] files = {"test1.gz", "test2.gz"};
+        Path baseFolder = new File(getTestName()).toPath();
+        _rolloverWatcher.onRollover(baseFolder, files);
+
+        TypedContent content = _rolloverWatcher.getTypedContent("test3.zip", false);
+
+        assertEquals("Unexpected content type", "application/x-zip", content.getContentType());
+        assertNull("Unexpected content stream", content.openInputStream());
+        assertEquals("Unexpected size", 0, content.getSize());
+    }
+
+    public void testGetContentType() throws Exception
+    {
+        assertEquals("Unexpected content type for log file", "text/plain", _rolloverWatcher.getContentType("test.log"));
+        assertEquals("Unexpected content type for gzip file", "application/x-gzip", _rolloverWatcher.getContentType("test.gz"));
+        assertEquals("Unexpected content type for zip file", "application/x-zip", _rolloverWatcher.getContentType("test.zip"));
+    }
+
+    private String readStream(InputStream contentStream) throws IOException
+    {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int length;
+        while ((length = contentStream.read(buffer)) > 0)
+        {
+            os.write(buffer, 0, length);
+        }
+        return new String(os.toByteArray());
+    }
+}

Added: qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/util/LoggerTestHelper.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/util/LoggerTestHelper.java?rev=1688178&view=auto
==============================================================================
--- qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/util/LoggerTestHelper.java (added)
+++ qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/util/LoggerTestHelper.java Mon Jun 29 10:55:24 2015
@@ -0,0 +1,83 @@
+/*
+ * 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.util;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.read.ListAppender;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class LoggerTestHelper
+{
+    private final static ch.qos.logback.classic.Logger ROOT_LOGGER = ((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME));
+
+    public static ListAppender createAndRegisterAppender(String appenderName)
+    {
+        ListAppender appender = new ListAppender();
+        appender.setName(appenderName);
+        ROOT_LOGGER.addAppender(appender);
+        appender.start();
+        return appender;
+    }
+
+    public static void deleteAndUnregisterAppender(Appender appender)
+    {
+        appender.stop();
+        ROOT_LOGGER.detachAppender(appender);
+    }
+
+    public static void deleteAndUnregisterAppender(String appenderName)
+    {
+        Appender appender = ROOT_LOGGER.getAppender(appenderName);
+        if (appender != null)
+        {
+            deleteAndUnregisterAppender(appender);
+        }
+    }
+
+    public static void assertLoggedEvent(ListAppender appender, boolean exists, String message, String loggerName, Level level)
+    {
+        List<ILoggingEvent> events;
+        synchronized(appender)
+        {
+            events = new ArrayList<>(appender.list);
+        }
+
+        boolean logged = false;
+        for (ILoggingEvent event: events)
+        {
+            if (event.getFormattedMessage().equals(message) && event.getLoggerName().equals(loggerName) && event.getLevel() == level)
+            {
+                logged = true;
+                break;
+            }
+        }
+        assertEquals("Event " + message + " from logger " + loggerName + " of log level " + level
+                + " is " + (exists ? "not" : "") + " found", exists, logged);
+    }
+
+}

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java Mon Jun 29 10:55:24 2015
@@ -20,7 +20,9 @@
  */
 package org.apache.qpid.server.management.plugin.servlet.rest;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
@@ -37,6 +39,7 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.qpid.server.model.TypedContent;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.SerializationConfig;
 import org.slf4j.Logger;
@@ -328,4 +331,45 @@ public abstract class AbstractServlet ex
         }
         return null;
     }
+
+    protected void writeTypedContent(TypedContent typedContent, HttpServletRequest request, HttpServletResponse response) throws IOException
+    {
+        response.setContentType(typedContent.getContentType());
+        response.setContentLength((int) typedContent.getSize());
+        String fileName = typedContent.getFileName();
+        if (fileName != null)
+        {
+            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + "\"");
+        }
+
+        try (InputStream contentStream = typedContent.openInputStream())
+        {
+            writeStreamContent(contentStream, request, response);
+        }
+        catch (FileNotFoundException e)
+        {
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+        }
+    }
+
+    protected void writeStreamContent(InputStream contentStream, HttpServletRequest request, HttpServletResponse response) throws IOException
+    {
+        if (contentStream == null)
+        {
+            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+        }
+        else
+        {
+            response.setStatus(HttpServletResponse.SC_OK);
+            try (OutputStream os = getOutputStream(request, response))
+            {
+                byte[] buffer = new byte[8192];
+                int length;
+                while ((length = contentStream.read(buffer)) > 0)
+                {
+                    os.write(buffer, 0, length);
+                }
+            }
+        }
+    }
 }

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java Mon Jun 29 10:55:24 2015
@@ -52,9 +52,7 @@ public class MessageContentServlet exten
         TypedContent content = queue.getMessageContent(Long.parseLong(path[2]));
         if(content != null)
         {
-            response.setContentType(content.getContentType());
-            response.setContentLength(content.getData().length);
-            getOutputStream(request, response).write(content.getData());
+            writeTypedContent(content, request, response);
         }
     }
 

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java?rev=1688178&r1=1688177&r2=1688178&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java Mon Jun 29 10:55:24 2015
@@ -16,7 +16,9 @@
  */
 package org.apache.qpid.server.management.plugin.servlet.rest;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
@@ -608,10 +610,7 @@ public class RestServlet extends Abstrac
         if(returnVal instanceof TypedContent)
         {
             TypedContent typedContent = (TypedContent)returnVal;
-            response.setStatus(HttpServletResponse.SC_OK);
-            response.setContentType(typedContent.getContentType());
-            response.setContentLength(typedContent.getData().length);
-            getOutputStream(request, response).write(typedContent.getData());
+            writeTypedContent(typedContent, request, response);
         }
         else
         {



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