diff --git a/common/source/java/ch/systemsx/cisd/common/logging/ConditionalNotificationLogger.java b/common/source/java/ch/systemsx/cisd/common/logging/ConditionalNotificationLogger.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d7dde6aeca8e42d5df406517aa0bdc1c1ba7cda
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/logging/ConditionalNotificationLogger.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008 ETH Zuerich, CISD
+ *
+ * Licensed 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 ch.systemsx.cisd.common.logging;
+
+import org.apache.log4j.Logger;
+
+/**
+ * An <code>ISimpleLogger</code> implementation which sends an email (via logger) when the number
+ * of log errors reaches a given number.
+ * <p>
+ * Note that this class sends one and only one notification email. To reset the state of this class,
+ * use {@link #reset(String)}.
+ * </p>
+ * 
+ * @author Christian Ribeaud
+ */
+public final class ConditionalNotificationLogger implements ISimpleLogger
+{
+    private final Logger notificationLog;
+
+    private final Logger operationLog;
+
+    private final int ignoredErrorCountBeforeNotification;
+
+    private int errorCount;
+
+    private boolean notified;
+
+    /**
+     * @param ignoredErrorCountBeforeNotification the number of errors that are ignored before
+     *            sending a notification email.
+     */
+    public ConditionalNotificationLogger(final Class<?> clazz,
+            final int ignoredErrorCountBeforeNotification)
+    {
+        assert clazz != null : "Unspecified class";
+        assert ignoredErrorCountBeforeNotification > -1 : "Negative ignored error "
+                + "count before notification";
+        this.ignoredErrorCountBeforeNotification = ignoredErrorCountBeforeNotification;
+        operationLog = LogFactory.getLogger(LogCategory.OPERATION, clazz);
+        notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, clazz);
+    }
+
+    /**
+     * Resets counting of errors and <code>notified</code> flag.
+     * <p>
+     * As side effect, it sends a notification log to inform that we are "green" again (if and only
+     * if an email has already been sent to inform the "bad" state and if log info is enabled).
+     * </p>
+     * 
+     * @param message the info log message.
+     */
+    public final void reset(final String message)
+    {
+        if (notified && notificationLog.isInfoEnabled())
+        {
+            notificationLog.info(message);
+        }
+        errorCount = 0;
+        notified = false;
+    }
+
+    //
+    // ISimpleLogger
+    //
+
+    public final void log(final LogLevel level, final String message)
+    {
+        if (LogLevel.ERROR.equals(level))
+        {
+            if (errorCount < ignoredErrorCountBeforeNotification)
+            {
+                operationLog.warn(message);
+            } else if (notified == false)
+            {
+                notificationLog.error(message);
+                notified = true;
+            }
+            errorCount++;
+        }
+    }
+}
\ No newline at end of file
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 296c522320140acf07229cf54cab30089be91dea..b47fa208f41f1e234333e42d46fcd10caafb6a74 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
@@ -22,10 +22,10 @@ import java.util.TimerTask;
 
 import org.apache.log4j.Logger;
 
+import ch.systemsx.cisd.common.logging.ConditionalNotificationLogger;
 import ch.systemsx.cisd.common.logging.ISimpleLogger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
-import ch.systemsx.cisd.common.logging.LogLevel;
 
 /**
  * A {@link TimerTask} that scans a source directory for entries that are accepted by some
@@ -58,8 +58,6 @@ public final class DirectoryScanningTimerTask extends TimerTask
      */
     private final int ignoredErrorCount;
 
