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 b23d48dc31d9c1907cad1b3b198dd9260c3ff382..4f2e65fa5ab83b77867d1732940ab39627661dab 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
@@ -94,7 +94,8 @@ public final class DirectoryScanningTimerTask extends TimerTask
      * @param filter The file filter that picks the entries to handle.
      * @param handler The handler that is used for treating the matching paths.
      */
-    public DirectoryScanningTimerTask(File sourceDirectory, FileFilter filter, IPathHandler handler)
+    public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter,
+            final IPathHandler handler)
     {
         this(sourceDirectory, filter, handler, 0);
     }
@@ -109,13 +110,19 @@ public final class DirectoryScanningTimerTask extends TimerTask
      *            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)
+    public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter,
+            final IPathHandler handler, final int ignoredErrorCount)
     {
         this(asScannedStore(sourceDirectory, filter), sourceDirectory, asScanningHandler(
                 sourceDirectory, handler), ignoredErrorCount);
     }
 
+    public DirectoryScanningTimerTask(final File sourceDirectory, final FileFilter filter,
+            final IStoreHandler handler)
+    {
+        this(asScannedStore(sourceDirectory, filter), sourceDirectory, handler, 0);
+    }
+
     /**
      * Creates a <var>DirectoryScanningTimerTask</var>.
      * 
@@ -126,8 +133,8 @@ public final class DirectoryScanningTimerTask extends TimerTask
      *            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(IScannedStore scannedStore, File faultyPathDirectory,
-            IStoreHandler handler, int ignoredErrorCount)
+    public DirectoryScanningTimerTask(final IScannedStore scannedStore,
+            final File faultyPathDirectory, final IStoreHandler handler, final int ignoredErrorCount)
     {
         assert scannedStore != null;
         assert handler != null;
@@ -145,10 +152,18 @@ public final class DirectoryScanningTimerTask extends TimerTask
     {
         return new IStoreHandler()
             {
-                public void handle(StoreItem item)
+
+                //
+                // IStoreHandler
+                //
+
+                public final void handle(final StoreItem item)
                 {
-                    File path = asFile(directory, item);
-                    handler.handle(path);
+                    final File path = asFile(directory, item);
+                    if (handler.mayHandle(path))
+                    {
+                        handler.handle(path);
+                    }
                 }
             };
     }
@@ -157,19 +172,20 @@ public final class DirectoryScanningTimerTask extends TimerTask
     {
         return new IScannedStore()
             {
-                public String getLocationDescription(StoreItem item)
+                public String getLocationDescription(final StoreItem item)
                 {
                     return DirectoryScanningTimerTask.getLocationDescription(asFile(item));
                 }
 
-                public boolean exists(StoreItem item)
+                public boolean exists(final StoreItem item)
                 {
                     return asFile(item).exists();
                 }
 
-                public StoreItem[] tryListSortedReadyToProcess(ISimpleLogger loggerOrNull)
+                public StoreItem[] tryListSortedReadyToProcess(final ISimpleLogger loggerOrNull)
                 {
-                    File[] files = FileUtilities.tryListFiles(directory, filter, loggerOrNull);
+                    final File[] files =
+                            FileUtilities.tryListFiles(directory, filter, loggerOrNull);
                     if (files != null)
                     {
                         FileUtilities.sortByLastModified(files);
@@ -180,9 +196,9 @@ public final class DirectoryScanningTimerTask extends TimerTask
                     }
                 }
 
-                private StoreItem[] asItems(File[] files)
+                private StoreItem[] asItems(final File[] files)
                 {
-                    StoreItem[] items = new StoreItem[files.length];
+                    final StoreItem[] items = new StoreItem[files.length];
                     for (int i = 0; i < items.length; i++)
                     {
                         items[i] = new StoreItem(files[i].getName());
@@ -190,19 +206,19 @@ public final class DirectoryScanningTimerTask extends TimerTask
                     return items;
                 }
 
-                private File asFile(StoreItem item)
+                private File asFile(final StoreItem item)
                 {
                     return DirectoryScanningTimerTask.asFile(directory, item);
                 }
             };
     }
 
-    private static String getLocationDescription(File file)
+    private static String getLocationDescription(final File file)
     {
         return file.getPath();
     }
 
-    private static File asFile(File parentDirectory, StoreItem item)
+    private static File asFile(final File parentDirectory, final StoreItem item)
     {
         return new File(parentDirectory, item.getName());
     }
@@ -221,7 +237,7 @@ public final class DirectoryScanningTimerTask extends TimerTask
             }
             checkForFaultyPathsFileChanged();
             final StoreItem[] paths = listFiles();
-            for (StoreItem path : paths)
+            for (final StoreItem path : paths)
             {
                 if (isFaultyPathsFile(path)) // Never touch the faultyPathsFile.
                 {
@@ -230,8 +246,9 @@ public final class DirectoryScanningTimerTask extends TimerTask
                 try
                 {
                     handle(path);
-                } catch (Exception ex) // do not stop when processing of one file has failed,
-                                        // continue with other files
+                } catch (final Exception ex) // do not stop when processing of one file has
+                // failed,
+                // continue with other files
                 {
                     printNotification(ex);
                 }
@@ -241,21 +258,21 @@ public final class DirectoryScanningTimerTask extends TimerTask
             {
                 operationLog.trace("Finished scanning directory " + sourceDirectory + ".");
             }
-        } catch (Exception ex)
+        } catch (final Exception ex)
         {
             printNotification(ex);
         }
     }
 
-    private void printNotification(Exception ex)
+    private void printNotification(final Exception ex)
     {
         notificationLog.error("An exception has occurred. (thread still running)", ex);
     }
 
-    private boolean isFaultyPathsFile(StoreItem item)
+    private boolean isFaultyPathsFile(final StoreItem item)
     {
-        String itemLocation = sourceDirectory.getLocationDescription(item);
-        String faultyPathsLocation = getLocationDescription(faultyPathsFile);
+        final String itemLocation = sourceDirectory.getLocationDescription(item);
+        final String faultyPathsLocation = getLocationDescription(faultyPathsFile);
         return itemLocation.equals(faultyPathsLocation);
     }
 
@@ -264,7 +281,7 @@ public final class DirectoryScanningTimerTask extends TimerTask
         if (faultyPathsFile.exists())
         {
             if (faultyPathsFile.lastModified() > faultyPathsLastChanged) // Handles manual
-                                                                            // manipulation.
+            // manipulation.
             {
                 faultyPaths.clear();
                 CollectionIO.readCollection(faultyPathsFile, faultyPaths);
@@ -286,8 +303,8 @@ public final class DirectoryScanningTimerTask extends TimerTask
     private StoreItem[] listFiles()
     {
         final boolean logNotifyError = (errorCountReadingDirectory == ignoredErrorCount); // Avoid
-                                                                                            // mailbox
-                                                                                            // flooding.
+        // mailbox
+        // flooding.
         final boolean logOperationError = (errorCountReadingDirectory < ignoredErrorCount);
         final ISimpleLogger errorLogger =
                 logNotifyError ? createSimpleErrorLogger(LogCategory.NOTIFY)
@@ -317,7 +334,7 @@ public final class DirectoryScanningTimerTask extends TimerTask
     {
         return new ISimpleLogger()
             {
-                public void log(LogLevel dummyLevel, String message)
+                public void log(final LogLevel dummyLevel, final String message)
                 {
                     if (category == LogCategory.NOTIFY)
                     {
@@ -330,7 +347,7 @@ public final class DirectoryScanningTimerTask extends TimerTask
             };
     }
 
-    private void handle(StoreItem item)
+    private void handle(final StoreItem item)
     {
         if (isFaultyPath(item))
         { // Guard: skip faulty paths.
@@ -351,15 +368,15 @@ public final class DirectoryScanningTimerTask extends TimerTask
         }
     }
 
-    private boolean isFaultyPath(StoreItem item)
+    private boolean isFaultyPath(final StoreItem item)
     {
-        String path = sourceDirectory.getLocationDescription(item);
+        final String path = sourceDirectory.getLocationDescription(item);
         return faultyPaths.contains(path);
     }
 
-    private void addToFaultyPaths(StoreItem item)
+    private void addToFaultyPaths(final StoreItem item)
     {
-        String path = sourceDirectory.getLocationDescription(item);
+        final String path = sourceDirectory.getLocationDescription(item);
         faultyPaths.add(path);
         CollectionIO.writeIterable(faultyPathsFile, faultyPaths);
         faultyPathsLastChanged = faultyPathsFile.lastModified();
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java b/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java
index 8d6e777a0b455a17b6cb524ea535a983c648a9ad..19670dc0be85244da795e33d92feb8a9014de674 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/IPathHandler.java
@@ -31,4 +31,12 @@ public interface IPathHandler
      * when the method returns.
      */
     public void handle(File path);
