diff --git a/common/source/java/ch/systemsx/cisd/common/Constants.java b/common/source/java/ch/systemsx/cisd/common/Constants.java
new file mode 100644
index 0000000000000000000000000000000000000000..ddfb5d628e1f4cd7322dae32556fd5f4286a9c77
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/Constants.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2007 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;
+
+/**
+ * Constants common to more than one CISD project.
+ *
+ * @author Bernd Rinn
+ */
+public class Constants
+{
+
+    /** The prefix of marker files that indicate that the processing of some path is finished. */
+    public static final String IS_FINISHED_PREFIX = ".is_finished_";
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a0fb7b4cdcba927dc2f1f09d37a4a0823b1fba5
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTask.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2007 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.FileFilter;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TimerTask;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+
+/**
+ * A {@link TimerTask} that scans a source directory for entries that are accepted by some {@link FileFilter} and
+ * handles the accepted entries by some {@link IPathHandler}. It maintains a list of faulty paths that failed to be
+ * handled OK in the past. Clearing the list will make the class to retry handling the paths.
+ * <p>
+ * The class should be constructed in the start-up phase and as part of the system's self-test in order to reveal
+ * problems with incorrect paths timely.
+ * 
+ * @author Bernd Rinn
+ */
+public final class DirectoryScanningTimerTask extends TimerTask implements ISelfTestable
+{
+
+    public static final String FAULTY_PATH_FILENAME = ".faulty_paths";
+
+    private static final Logger operationLog =
+            LogFactory.getLogger(LogCategory.OPERATION, DirectoryScanningTimerTask.class);
+
+    private static final Logger notificationLog =
+            LogFactory.getLogger(LogCategory.NOTIFY, DirectoryScanningTimerTask.class);
+
+    private static final IFromStringConverter<File> FILE_CONVERTER = new IFromStringConverter<File>()
+        {
+            public File fromString(String value)
+            {
+                return new File(value);
+            }
+        };
+
+    private final IPathHandler handler;
+
+    private final File sourceDirectory;
+
+    private boolean errorReadingDirectory;
+
+    private final FileFilter filter;
+
+    private final Set<File> faultyPaths;
+
+    private final File faultyPathsFile;
+
+    private long faultyPathsLastChanged;
+
+    /**
+     * A handler for paths. The paths are supposed to go away when they have been handled successfully.
+     */
+    public interface IPathHandler
+    {
+        /**
+         * Handles the <var>path</var>.
+         * 
+         * @return <code>true</code> if the <var>path</var> has been handled correctly and <code>false</code>
+         *         otherwise.
+         */
+        public boolean handle(File path);
+    }
+
+    /**
+     * 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.
+     */
+    public DirectoryScanningTimerTask(File sourceDirectory, FileFilter filter, IPathHandler handler)
+    {
+        assert sourceDirectory != null;
+        assert filter != null;
+        assert handler != null;
+
+        this.sourceDirectory = sourceDirectory;
+        this.filter = filter;
+        this.handler = handler;
+        this.faultyPaths = new HashSet<File>();
+        this.faultyPathsFile = new File(sourceDirectory, FAULTY_PATH_FILENAME);
+        faultyPathsFile.delete();
+    }
+
+    /**
+     * Handles all entries in {@link #sourceDirectory} that are picked by the {@link #filter} by some {@link #handler}.
+     */
+    @Override
+    public void run()
+    {
+        if (operationLog.isTraceEnabled())
+        {
+            operationLog.trace("Start scanning directory.");
+        }
+        checkForFaultyPathsFileChanged();
+        final File[] paths = listFiles();
+        if (paths == null) // Means: error reading directory listing
+        {
+            return;
+        }
+        // Sort in order of "oldest first" in order to move older items before newer items. This becomes important when
+        // doing online quality control of measurements.
+        Arrays.sort(paths, new Comparator<File>()
+            {
+                public int compare(File o1, File o2)
+                {
+                    return (int) (o1.lastModified() - o2.lastModified());
+                }
+            });
+        for (File path : paths)
+        {
+            if (faultyPathsFile.equals(path)) // Never touch the faultyPathsFile.
+            {
+                continue;
+            }
+            handle(path);
+        }
+        if (operationLog.isTraceEnabled())
+        {
+            operationLog.trace("Finished scanning directory.");
+        }
+    }
+
+    private void checkForFaultyPathsFileChanged()
+    {
+        if (faultyPathsFile.exists())
+        {
+            if (faultyPathsFile.lastModified() > faultyPathsLastChanged) // Handles manual manipulation.
+            {
+                faultyPaths.clear();
+                CollectionIO.readCollection(faultyPathsFile, faultyPaths, FILE_CONVERTER);
+                faultyPathsLastChanged = faultyPathsFile.lastModified();
+                if (operationLog.isInfoEnabled())
+                {
+                    operationLog.info(String.format("Reread faulty paths file (%s), new set contains %d entries",
+                            faultyPathsFile.getPath(), faultyPaths.size()));
+                }
+            }
+        } else
+        // Handles manual removal.
+        {
+            faultyPaths.clear();
+        }
+    }
+
+    private File[] listFiles()
+    {
+        File[] paths = null;
+        RuntimeException ex = null;
+        try
+        {
+            paths = sourceDirectory.listFiles(filter);
+        } catch (RuntimeException e)
+        {
+            ex = e;
+        }
+        if (paths == null)
+        {
+            if (errorReadingDirectory == false)
+            {
+                logFailureInDirectoryListing(ex);
+                errorReadingDirectory = true; // Avoid mailbox flooding.
+            }
+        } else
+        {
+            errorReadingDirectory = false;
+        }
+        return paths;
+    }
+
+    private void logFailureInDirectoryListing(RuntimeException ex)
+    {
+        if (ex == null)
+        {
+            if (sourceDirectory.isFile())
+            {
+                notificationLog.error(String
+                        .format("Failed to get listing of directory '%s' (path is file instead of directory).",
+                                sourceDirectory));
+            } else
+            {
+                notificationLog.error(String.format("Failed to get listing of directory '%s' (path not found).",
+                        sourceDirectory));
+            }
+        } else
+        {
+            notificationLog.error(String.format("Failed to get listing of directory '%s'.", sourceDirectory), ex);
+        }
+    }
+
+    private void handle(File path)
+    {
+        if (faultyPaths.contains(path))
+        { // Guard: skip faulty paths.
+            return;
+        }
+
+        final boolean handledOK = handler.handle(path);
+        if (path.exists())
+        {
+            if (handledOK)
+            {
+                operationLog.warn(String.format("Handler %s reports path '%s' be handled OK, but path still exists.",
+                        handler.getClass().getSimpleName(), path));
+            }
+            faultyPaths.add(path);
+            CollectionIO.writeIterable(faultyPathsFile, faultyPaths);
+            faultyPathsLastChanged = faultyPathsFile.lastModified();
+        }
+    }
+
+    public void check() throws ConfigurationFailureException
+    {
+        if (operationLog.isDebugEnabled())
+        {
+            operationLog.debug("Checking source directory '" + sourceDirectory.getAbsolutePath() + "'.");
+        }
+        final String errorMessage = FileUtilities.checkDirectoryFullyAccessible(sourceDirectory, "source");
+        if (errorMessage != null)
+        {
+            throw new ConfigurationFailureException(errorMessage);
+        }
+    }
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IntraFSPathMover.java b/common/source/java/ch/systemsx/cisd/common/utilities/IntraFSPathMover.java
new file mode 100644
index 0000000000000000000000000000000000000000..1df5264dd2ce43017a5272cde27ebf4324bb53f5
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/IntraFSPathMover.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 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 org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+
+/**
+ * A {@link DirectoryScanningTimerTask.IPathHandler} that moves paths out of the way within one file system by calling
+ * {@link File#renameTo(File)}..
+ * 
+ * @author Bernd Rinn
+ */
+public class IntraFSPathMover implements DirectoryScanningTimerTask.IPathHandler
+{
+
+    private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, IntraFSPathMover.class);
+
+    private static final Logger notificationLog = LogFactory.getLogger(LogCategory.NOTIFY, IntraFSPathMover.class);
+
+    private final File destinationDirectory;
+
+    /**
+     * Creates a <var>PathMover</var>.
+     * 
+     * @param destinationDirectory The directory to move paths to.
+     */
+    public IntraFSPathMover(File destinationDirectory)
+    {
+        assert destinationDirectory != null;
+        assert FileUtilities.checkDirectoryFullyAccessible(destinationDirectory, "destination") == null : FileUtilities
+                .checkDirectoryFullyAccessible(destinationDirectory, "destination");
+
+        this.destinationDirectory = destinationDirectory;
+    }
+
+    public boolean handle(File path)
+    {
+        assert path != null;
+        assert destinationDirectory != null;
+
+        if (operationLog.isInfoEnabled())
+        {
+            operationLog
+                    .info(String.format("Moving path '%s' to '%s'", path.getPath(), destinationDirectory.getPath()));
+        }
+        boolean movedOK = path.renameTo(new File(destinationDirectory, path.getName()));
+        if (movedOK == false)
+        {
+            notificationLog.error(String.format("Moving path '%s' to directory '%s' failed.", path,
+                    destinationDirectory));
+        }
+        return movedOK;
+    }
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/NamePrefixFileFilter.java b/common/source/java/ch/systemsx/cisd/common/utilities/NamePrefixFileFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7ee629171913c21ebd370ab4523dec29339af8f
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/NamePrefixFileFilter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2007 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.FileFilter;
+
+/**
+ * A {@link FileFilter} that checks whether the name part of the <var>pathname</var> starts (or does not start) with
+ * the <var>prefix</var> specified.
+ * 
+ * @author Bernd Rinn
+ */
+public class NamePrefixFileFilter implements FileFilter
+{
+
+    private final String prefix;
+
+    private boolean shouldStartWithPrefix;
+
+    public NamePrefixFileFilter(String prefix, boolean shouldStartWithPrefix)
+    {
+        assert prefix != null;
+        
+        this.prefix = prefix;
+        this.shouldStartWithPrefix = shouldStartWithPrefix;
+    }
+
+    public boolean accept(File pathname)
+    {
+        assert pathname != null;
+        
+        final boolean startsWithPrefix = pathname.getName().startsWith(prefix);
+        return startsWithPrefix == shouldStartWithPrefix;
+    }
+
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f48013d7a2aa805922dff9d6a91035917ea6f6f5
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/DirectoryScanningTimerTaskTest.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2007 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 static org.testng.AssertJUnit.assertEquals;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogInitializer;
+import ch.systemsx.cisd.common.logging.LogMonitoringAppender;
+import ch.systemsx.cisd.common.utilities.CollectionIO;
+import ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask;
+import ch.systemsx.cisd.common.utilities.FileUtilities;
+import ch.systemsx.cisd.common.utilities.DirectoryScanningTimerTask.IPathHandler;
+import ch.systemsx.cisd.common.utilities.StoringUncaughtExceptionHandler;
+
+/**
+ * Test cases for the {@link DirectoryScanningTimerTask}.
+ * 
+ * @author Bernd Rinn
+ */
+public class DirectoryScanningTimerTaskTest
+{
+
+    private static final File unitTestRootDirectory = new File("targets" + File.separator + "unit-test-wd");
+
+    private static final File workingDirectory = new File(unitTestRootDirectory, "DirectoryScanningTimerTaskTest");
+
+    private final StoringUncaughtExceptionHandler exceptionHandler = new StoringUncaughtExceptionHandler();
+
+    private final static FileFilter ALWAYS_FALSE_FILE_FILTER = new FileFilter()
+        {
+            public boolean accept(File pathname)
+            {
+                return false;
+            }
+        };
+
+    private final static FileFilter ALWAYS_TRUE_FILE_FILTER = new FileFilter()
+        {
+            public boolean accept(File pathname)
+            {
+                return true;
+            }
+        };
+
+    private final static String EXCEPTION_THROWING_FILE_FILTER_MESSAGE = "Exception throwing file filter does its job.";
+
+    private final static FileFilter EXCEPTION_THROWING_FILE_FILTER = new FileFilter()
+        {
+            public boolean accept(File pathname)
+            {
+                throw new RuntimeException(EXCEPTION_THROWING_FILE_FILTER_MESSAGE);
+            }
+        };
+
+    private final MockPathHandler mockPathHandler = new MockPathHandler();
+
+    /**
+     * A mock implementation that stores the handled paths.
+     */
+    public static class MockPathHandler implements IPathHandler
+    {
+
+        final List<File> handledPaths = new ArrayList<File>();
+
+        public void clear()
+        {
+            handledPaths.clear();
+        }
+
+        public boolean handle(File path)
+        {
+            handledPaths.add(path);
+            path.delete();
+            return true;
+        }
+
+    }
+
+    @BeforeClass
+    public void init()
+    {
+        LogInitializer.init();
+        unitTestRootDirectory.mkdirs();
+        assert unitTestRootDirectory.isDirectory();
+        Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
+    }
+
+    @BeforeMethod
+    public void setUp()
+    {
+        FileUtilities.deleteRecursively(workingDirectory);
+        workingDirectory.mkdirs();
+        workingDirectory.deleteOnExit();
+        mockPathHandler.clear();
+    }
+
+    @AfterMethod
+    public void checkException()
+    {
+        exceptionHandler.checkAndRethrowException();
+    }
+
+    @Test(expectedExceptions =
+        { ConfigurationFailureException.class })
+    public void testFailedConstructionNonExistent()
+    {
+        final File nonExistentFile = new File(unitTestRootDirectory, "non-existent");
+        nonExistentFile.delete();
+        final DirectoryScanningTimerTask task =
+                new DirectoryScanningTimerTask(nonExistentFile, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        task.check();
+    }
+
+    @Test(expectedExceptions =
+        { ConfigurationFailureException.class })
+    public void testFailedConstructionFileInsteadOfDirectory() throws IOException
+    {
+        final File file = new File(unitTestRootDirectory, "existent_file");
+        file.delete();
+        file.deleteOnExit();
+        file.createNewFile();
+        final DirectoryScanningTimerTask task = new DirectoryScanningTimerTask(file, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        task.check();
+   }
+
+    @Test(groups =
+        { "requires_unix" }, expectedExceptions =
+        { ConfigurationFailureException.class })
+    public void testFailedConstructionReadOnly() throws IOException, InterruptedException
+    {
+        final File readOnlyDirectory = new File(unitTestRootDirectory, "read_only_directory");
+        readOnlyDirectory.delete();
+        readOnlyDirectory.mkdir();
+        readOnlyDirectory.deleteOnExit();
+        assert readOnlyDirectory.setReadOnly();
+
+        try
+        {
+            // Here we should get an AssertationError
+            final DirectoryScanningTimerTask task = new DirectoryScanningTimerTask(readOnlyDirectory, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+            task.check();
+        } finally
+        {
+            // Unfortunately, with JDK 5 there is no portable way to set a file or directory read/write, once
+            // it has been set read-only, thus this test 'requires_unix' for the time being.
+            Runtime.getRuntime().exec(String.format("/bin/chmod u+w %s", readOnlyDirectory.getPath())).waitFor();
+            if (readOnlyDirectory.canWrite() == false)
+            {
+                // Can't use assert here since we expect an AssertationError
+                throw new IllegalStateException();
+            }
+        }
+    }
+
+    @Test
+    public void testFaultyPathsDeletion()
+    {
+        final File faultyPaths = new File(workingDirectory, DirectoryScanningTimerTask.FAULTY_PATH_FILENAME);
+        CollectionIO.writeIterable(faultyPaths, Collections.singleton("some_path"));
+        new DirectoryScanningTimerTask(workingDirectory, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        assert faultyPaths.length() == 0;
+    }
+
+    @Test
+    public void testProcessOK() throws IOException
+    {
+        final File someFile = new File(workingDirectory, "some_file");
+        someFile.createNewFile();
+        someFile.deleteOnExit();
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(workingDirectory, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        assertEquals(0, mockPathHandler.handledPaths.size());
+        scanner.run();
+        assertEquals(1, mockPathHandler.handledPaths.size());
+        assertEquals(someFile, mockPathHandler.handledPaths.get(0));
+    }
+
+    @Test
+    public void testFileFilterUsed() throws IOException
+    {
+        final File someFile = new File(workingDirectory, "some_file");
+        someFile.createNewFile();
+        someFile.deleteOnExit();
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(workingDirectory, ALWAYS_FALSE_FILE_FILTER, mockPathHandler);
+        assertEquals(0, mockPathHandler.handledPaths.size());
+        scanner.run();
+        assertEquals(0, mockPathHandler.handledPaths.size());
+    }
+
+    @Test
+    public void testManipulateFaultyPaths() throws IOException
+    {
+        final File faultyPaths = new File(workingDirectory, DirectoryScanningTimerTask.FAULTY_PATH_FILENAME);
+        final File someFile = new File(workingDirectory, "some_file");
+        someFile.createNewFile();
+        someFile.deleteOnExit();
+        assert someFile.exists();
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(workingDirectory, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        CollectionIO.writeIterable(faultyPaths, Collections.singleton(someFile));
+        scanner.run();
+        assertEquals(0, mockPathHandler.handledPaths.size());
+    }
+
+    @Test
+    public void testFaultyPaths() throws IOException
+    {
+        final File faultyPaths = new File(workingDirectory, DirectoryScanningTimerTask.FAULTY_PATH_FILENAME);
+        final File someFile = new File(workingDirectory, "some_file");
+        final MockPathHandler myPathHandler = new MockPathHandler()
+            {
+                boolean firstTime = true;
+
+                @Override
+                public boolean handle(File path)
+                {
+                    if (firstTime)
+                    {
+                        firstTime = false;
+                        return false;
+                    }
+                    return super.handle(path);
+                }
+            };
+        someFile.createNewFile();
+        assert someFile.exists();
+        someFile.deleteOnExit();
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(workingDirectory, ALWAYS_TRUE_FILE_FILTER, myPathHandler);
+
+        // See whether faulty_paths settings works.
+        scanner.run();
+        assertEquals(0, mockPathHandler.handledPaths.size());
+        List<String> faulty = CollectionIO.readList(faultyPaths);
+        assertEquals(1, faulty.size());
+        assertEquals(someFile.getPath(), faulty.get(0));
+        // See whether fault_paths resetting works.
+        assert faultyPaths.delete();
+        myPathHandler.clear(); // Isn't necessary, just for expressing intention.
+        // See whether faulty_paths settings works.
+        scanner.run();
+        assertEquals(1, myPathHandler.handledPaths.size());
+        assertEquals(someFile, myPathHandler.handledPaths.get(0));
+    }
+
+    @Test
+    public void testPathOrder() throws IOException
+    {
+        final File dir = new File(workingDirectory, "testPathOrder");
+        final File f1 = new File(dir, "1");
+        final File f2 = new File(dir, "2");
+        final File f3 = new File(dir, "3");
+        final File f4 = new File(dir, "4");
+        final long now = System.currentTimeMillis();
+        dir.mkdir();
+        dir.deleteOnExit();
+        f1.createNewFile();
+        f1.deleteOnExit();
+        f2.createNewFile();
+        f2.deleteOnExit();
+        f3.createNewFile();
+        f3.deleteOnExit();
+        f4.createNewFile();
+        f4.deleteOnExit();
+        // Order should be: 2, 4, 3, 1
+        f2.setLastModified(now - 10000);
+        f4.setLastModified(now - 5000);
+        f3.setLastModified(now - 1000);
+        f1.setLastModified(now);
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(dir, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        scanner.run();
+        assertEquals(f2, mockPathHandler.handledPaths.get(0));
+        assertEquals(f4, mockPathHandler.handledPaths.get(1));
+        assertEquals(f3, mockPathHandler.handledPaths.get(2));
+        assertEquals(f1, mockPathHandler.handledPaths.get(3));
+        assertEquals(4, mockPathHandler.handledPaths.size());
+    }
+
+    @Test
+    public void testMissingDirectory()
+    {
+        final File dir = new File(workingDirectory, "testMissingDirectory");
+        dir.mkdir();
+        LogMonitoringAppender appender =
+                LogMonitoringAppender.addAppender(LogCategory.NOTIFY, "Failed to get listing of directory");
+        // The directory needs to exist when the scanner is created, otherwise the self-test will fail.
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(dir, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        dir.delete();
+        assert dir.exists() == false;
+        scanner.run();
+        assert appender.hasLogHappened();
+        LogMonitoringAppender.removeAppender(appender);
+    }
+
+    @Test
+    public void testDirectoryIsFile() throws IOException
+    {
+        final File dir = new File(workingDirectory, "testMissingDirectory");
+        dir.mkdir();
+        LogMonitoringAppender appender =
+                LogMonitoringAppender.addAppender(LogCategory.NOTIFY, "Failed to get listing of directory");
+        // The directory needs to exist when the scanner is created, otherwise the self-test will fail.
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(dir, ALWAYS_TRUE_FILE_FILTER, mockPathHandler);
+        dir.delete();
+        dir.createNewFile();
+        dir.deleteOnExit();
+        assert dir.isFile();
+        scanner.run();
+        assert appender.hasLogHappened();
+        dir.delete();
+        LogMonitoringAppender.removeAppender(appender);
+    }
+
+    @Test
+    public void testFailingFileFilter() throws IOException
+    {
+        final File dir = new File(workingDirectory, "testMissingDirectory");
+        dir.mkdir();
+        dir.deleteOnExit();
+        final File file = new File(dir, "some.file");
+        file.createNewFile();
+        file.deleteOnExit();
+        LogMonitoringAppender appender1 =
+                LogMonitoringAppender.addAppender(LogCategory.NOTIFY, "Failed to get listing of directory");
+        LogMonitoringAppender appender2 =
+                LogMonitoringAppender.addAppender(LogCategory.NOTIFY, EXCEPTION_THROWING_FILE_FILTER_MESSAGE);
+        // The directory needs to exist when the scanner is created, otherwise the self-test will fail.
+        final DirectoryScanningTimerTask scanner =
+                new DirectoryScanningTimerTask(dir, EXCEPTION_THROWING_FILE_FILTER, mockPathHandler);
+        scanner.run();
+        assert appender1.hasLogHappened();
+        assert appender2.hasLogHappened();
+        file.delete();
+        dir.delete();
+        LogMonitoringAppender.removeAppender(appender1);
+        LogMonitoringAppender.removeAppender(appender2);
+    }
+
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/NamePrefixFileFilterTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/NamePrefixFileFilterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..453d5e3a2b6c247e1aed4e99656cad24dd57ff7f
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/NamePrefixFileFilterTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007 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.FileFilter;
+
+import org.testng.annotations.Test;
+
+/**
+ * Test cases for {@link NamePrefixFileFilter}.
+ *
+ * @author Bernd Rinn
+ */
+public class NamePrefixFileFilterTest
+{
+    
+    private static final String PREFIX = "PReFiX";
+    
+    @Test
+    public void testNamePrefixShouldOccurAndFound()
+    {
+        final FileFilter filter = new NamePrefixFileFilter(PREFIX, true);
+        assert filter.accept(new File(PREFIX + "andSomePostFix.txt").getAbsoluteFile());
+    }
+
+    @Test
+    public void testNamePrefixShouldOccurButNotFound()
+    {
+        final FileFilter filter = new NamePrefixFileFilter(PREFIX, true);
+        assert filter.accept(new File("somefilewithouttheprefix.dat").getAbsoluteFile()) == false;
+    }
+
+    @Test
+    public void testNamePrefixShouldNotOccurAndNotFound()
+    {
+        final FileFilter filter = new NamePrefixFileFilter(PREFIX, false);
+        assert filter.accept(new File("somefilewithouttheprefix.dat").getAbsoluteFile());
+    }
+
+    @Test
+    public void testNamePrefixShouldNotOccurButFound()
+    {
+        final FileFilter filter = new NamePrefixFileFilter(PREFIX, false);
+        assert filter.accept(new File(PREFIX + "andSomePostFix.txt").getAbsoluteFile()) == false;
+    }
+
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/StoringUncaughtExceptionHandler.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/StoringUncaughtExceptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..a274fe50fbcef43e449acdd369c6264c367759f2
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/StoringUncaughtExceptionHandler.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2007 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.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * An exception handler that stores the first occurring exception for later investigation. Needs to be activated by
+ * 
+ * <pre>
+ * StoringUncaughtExceptionHandler exceptionHandler = new StoringUncaughtExceptionHandler();
+ * Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
+ * </pre>
+ * 
+ * @author Bernd Rinn
+ */
+public class StoringUncaughtExceptionHandler implements UncaughtExceptionHandler
+{
+
+    private Throwable throwable;
+
+    private String threadName;
+
+    public void uncaughtException(Thread t, Throwable e)
+    {
+        if (throwable == null) // Only store the first throwable
+        {
+            throwable = e;
+            threadName = t.getName();
+        }
+    }
+
+    /** Resets the handler. Any stored exception will be lost. */
+    public void reset()
+    {
+        throwable = null;
+        threadName = null;
+    }
+
+    /**
+     * @return <code>true</code> if an exception or error has occurred, <code>false</code> otherwise.
+     */
+    public boolean hasExceptionOccurred()
+    {
+        return (throwable != null);
+    }
+
+    /**
+     * @return The throwable, if any has been occurred, or <code>null</code> otherwise.
+     */
+    public Throwable getThrowable()
+    {
+        return throwable;
+    }
+
+    /**
+     * @return The name of the thread where the exception or error has occurred, or <code>null</code>, if no
+     *         exception or error has occurred.
+     */
+    public String getThreadName()
+    {
+        return threadName;
+    }
+
+    /**
+     * Checks whether an exception or error has occurred and, if yes, throws a new {@link RuntimeException} with the
+     * caught exception as cause in the current thread.
+     */
+    public void checkAndRethrowException()
+    {
+        if (hasExceptionOccurred())
+        {
+            throw new RuntimeException(String.format("An exception occurred in thread %s.", getThreadName()),
+                    getThrowable());
+        }
+    }
+
+}
\ No newline at end of file