From e6e3a0299c6abae113bf5632d1cf12e9e4ea068d Mon Sep 17 00:00:00 2001
From: brinn <brinn>
Date: Tue, 9 Oct 2007 08:31:56 +0000
Subject: [PATCH] add: feature to ignore a specifiable number of errors in
 listing a directory and log only then

SVN: 2063
---
 .../utilities/DirectoryScanningTimerTask.java | 37 ++++++++--
 .../common/logging/LogMonitoringAppender.java |  5 ++
 .../DirectoryScanningTimerTaskTest.java       | 68 ++++++++++++++++++-
 3 files changed, 102 insertions(+), 8 deletions(-)

diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
index ca0cd27531a..da6af97a52f 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
@@ -61,10 +61,13 @@ public final class DirectoryScanningTimerTask extends TimerTask implements ISelf
         };
 
     private final IPathHandler handler;
-    
+
     private final File sourceDirectory;
 
-    private boolean errorReadingDirectory;
+    /** The number of consecutive errors of reading a directory that need to occur before the event is logged. */
+    private final int ignoredErrorCount;
+
+    private int errorCountReadingDirectory;
 
     private final FileFilter filter;
 
@@ -82,11 +85,29 @@ public final class DirectoryScanningTimerTask extends TimerTask implements ISelf
      * @param handler The handler that is used for treating the matching paths.
      */
     public DirectoryScanningTimerTask(File sourceDirectory, FileFilter filter, IPathHandler handler)
+    {
+        this(sourceDirectory, filter, handler, 0);
+    }
+
+    /**
+     * Creates a <var>DirectoryScanningTimerTask</var>.
+     * 
+     * @param sourceDirectory The directory to scan for entries.
+     * @param filter The file filter that picks the entries to handle.
+     * @param handler The handler that is used for treating the matching paths.
+     * @param ignoredErrorCount The number of consecutive errors of reading the directory that need to occur before the
+     *            next error is logged (can be used to suppress error when the directory is on a remote share and the
+     *            server is flaky sometimes)
+     */
+    public DirectoryScanningTimerTask(File sourceDirectory, FileFilter filter, IPathHandler handler,
+            int ignoredErrorCount)
     {
         assert sourceDirectory != null;
         assert filter != null;
         assert handler != null;
+        assert ignoredErrorCount >= 0;
 
+        this.ignoredErrorCount = ignoredErrorCount;
         this.sourceDirectory = sourceDirectory;
         this.filter = filter;
         this.handler = handler;
@@ -154,18 +175,24 @@ public final class DirectoryScanningTimerTask extends TimerTask implements ISelf
 
     private File[] listFiles()
     {
-        final boolean logErrors = (errorReadingDirectory == false);
+        final boolean logErrors = (errorCountReadingDirectory == ignoredErrorCount); // Avoid mailbox flooding.
         final ISimpleLogger errorLogger = logErrors ? createSimpleErrorLogger() : null;
 
         final File[] paths = FileUtilities.tryListFiles(sourceDirectory, filter, errorLogger);
-        if (errorReadingDirectory && paths != null)
+        if (errorCountReadingDirectory > ignoredErrorCount && paths != null)
         {
             if (notificationLog.isInfoEnabled())
             {
                 notificationLog.info(String.format("Directory '%s' is available again.", sourceDirectory));
             }
         }
-        errorReadingDirectory = (paths == null); // Avoid mailbox flooding.
+        if (paths == null)
+        {
+            ++errorCountReadingDirectory;
+        } else
+        {
+            errorCountReadingDirectory = 0;
+        }
         return (paths == null) ? new File[0] : paths;
     }
 
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/logging/LogMonitoringAppender.java b/common/sourceTest/java/ch/systemsx/cisd/common/logging/LogMonitoringAppender.java
index 1b2877e3efd..4f1f3b61936 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/logging/LogMonitoringAppender.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/logging/LogMonitoringAppender.java
@@ -109,6 +109,11 @@ public final class LogMonitoringAppender extends AppenderSkeleton
         return false;
     }
 