+
+    /**
+     * Whether given <var>path</var> may be handled or not.
+     * <p>
+     * This method is called just before {@link #handle(File)}.
+     * </p>
+     */
+    public boolean mayHandle(File path);
 }
\ No newline at end of file
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/WatermarkWatcher.java b/common/source/java/ch/systemsx/cisd/common/utilities/WatermarkWatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ef041964358139ec8eebd672cf1f473130bd65f
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/WatermarkWatcher.java
@@ -0,0 +1,123 @@
+/*
+ * 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.utilities;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileSystemUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+
+/**
+ * A watermark watcher.
+ * <p>
+ * This class is thread-safe.
+ * </p>
+ * 
+ * @see FileSystemUtils
+ * @author Christian Ribeaud
+ */
+public final class WatermarkWatcher
+{
+
+    private static final Logger notificationLog =
+            LogFactory.getLogger(LogCategory.NOTIFY, WatermarkWatcher.class);
+
+    private static final Logger operationLog =
+            LogFactory.getLogger(LogCategory.OPERATION, WatermarkWatcher.class);
+
+    private final long watermark;
+
+    private boolean notified;
+
+    /**
+     * @param watermark the watermark value in kilobytes. If negative, then
+     *            {@link #isWatermarkReached(File)} always return <code>false</code>.
+     */
+    public WatermarkWatcher(final long watermark)
+    {
+        this.watermark = watermark;
+    }
+
+    private final static String displayKilobyteValue(final long value)
+    {
+        return FileUtils.byteCountToDisplaySize(value * FileUtils.ONE_KB);
+    }
+
+    /**
+     * Checks whether the watermark value is reached.
+     * <p>
+     * If so, then informs (only once!) the administrator (via notification log) about it. The
+     * administrator gets also informed when the space is sufficient again.
+     * </p>
+     */
+    public final synchronized boolean isWatermarkReached(final File path)
+    {
+        if (watermark < 0)
+        {
+            return false;
+        }
+        try
+        {
+            final String canonicalPath = path.getCanonicalPath();
+            final long freeSpace = FileSystemUtils.freeSpaceKb(canonicalPath);
+            final String freeSpaceDisplayed = displayKilobyteValue(freeSpace);
+            if (freeSpace <= watermark)
+            {
+                if (notified == false)
+                {
+                    notificationLog.warn(String.format(
+                            "The amount of available space (%s) on '%s' "
+                                    + "is lower than the specified watermark (%s).",
+                            freeSpaceDisplayed, canonicalPath, displayKilobyteValue(watermark)));
+                }
+                notified = true;
+                return true;
+            } else
+            {
+                if (notified == true)
+                {
+                    notificationLog
+                            .info(String
+                                    .format(
+                                            "The amount of available space (%s) on '%s' "
+                                                    + "is again sufficient (greater than the specified watermark: %s).",
+                                            freeSpaceDisplayed, canonicalPath,
+                                            displayKilobyteValue(watermark)));
+                } else
+                {
+                    if (operationLog.isInfoEnabled())
+                    {
+                        operationLog.info(String.format(
+                                "Amount of available space on '%s' is: %s.", canonicalPath,
+                                freeSpaceDisplayed));
+                    }
+                }
+                notified = false;
+                return false;
+            }
+        } catch (IOException ex)
+        {
+            operationLog.error("The watermark watcher can not work properly.", ex);
+            return false;
+        }
+    }
+}
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 32b983603e22cb2a0467f6f57249cbcbe7c18516..53df73c9f64dddf0b44ab32eecc5f34fc5f548af 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java
@@ -73,7 +73,7 @@ public class DirectoryScanningTimerTaskTest
     /**
      * A mock implementation that stores the handled paths.
      */
