From 89dee3ca7fa6fa17d97549e9f2b5cf90680fd149 Mon Sep 17 00:00:00 2001
From: brinn <brinn>
Date: Wed, 2 Jul 2008 14:39:12 +0000
Subject: [PATCH] refactor: distinguish between copiers that perform immutable
 copies of files and copiers that perform immutable copies of directories (as
 a particular implementation may not be good for both) add: methods for
 checking readable access to files or directories in FileUtilities

SVN: 7009
---
 .../bds/storage/filesystem/Directory.java     |  8 +-
 .../storage/filesystem/LinkMakerProvider.java | 12 +--
 .../cisd/common/utilities/FileUtilities.java  | 94 +++++++++++++++++--
 .../utilities/IDirectoryImmutableCopier.java  | 53 +++++++++++
 .../utilities/IFileImmutableCopier.java       | 51 ++++++++++
 .../utilities/IPathImmutableCopier.java       | 18 +---
 .../common/utilities/PathPrefixPrepender.java |  2 +-
 .../utilities/RecursiveHardLinkMaker.java     | 42 ++++++---
 .../common/utilities/FileUtilitiesTest.java   |  6 +-
 .../utilities/RecursiveHardLinkMakerTest.java |  9 +-
 .../cisd/datamover/LocalProcessor.java        | 10 +-
 .../filesystem/FileSysOperationsFactory.java  | 20 ++--
 .../filesystem/RetryingPathMover.java         |  2 +-
 .../intf/IFileSysOperationsFactory.java       |  4 +-
 .../filesystem/store/FileStoreLocal.java      |  2 +-
 15 files changed, 261 insertions(+), 72 deletions(-)
 create mode 100644 common/source/java/ch/systemsx/cisd/common/utilities/IDirectoryImmutableCopier.java
 create mode 100644 common/source/java/ch/systemsx/cisd/common/utilities/IFileImmutableCopier.java

diff --git a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java
index fb8fa52b7df..ea47436d128 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java
@@ -158,9 +158,9 @@ final class Directory extends AbstractNode implements IDirectory
         assert node != null : "Node can not be null.";
         assert name != null : "Name can not be null.";
         final java.io.File file = getNodeFile(node);