+    public void verifyLogHasNotHappened()
+    {
+        assert logHappened == false : "Following log snipped was found but shouldn't have: " + messagePart;
+    }
+    
     public void verifyLogHasHappened()
     {
         assert logHappened : "Following log snippet has been missed: " + messagePart;
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java
index 28bbb47e9fd..802f22a225b 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java
@@ -138,9 +138,10 @@ public class DirectoryScanningTimerTaskTest
         file.delete();
         file.deleteOnExit();
         file.createNewFile();
-        final DirectoryScanningTimerTask task = new DirectoryScanningTimerTask(file, ACCEPT_ALL_FILTER, mockPathHandler);
+        final DirectoryScanningTimerTask task =
+                new DirectoryScanningTimerTask(file, ACCEPT_ALL_FILTER, mockPathHandler);
         task.check();
-   }
+    }
 
     @Test(groups =
         { "requires_unix" }, expectedExceptions =
@@ -156,7 +157,8 @@ public class DirectoryScanningTimerTaskTest
         try
         {
             // Here we should get an AssertationError
-            final DirectoryScanningTimerTask task = new DirectoryScanningTimerTask(readOnlyDirectory, ACCEPT_ALL_FILTER, mockPathHandler);
+            final DirectoryScanningTimerTask task =
+                    new DirectoryScanningTimerTask(readOnlyDirectory, ACCEPT_ALL_FILTER, mockPathHandler);
             task.check();
         } finally
         {
@@ -359,4 +361,64 @@ public class DirectoryScanningTimerTaskTest
         LogMonitoringAppender.removeAppender(appender2);
     }
 
+    @Test
+    public void testSuppressLogging() throws IOException
+    {
+        final File dir = new File(workingDirectory, "testSuppressLogging");
+        dir.mkdir();
+        LogMonitoringAppender appenderError =
+                LogMonitoringAppender.addAppender(LogCategory.NOTIFY, "Failed to get listing of directory");
+        LogMonitoringAppender appenderOK =
+            LogMonitoringAppender.addAppender(LogCategory.NOTIFY, "' is available again");
+        final int numberOfErrorsToIgnore = 2;
+        // The directory needs to exist when the scanner is created, otherwise the self-test will fail.
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(dir, ACCEPT_ALL_FILTER, mockPathHandler, numberOfErrorsToIgnore);
+        dir.delete();
+        assert dir.exists() == false;
+        // First error -> ignored
+        scanner.run();
+        appenderError.verifyLogHasNotHappened();
+        // Second error -> ignored
+        scanner.run();
+        appenderError.verifyLogHasNotHappened();
+        // Third error -> recorded
+        scanner.run();
+        appenderError.verifyLogHasHappened();
+        dir.mkdir();
+        assert dir.exists();
+        // Now it is OK again and that should be logged as well
+        scanner.run();
+        appenderOK.verifyLogHasHappened();
+        LogMonitoringAppender.removeAppender(appenderError);
+        LogMonitoringAppender.removeAppender(appenderOK);
+    }
+
+    @Test
+    public void testDoNotLogDirectoryAvailableWhenNoErrorWasLogged() throws IOException
+    {
+        final File dir = new File(workingDirectory, "testDoNotLogDirectoryAvailableWhenNoErrorWasLogged");
+        dir.mkdir();
+        LogMonitoringAppender appender =
+                LogMonitoringAppender.addAppender(LogCategory.NOTIFY, "' is available again.");
+        final int numberOfErrorsToIgnore = 2;
+        // The directory needs to exist when the scanner is created, otherwise the self-test will fail.
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(dir, ACCEPT_ALL_FILTER, mockPathHandler, numberOfErrorsToIgnore);
+        dir.delete();
+        assert dir.exists() == false;
+        // First error -> ignored
+        scanner.run();
+        appender.verifyLogHasNotHappened();
+        // Second error -> ignored
+        scanner.run();
+        appender.verifyLogHasNotHappened();
+        dir.mkdir();
+        assert dir.exists();
+        // Now it's OK, but nothing should be logged because the error wasn't logged either.
+        scanner.run();
+        appender.verifyLogHasNotHappened();
+        LogMonitoringAppender.removeAppender(appender);
+    }
+
 }
-- 
GitLab