-    public static class MockPathHandler implements IPathHandler
+    private static class MockPathHandler implements IPathHandler
     {
 
         final List<File> handledPaths = new ArrayList<File>();
@@ -83,12 +83,21 @@ public class DirectoryScanningTimerTaskTest
             handledPaths.clear();
         }
 
-        public void handle(File path)
+        //
+        // IPathHandler
+        //
+
+        public void handle(final File path)
         {
             handledPaths.add(path);
             path.delete();
         }
 
+        public final boolean mayHandle(final File path)
+        {
+            return true;
+        }
+
     }
 
     @BeforeClass
@@ -157,7 +166,7 @@ public class DirectoryScanningTimerTaskTest
         assert someFile.exists();
         final DirectoryScanningTimerTask scanner =
                 new DirectoryScanningTimerTask(workingDirectory, ACCEPT_ALL_FILTER, mockPathHandler);
-        String fileLocation = someFile.getPath();
+        final String fileLocation = someFile.getPath();
         CollectionIO.writeIterable(faultyPaths, Collections.singleton(fileLocation));
         scanner.run();
         assertEquals(0, mockPathHandler.handledPaths.size());
@@ -193,7 +202,7 @@ public class DirectoryScanningTimerTaskTest
         // See whether faulty_paths settings works.
         scanner.run();
         assertEquals(0, mockPathHandler.handledPaths.size());
-        List<String> faulty = CollectionIO.readList(faultyPaths);
+        final List<String> faulty = CollectionIO.readList(faultyPaths);
         assertEquals(1, faulty.size());
         assertEquals(someFile.getPath(), faulty.get(0));
         // See whether fault_paths resetting works.