-    private int errorCountReadingDirectory;
-
     private final IDirectoryScanningHandler directoryScanningHandler;
 
     /**
@@ -214,54 +212,18 @@ public final class DirectoryScanningTimerTask extends TimerTask
 
     private final StoreItem[] listStoreItems()
     {
-        // Avoid mailbox flooding.
-        final boolean logNotifyError = (errorCountReadingDirectory == ignoredErrorCount);
-        final boolean logOperationError = (errorCountReadingDirectory < ignoredErrorCount);
-        final ISimpleLogger errorLogger =
-                logNotifyError ? createSimpleErrorLogger(LogCategory.NOTIFY)
-                        : (logOperationError ? createSimpleErrorLogger(LogCategory.OPERATION)
-                                : null);
-        final StoreItem[] storeItems = sourceDirectory.tryListSortedReadyToProcess(errorLogger);
-        if (errorCountReadingDirectory > ignoredErrorCount && storeItems != null)
+        final ConditionalNotificationLogger notificationLogger =
+                new ConditionalNotificationLogger(getClass(), ignoredErrorCount);
+        final StoreItem[] storeItems =
+                sourceDirectory.tryListSortedReadyToProcess(notificationLogger);
+        if (storeItems != null)
         {
-            if (notificationLog.isInfoEnabled())
-            {
-                notificationLog.info(String.format("Directory '%s' is available again.",
-                        sourceDirectory));
-            }
-        }
-        if (storeItems == null)
-        {
-            ++errorCountReadingDirectory;
-        } else
-        {
-            errorCountReadingDirectory = 0;
+            notificationLogger.reset(String.format("Directory '%s' is available again.",
+                    sourceDirectory));
         }
         return (storeItems == null) ? StoreItem.EMPTY_ARRAY : storeItems;
     }
 
-    private final ISimpleLogger createSimpleErrorLogger(final LogCategory category)
-    {
-        return new ISimpleLogger()
-            {
-
-                //
-                // ISimpleLogger
-                //
-
-                public final void log(final LogLevel dummyLevel, final String message)
-                {
-                    if (category == LogCategory.NOTIFY)
-                    {
-                        notificationLog.log(org.apache.log4j.Level.ERROR, message);
-                    } else
-                    {
-                        operationLog.log(org.apache.log4j.Level.WARN, message);
-                    }
-                }
-            };
-    }
-
     //
     // Helper classes
     //
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/logging/ConditionalNotificationLoggerTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/logging/ConditionalNotificationLoggerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..177fa605a46732f539dd1c1fd3bc95abe66be1e1
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/logging/ConditionalNotificationLoggerTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2008 ETH Zuerich, CISD
+ *
+ * Licensed 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 ch.systemsx.cisd.common.logging;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Level;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * Test cases for corresponding {@link ConditionalNotificationLogger} class.
+ * 
+ * @author Christian Ribeaud
+ */
+public final class ConditionalNotificationLoggerTest
+{
+
+    private final ConditionalNotificationLogger createConditionalNotificationLogger(
+            final int ignoredErrorCountBeforeNotification)
+    {
+        final ConditionalNotificationLogger logger =
+                new ConditionalNotificationLogger(getClass(), ignoredErrorCountBeforeNotification);
+        return logger;
+    }
+
+    private BufferedAppender operationLogRecorder;
+
+    private BufferedAppender notificationLogRecorder;
+
+    @BeforeMethod
+    public final void setUp()
+    {
+        operationLogRecorder = new BufferedAppender("%m", Level.WARN);
+        notificationLogRecorder = new BufferedAppender("%m", Level.ERROR);
+    }
+
+    @Test
+    public final void testConstructor()
+    {
+        boolean fail = true;
+        try
+        {
+            new ConditionalNotificationLogger(null, -1);
+        } catch (final AssertionError e)
+        {
+            fail = false;
+        }
+        try
+        {
+            new ConditionalNotificationLogger(getClass(), -1);
+        } catch (final AssertionError e)
+        {
+            fail = false;
+        }
+        assertFalse(fail);
+    }
+
+    @DataProvider(name = "ignoredErrorCount")
+    public final Object[][] getIgnoredErrorCount()
+    {
+        return new Object[][]
+            {
+                { 0 },
+                { 1 },
+                { 2 }, };
+    }
+
+    @Test(dataProvider = "ignoredErrorCount")
+    public final void testLogWithErrorLevel(final int ignoredErrorCountBeforeNotification)
+    {
+        final ConditionalNotificationLogger logger =
+                createConditionalNotificationLogger(ignoredErrorCountBeforeNotification);
+        final String logMessage = "Some message";
+        for (int i = 0; i <= ignoredErrorCountBeforeNotification; i++)
+        {
+            logger.log(LogLevel.ERROR, logMessage);
+            assertEquals(StringUtils.repeat(logMessage, i + 1), operationLogRecorder
+                    .getLogContent());
+            if (i >= ignoredErrorCountBeforeNotification)
+            {
+                assertEquals(logMessage, notificationLogRecorder.getLogContent());
+            }
+        }
+        logger.log(null, logMessage);
+        assertEquals(logMessage, notificationLogRecorder.getLogContent());
+    }
+
+    @Test
+    public final void testLogWithNullLevel()
+    {
+        final ConditionalNotificationLogger logger = createConditionalNotificationLogger(0);
+        final String logMessage = "Some message";
+        logger.log(null, logMessage);
+        assertEquals("", operationLogRecorder.getLogContent());
+        assertEquals("", notificationLogRecorder.getLogContent());
+    }
+
+    @Test
+    public final void testReset()
+    {
+        final ConditionalNotificationLogger logger = createConditionalNotificationLogger(2);
+        final String logMessage = "Some message";
+        logger.log(LogLevel.ERROR, logMessage);
+        logger.log(LogLevel.ERROR, logMessage);
+        logger.log(LogLevel.ERROR, logMessage);
+        assertEquals(logMessage, notificationLogRecorder.getLogContent());
+        logger.log(LogLevel.ERROR, logMessage);
+        assertEquals(logMessage, notificationLogRecorder.getLogContent());
+        logger.reset("Green again");
+        notificationLogRecorder.resetLogContent();
+        logger.log(LogLevel.ERROR, logMessage);
+        assertEquals("", notificationLogRecorder.getLogContent());
+    }
+}