-        final java.io.File fileLink =
-                LinkMakerProvider.getLinkMaker().tryImmutableCopy(file, nodeFile, name);
-        if (fileLink != null)
+        final boolean ok =
+                LinkMakerProvider.getLinkMaker().copyFileImmutably(file, nodeFile, name);
+        if (ok)
         {
             final Link link = (Link) NodeFactory.createLinkNode(name, file);
             link.setParent(this);
@@ -237,6 +237,6 @@ final class Directory extends AbstractNode implements IDirectory
     @Override
     public final boolean isValid()
     {
-        return super.isValid() && FileUtilities.tryCheckDirectoryFullyAccessible(nodeFile, "") == null;
+        return super.isValid() && FileUtilities.checkDirectoryFullyAccessible(nodeFile, "") == null;
     }
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/LinkMakerProvider.java b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/LinkMakerProvider.java
index 4ce7a2b7bbb..40f2f3263f9 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/LinkMakerProvider.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/LinkMakerProvider.java
@@ -18,11 +18,11 @@ package ch.systemsx.cisd.bds.storage.filesystem;
 
 import ch.systemsx.cisd.common.Constants;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
-import ch.systemsx.cisd.common.utilities.IPathImmutableCopier;
+import ch.systemsx.cisd.common.utilities.IFileImmutableCopier;
 import ch.systemsx.cisd.common.utilities.RecursiveHardLinkMaker;
 
 /**
- * A provider of {@link IPathImmutableCopier} implementations.
+ * A provider of {@link IFileImmutableCopier} implementations.
  * 
  * @author Christian Ribeaud
  */
@@ -33,16 +33,16 @@ public final class LinkMakerProvider
 
     private static final int MAX_COPY_RETRIES = 7;
 
-    private static IPathImmutableCopier hardLinkMaker;
+    private static IFileImmutableCopier hardLinkMaker;
 
     private LinkMakerProvider()
     {
         // This class can not be instantiated.
     }
 
-    private final static IPathImmutableCopier tryCreateHardLinkMaker()
+    private final static IFileImmutableCopier tryCreateHardLinkMaker()
     {
-        final IPathImmutableCopier copier =
+        final IFileImmutableCopier copier =
                 RecursiveHardLinkMaker.tryCreateRetrying(Constants.MILLIS_TO_WAIT_BEFORE_TIMEOUT,
                         MAX_COPY_RETRIES, Constants.MILLIS_TO_SLEEP_BEFORE_RETRYING);
         if (copier != null)
@@ -58,7 +58,7 @@ public final class LinkMakerProvider
      * Returns an <code>IPathImmutableCopier</code> implementation which makes <i>hard links</i>
      * using the underlying <i>operating system</i>.
      */
-    public final static IPathImmutableCopier getLinkMaker()
+    public final static IFileImmutableCopier getLinkMaker()
     {
         if (hardLinkMaker == null)
         {
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/FileUtilities.java b/common/source/java/ch/systemsx/cisd/common/utilities/FileUtilities.java
index b3583c537f9..708adfee728 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/FileUtilities.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/FileUtilities.java
@@ -378,7 +378,45 @@ public final class FileUtilities
         assert path != null;
         assert kindOfPath != null;
 
-        return checkPathFullyAccessible(path, kindOfPath, "path");
+        return checkPathAccessible(path, kindOfPath, "path", true);
+    }
+
+    /**
+     * Checks whether a <var>path</var> of some <var>kind</var> is accessible for reading to the
+     * program.
+     * 
+     * @param kindOfPath description of given <var>path</var>. Mainly used for error messages.
+     * @return <code>null</code> if the <var>directory</var> is fully accessible and an error
+     *         message describing the problem with the <var>directory</var> otherwise.
+     */
+    public static String checkPathReadAccessible(final File path, final String kindOfPath)
+    {
+        assert path != null;
+        assert kindOfPath != null;
+
+        return checkPathAccessible(path, kindOfPath, "path", false);
+    }
+
+    /**
+     * Checks whether a <var>directory</var> of some <var>kind</var> is accessible for reading to
+     * the program (it's a directory, you can read and write in it)
+     * 
+     * @return <code>null</code> if the <var>directory</var> is accessible for reading and an
+     *         error message describing the problem with the <var>directory</var> otherwise.
+     */
+    public static String checkDirectoryReadAccessible(final File directory,
+            final String kindOfDirectory)
+    {
+        assert directory != null;
+        assert kindOfDirectory != null;
+
+        final String msg = checkPathAccessible(directory, kindOfDirectory, "directory", false);
+        if (msg == null && directory.isDirectory() == false)
+        {
+            return String.format("Path '%s' is supposed to be a %s directory but isn't.",
+                    directory.getPath(), kindOfDirectory);
+        }
+        return msg;
     }
 
     /**
@@ -388,23 +426,65 @@ public final class FileUtilities
      * @return <code>null</code> if the <var>directory</var> is fully accessible and an error
      *         message describing the problem with the <var>directory</var> otherwise.
      */
-    public static String tryCheckDirectoryFullyAccessible(final File directory,
+    public static String checkDirectoryFullyAccessible(final File directory,
             final String kindOfDirectory)
     {
         assert directory != null;
         assert kindOfDirectory != null;
 
-        final String msg = checkPathFullyAccessible(directory, kindOfDirectory, "directory");
+        final String msg = checkPathAccessible(directory, kindOfDirectory, "directory", true);
         if (msg == null && directory.isDirectory() == false)
         {
-            return String.format("Path '%s' is supposed to be a %s directory, but is a file.",
+            return String.format("Path '%s' is supposed to be a %s directory but isn't.",
                     directory.getPath(), kindOfDirectory);
         }
         return msg;
     }
 
-    private static String checkPathFullyAccessible(final File path, final String kindOfPath,
-            final String directoryOrFile)
+    /**
+     * Checks whether a <var>file</var> of some <var>kindOfFile</var> is accessible for reading to
+     * the program (so it's a file and you can read it)
+     * 
+     * @return <code>null</code> if the <var>file</var> is accessible to reading and an error
+     *         message describing the problem with the <var>file</var> otherwise.
+     */
+    public static String checkFileReadAccessible(final File file, final String kindOfFile)
+    {
+        assert file != null;
+        assert kindOfFile != null;
+
+        final String msg = checkPathAccessible(file, kindOfFile, "directory", false);
+        if (msg == null && file.isFile() == false)
+        {
+            return String.format("Path '%s' is supposed to be a %s file but isn't.",
+                    file.getPath(), kindOfFile);
+        }
+        return msg;
+    }
+
+    /**
+     * Checks whether a <var>file</var> of some <var>kindOfFile</var> is accessible for reading
+     * and writing to the program (so it's a file and you can read and write it)
+     * 
+     * @return <code>null</code> if the <var>file</var> is fully accessible and an error message
+     *         describing the problem with the <var>file</var> otherwise.
+     */
+    public static String checkFileFullyAccessible(final File file, final String kindOfFile)
+    {
+        assert file != null;
+        assert kindOfFile != null;
+
+        final String msg = checkPathAccessible(file, kindOfFile, "file", true);
+        if (msg == null && file.isFile() == false)
+        {
+            return String.format("Path '%s' is supposed to be a %s file but isn't.",
+                    file.getPath(), kindOfFile);
+        }
+        return msg;
+    }
+
+    private static String checkPathAccessible(final File path, final String kindOfPath,
+            final String directoryOrFile, boolean readAndWrite)
     {
         assert path != null;
         assert kindOfPath != null;
@@ -422,7 +502,7 @@ public final class FileUtilities
                         .capitalize(kindOfPath), directoryOrFile, path.getPath());
             }
         }
-        if (path.canWrite() == false)
+        if (readAndWrite && path.canWrite() == false)
         {
             return String.format("%s directory '%s' is not writable.", StringUtilities
                     .capitalize(kindOfPath), path.getPath());
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IDirectoryImmutableCopier.java b/common/source/java/ch/systemsx/cisd/common/utilities/IDirectoryImmutableCopier.java
new file mode 100644
index 00000000000..96ce8ccee7b
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/IDirectoryImmutableCopier.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+/**
+ * A role which can perform an immutable copy of a direcoty. <i>Immutable</i> here means, that none
+ * of the copied file must be changed or else the corresponding original file may be changed, tool
+ * 
+ * @author Bernd Rinn
+ */
+public interface IDirectoryImmutableCopier
+{
+    /**
+     * Creates a copy of the directory <code>file</code> (which may be a file or a directory) in
+     * <code>destinationDirectory</code>, which must not be modified later.
+     * <p>
+     * Note that this method don't do any checks about whether paths are directories and whether
+     * they exist or not. Use methods like
+     * {@link FileUtilities#checkDirectoryFullyAccessible(File, String)} for checking prior to
+     * calling this method where appropriate.
+     * </p>
+     * <p>
+     * <i>Can use hard links if available.</i>
+     * </p>
+     * 
+     * @param sourceDirectory The source directory. Really has to be a directory. Can not be
+     *            <code>null</code> and needs to exists.
+     * @param destinationDirectory The directory to copy <var>sourceDirectory</var> to. Can not be
+     *            <code>null</code> and must be an existing directory.
+     * @param targetNameOrNull The target name of the source directory. If <code>null</code>, the
+     *            name of the source directory itself is used.
+     * @return <code>true</code> if the operation was successful, <code>false</code> otherwise.
+     */
+    boolean copyDirectoryImmutably(final File sourceDirectory, final File destinationDirectory,
+            String targetNameOrNull);
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IFileImmutableCopier.java b/common/source/java/ch/systemsx/cisd/common/utilities/IFileImmutableCopier.java
new file mode 100644
index 00000000000..6a20d6ca36c
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/IFileImmutableCopier.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * A role which can perform an immutable copy of a file. <i>Immutable</i> here means, that the
+ * copied file must not be changed or else the original file may be changed, too.
+ * 
+ * @author Bernd Rinn
+ */
+public interface IFileImmutableCopier
+{
+    /**
+     * Creates a copy of the file <code>file</code> (which may be a file or a directory) in
+     * <code>destinationDirectory</code>, which must not be modified later.
+     * <p>
+     * Note that this method don't do any checks about whether paths are files and whether
+     * they exist or not. Use methods like
+     * {@link FileUtilities#checkPathFullyAccessible(File, String)} for checking prior to
+     * calling this method where appropriate.
+     * </p>
+     * <p>
+     * <i>Can use hard links if available.</i>
+     * </p>
+     * 
+     * @param file The source file. This really has to be a file. Can not be <code>null</code>.
+     * @param destinationDirectory The directory where given <var>path</var> should be copied. Can
+     *            not be <code>null</code> and must be an existing directory.
+     * @param nameOrNull The link name in the destination directory. If it is <code>null</code>,
+     *            the name of <var>file</var> will be used instead.
+     * @return <code>true</code>, if the file was copied successfully, <code>false</code> otherwise.
+     */
+    boolean copyFileImmutably(final File file, final File destinationDirectory,
+            final String nameOrNull);
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/IPathImmutableCopier.java b/common/source/java/ch/systemsx/cisd/common/utilities/IPathImmutableCopier.java
index 699d15d8231..89f881dfa50 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/IPathImmutableCopier.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/IPathImmutableCopier.java
@@ -16,27 +16,11 @@
 
 package ch.systemsx.cisd.common.utilities;
 
-import java.io.File;
-
 /**
  * Utility to create copies of files/resources, which should not be modified later.
  * 
  * @author Tomasz Pylak
  */
-public interface IPathImmutableCopier
+public interface IPathImmutableCopier extends IFileImmutableCopier, IDirectoryImmutableCopier
 {
-    /**
-     * Creates a copy of <code>path</code> (which may be a file or a directory) in
-     * <code>destinationDirectory</code>, which must not be modified later.
-     * <p>
-     * <i>Can use hard links if available.</i>
-     * </p>
-     * 
-     * @param path the source path. Can be a file or a directory. Can not be <code>null</code>.
-     * @param destinationDirectory the directory where given <var>path</var> should be copied. Can
-     *            not be <code>null</code> and must be an existing directory.
-     * @param nameOrNull the link name in the destination directory.
-     * @return the new path created, or <code>null</code> if the operation fails.
-     */
-    File tryImmutableCopy(final File path, final File destinationDirectory, final String nameOrNull);
 }
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/PathPrefixPrepender.java b/common/source/java/ch/systemsx/cisd/common/utilities/PathPrefixPrepender.java
index adc20b81167..8a358e59b31 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/PathPrefixPrepender.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/PathPrefixPrepender.java
@@ -79,7 +79,7 @@ public final class PathPrefixPrepender
         {
             final File file = new File(prefix);
             final String response =
-                    FileUtilities.tryCheckDirectoryFullyAccessible(file, type + " prefix path");
+                    FileUtilities.checkDirectoryFullyAccessible(file, type + " prefix path");
             if (response != null)
             {
                 throw new ConfigurationFailureException(response);
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMaker.java b/common/source/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMaker.java
index 411b98372f1..1144cbdc8fa 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMaker.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMaker.java
@@ -38,10 +38,11 @@ import ch.systemsx.cisd.common.process.ProcessExecutionHelper;
  * 
  * @author Tomasz Pylak
  */
-public final class RecursiveHardLinkMaker implements IPathImmutableCopier
+public final class RecursiveHardLinkMaker implements IPathImmutableCopier,
+        IDirectoryImmutableCopier, IFileImmutableCopier
 {
     private static final String HARD_LINK_EXEC = "ln";
-    
+
     private static final Logger operationLog =
             LogFactory.getLogger(LogCategory.OPERATION, RecursiveHardLinkMaker.class);
 
@@ -101,7 +102,7 @@ public final class RecursiveHardLinkMaker implements IPathImmutableCopier
     //
     // Factory methods
     //
-    
+
     /**
      * Creates copier which won't retry an operation if it fails.
      * 
@@ -112,10 +113,10 @@ public final class RecursiveHardLinkMaker implements IPathImmutableCopier
         return new RecursiveHardLinkMaker(linkExecPath, null);
     }
 
-    /** 
+    /**
      * Creates copier trying to find the path to the <code>ln</code> executable.
      * 
-     * @return <code>null</code> if the <code>ln</code> executable was not found. 
+     * @return <code>null</code> if the <code>ln</code> executable was not found.
      */
     public static final IPathImmutableCopier tryCreate()
     {
@@ -155,10 +156,6 @@ public final class RecursiveHardLinkMaker implements IPathImmutableCopier
         return new RecursiveHardLinkMaker(lnExec.getAbsolutePath(), singleFileLinkTimeoutOrNull);
     }
 
-    //
-    // IPathImmutableCopier
-    //
-    
     /**
      * Copies <var>path</var> (file or directory) to <var>destinationDirectory</var> by
      * duplicating directory structure and creating hard link for each file.
@@ -166,12 +163,12 @@ public final class RecursiveHardLinkMaker implements IPathImmutableCopier
      * <i>Note that <var>nameOrNull</var> cannot already exist in given <var>destinationDirectory</var>.</i>
      * </p>
      */
-    public final File tryImmutableCopy(final File path, final File destinationDirectory,
+    private final File tryImmutableCopy(final File path, final File destinationDirectory,
             final String nameOrNull)
     {
         assert path != null : "Given path can not be null.";
         assert destinationDirectory != null && destinationDirectory.isDirectory() : "Given destination directory can not be null and must be a directory.";
-        final String destName = nameOrNull == null ? path.getName() : nameOrNull;
+        final String destName = (nameOrNull == null) ? path.getName() : nameOrNull;
         final File destFile = new File(destinationDirectory, destName);
         if (destFile.exists())
         {
@@ -188,6 +185,29 @@ public final class RecursiveHardLinkMaker implements IPathImmutableCopier
         return tryMakeCopy(path, destinationDirectory, nameOrNull);
     }
 
+    //
+    // IDirectoryImmutableCopier
+    //
+
+    public boolean copyDirectoryImmutably(File sourceDirectory, File destinationDirectory,
+            String targetNameOrNull)
+    {
+        assert sourceDirectory != null && sourceDirectory.isDirectory();
+        assert destinationDirectory != null && destinationDirectory.isDirectory();
+        return (tryImmutableCopy(sourceDirectory, destinationDirectory, targetNameOrNull) != null);
+    }
+
+    //
+    // IFileImmutableCopier
+    //
+
+    public boolean copyFileImmutably(File file, File destinationDirectory, String nameOrNull)
+    {
+        assert file != null && file.isFile();
+        assert destinationDirectory != null && destinationDirectory.isDirectory();
+        return (tryImmutableCopy(file, destinationDirectory, nameOrNull) != null);
+    }
+
     private final File tryMakeCopy(final File resource, final File destinationDirectory,
             final String nameOrNull)
     {
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/FileUtilitiesTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/FileUtilitiesTest.java
index fc7a020daa9..f83938daf24 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/FileUtilitiesTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/FileUtilitiesTest.java
@@ -53,7 +53,7 @@ public final class FileUtilitiesTest extends AbstractFileSystemTestCase
     {
         final File nonExistentFile = new File(workingDirectory, "non-existent");
         nonExistentFile.delete();
-        String errorMsg = FileUtilities.tryCheckDirectoryFullyAccessible(nonExistentFile, "test");
+        String errorMsg = FileUtilities.checkDirectoryFullyAccessible(nonExistentFile, "test");
         assertNotNull(errorMsg);
     }
 
@@ -64,7 +64,7 @@ public final class FileUtilitiesTest extends AbstractFileSystemTestCase
         file.delete();
         file.deleteOnExit();
         file.createNewFile();
-        String errorMsg = FileUtilities.tryCheckDirectoryFullyAccessible(file, "test");
+        String errorMsg = FileUtilities.checkDirectoryFullyAccessible(file, "test");
         assertNotNull(errorMsg);
     }
 
@@ -78,7 +78,7 @@ public final class FileUtilitiesTest extends AbstractFileSystemTestCase
         readOnlyDirectory.deleteOnExit();
         assert readOnlyDirectory.setReadOnly();
 
-        String errorMsg = FileUtilities.tryCheckDirectoryFullyAccessible(readOnlyDirectory, "test");
+        String errorMsg = FileUtilities.checkDirectoryFullyAccessible(readOnlyDirectory, "test");
 
         // --- clean before checking results
         // Unfortunately, with JDK 5 there is no portable way to set a file or directory read/write,
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMakerTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMakerTest.java
index 0aea53f1c30..d41290fe741 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMakerTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/RecursiveHardLinkMakerTest.java
@@ -16,7 +16,7 @@
 
 package ch.systemsx.cisd.common.utilities;
 
-import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.*;
 
 import java.io.File;
 import java.io.IOException;
@@ -143,8 +143,8 @@ public class RecursiveHardLinkMakerTest
     {
         File inputDir = createDirectory(workingDirectory, "resource-to-copy");
         createStructure(inputDir);
-        File newInput = createHardLinkCopier().tryImmutableCopy(inputDir, outputDir, null);
-        assert newInput != null;
+        assertTrue(createHardLinkCopier().copyDirectoryImmutably(inputDir, outputDir, null));
+        File newInput = new File(outputDir, inputDir.getName());
 
         assertStructureExists(newInput);
         boolean deleted = FileUtilities.deleteRecursively(inputDir);
@@ -170,7 +170,8 @@ public class RecursiveHardLinkMakerTest
         File src = createFile(workingDirectory, "fileXXX");
         assertFileExists(src);
 
-        File dest = createHardLinkCopier().tryImmutableCopy(src, outputDir, null);
+        assertTrue(createHardLinkCopier().copyFileImmutably(src, outputDir, null));
+        File dest = new File(outputDir, src.getName());
         assertFileExists(dest);
 
         modifyDest(dest);
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java b/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java
index e12f5ef7515..bc6c9a27a51 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/LocalProcessor.java
@@ -31,8 +31,8 @@ import ch.systemsx.cisd.common.logging.Log4jSimpleLogger;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.common.utilities.FileUtilities;
+import ch.systemsx.cisd.common.utilities.IDirectoryImmutableCopier;
 import ch.systemsx.cisd.common.utilities.IPathHandler;
-import ch.systemsx.cisd.common.utilities.IPathImmutableCopier;
 import ch.systemsx.cisd.datamover.filesystem.intf.IPathMover;
 import ch.systemsx.cisd.datamover.filesystem.intf.IRecoverableTimerTaskFactory;
 import ch.systemsx.cisd.datamover.utils.LocalBufferDirs;
@@ -56,7 +56,7 @@ public final class LocalProcessor implements IPathHandler, IRecoverableTimerTask
     private static final Logger notificationLog =
             LogFactory.getLogger(LogCategory.NOTIFY, LocalProcessor.class);
 
-    private final IPathImmutableCopier copier;
+    private final IDirectoryImmutableCopier copier;
 
     private final IPathMover mover;
 
@@ -81,7 +81,7 @@ public final class LocalProcessor implements IPathHandler, IRecoverableTimerTask
     private final File manualInterventionDir;
 
     LocalProcessor(final Parameters parameters, final LocalBufferDirs bufferDirs,
-            final IPathImmutableCopier copier, final IPathMover mover)
+            final IDirectoryImmutableCopier copier, final IPathMover mover)
     {
         this.inputDir = bufferDirs.getCopyCompleteDir();
         this.outputDir = bufferDirs.getReadyToMoveDir();
@@ -272,8 +272,8 @@ public final class LocalProcessor implements IPathHandler, IRecoverableTimerTask
                     return;
                 }
             }
-            extraTmpCopy = copier.tryImmutableCopy(path, tempDir, null);
-            if (extraTmpCopy == null)
+            final boolean ok = copier.copyDirectoryImmutably(path, tempDir, null);
+            if (ok == false)
             {
                 notificationLog.error(String.format("Creating extra copy of '%s' failed.", path));
                 return;
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java
index d78427c5a84..9f502bb6270 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/FileSysOperationsFactory.java
@@ -27,7 +27,7 @@ import ch.systemsx.cisd.common.exceptions.Status;
 import ch.systemsx.cisd.common.exceptions.StatusFlag;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
-import ch.systemsx.cisd.common.utilities.IPathImmutableCopier;
+import ch.systemsx.cisd.common.utilities.IDirectoryImmutableCopier;
 import ch.systemsx.cisd.common.utilities.OSUtilities;
 import ch.systemsx.cisd.common.utilities.RecursiveHardLinkMaker;
 import ch.systemsx.cisd.datamover.filesystem.intf.IFileSysOperationsFactory;
@@ -76,27 +76,27 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
         return executableFile;
     }
 
-    private final IPathImmutableCopier createFakedImmCopier()
+    private final IDirectoryImmutableCopier createFakedImmCopier()
     {
         final IPathCopier normalCopier = getCopier(false);
-        return new IPathImmutableCopier()
+        return new IDirectoryImmutableCopier()
             {
                 //
-                // IPathImmutableCopier
+                // IDirectoryImmutableCopier
                 //
 
-                public final File tryImmutableCopy(final File file, final File destinationDirectory,
-                        final String nameOrNull)
+                public final boolean copyDirectoryImmutably(final File file,
+                        final File destinationDirectory, String targetNameOrNull)
                 {
                     final Status status = normalCopier.copy(file, destinationDirectory);
                     if (StatusFlag.OK.equals(status.getFlag()))
                     {
-                        return new File(destinationDirectory, file.getName());
+                        return true;
                     } else
                     {
                         notificationLog.error(String.format("Copy of '%s' to '%s' failed: %s.",
                                 file.getPath(), destinationDirectory.getPath(), status));
-                        return null;
+                        return false;
                     }
                 }
             };
@@ -112,7 +112,7 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
                 Constants.MILLIS_TO_SLEEP_BEFORE_RETRYING);
     }
 
-    public final IPathImmutableCopier getImmutableCopier()
+    public final IDirectoryImmutableCopier getImmutableCopier()
     {
         final String lnExec = parameters.getHardLinkExecutable();
         if (lnExec != null)
@@ -120,7 +120,7 @@ public class FileSysOperationsFactory implements IFileSysOperationsFactory
             return RecursiveHardLinkMaker.create(lnExec);
         }
 
-        IPathImmutableCopier copier = null;
+        IDirectoryImmutableCopier copier = null;
         if (OSUtilities.isWindows() == false)
         {
             copier = RecursiveHardLinkMaker.tryCreate();
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java
index 5dc21f2c386..e01e32afe75 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/RetryingPathMover.java
@@ -94,7 +94,7 @@ class RetryingPathMover implements IPathMover
     private boolean checkDirectoryAccesible(final File destinationDirectory)
     {
         String errorMessage =
-                FileUtilities.tryCheckDirectoryFullyAccessible(destinationDirectory, "destination");
+                FileUtilities.checkDirectoryFullyAccessible(destinationDirectory, "destination");
         if (errorMessage != null)
         {
             operationLog.error("Unaccessible directory: " + errorMessage);
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java
index e1c7a5e9327..949666bc00c 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/intf/IFileSysOperationsFactory.java
@@ -17,7 +17,7 @@ package ch.systemsx.cisd.datamover.filesystem.intf;
 
 import java.io.File;
 
-import ch.systemsx.cisd.common.utilities.IPathImmutableCopier;
+import ch.systemsx.cisd.common.utilities.IDirectoryImmutableCopier;
 
 /**
  * A role that provides access to the roles which perform file system operations.
@@ -28,7 +28,7 @@ public interface IFileSysOperationsFactory
 {
     public IPathCopier getCopier(boolean requiresDeletionBeforeCreation);
 
-    public IPathImmutableCopier getImmutableCopier();
+    public IDirectoryImmutableCopier getImmutableCopier();
 
     public IPathRemover getRemover();
 
diff --git a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreLocal.java b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreLocal.java
index 3df9ba26dec..24f78d224b2 100644
--- a/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreLocal.java
+++ b/datamover/source/java/ch/systemsx/cisd/datamover/filesystem/store/FileStoreLocal.java
@@ -140,7 +140,7 @@ public class FileStoreLocal extends AbstractFileStore implements IExtendedFileSt
         } else
         {
             unaccesibleMsg =
-                    FileUtilities.tryCheckDirectoryFullyAccessible(getPath(), getDescription());
+                    FileUtilities.checkDirectoryFullyAccessible(getPath(), getDescription());
         }
         if (unaccesibleMsg != null)
         {
-- 
GitLab