azkaban-aplcache

use java lib instead of external sh commands to do recursive

12/18/2017 7:12:17 PM

Details

diff --git a/azkaban-common/src/main/java/azkaban/utils/FileIOUtils.java b/azkaban-common/src/main/java/azkaban/utils/FileIOUtils.java
index 68d64f1..e9e6e9b 100644
--- a/azkaban-common/src/main/java/azkaban/utils/FileIOUtils.java
+++ b/azkaban-common/src/main/java/azkaban/utils/FileIOUtils.java
@@ -26,6 +26,8 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -86,10 +88,10 @@ public class FileIOUtils {
     }
   }
 
+
   /**
-   * Run a unix command that will hard link files and recurse into directories.
+   * Hard link files and recurse into directories.
    */
-
   public static void createDeepHardlink(final File sourceDir, final File destDir)
       throws IOException {
     if (!sourceDir.exists()) {
@@ -105,51 +107,18 @@ public class FileIOUtils {
     final Set<String> paths = new HashSet<>();
     createDirsFindFiles(sourceDir, sourceDir, destDir, paths);
 
-    final StringBuffer buffer = new StringBuffer();
     for (String path : paths) {
       final File sourceLink = new File(sourceDir, path);
-      path = "." + path;
-
-      buffer.append("ln ").append(sourceLink.getAbsolutePath()).append("/*")
-          .append(" ").append(path).append(";");
-    }
-
-    runShellCommand(buffer.toString(), destDir);
-  }
-
-  private static void runShellCommand(final String command, final File workingDir)
-      throws IOException {
-    final ProcessBuilder builder = new ProcessBuilder().command("sh", "-c", command);
-    builder.directory(workingDir);
+      path = destDir + path;
 
-    // XXX what about stopping threads ??
-    final Process process = builder.start();
-    try {
-      final NullLogger errorLogger = new NullLogger(process.getErrorStream());
-      final NullLogger inputLogger = new NullLogger(process.getInputStream());
-      errorLogger.start();
-      inputLogger.start();
-
-      try {
-        if (process.waitFor() < 0) {
-          // Assume that the error will be in standard out. Otherwise it'll be
-          // in standard in.
-          String errorMessage = errorLogger.getLastMessages();
-          if (errorMessage.isEmpty()) {
-            errorMessage = inputLogger.getLastMessages();
-          }
-
-          throw new IOException(errorMessage);
+      final File[] targetFiles = sourceLink.listFiles();
+      for (final File targetFile : targetFiles) {
+        if (targetFile.isFile()) {
+          final File linkFile = new File(path, targetFile.getName());
+          Files.createLink(linkFile.toPath(), Paths.get(targetFile.getAbsolutePath()));
         }
-      } catch (final InterruptedException e) {
-        logger.error(e);
       }
-    } finally {
-      IOUtils.closeQuietly(process.getInputStream());
-      IOUtils.closeQuietly(process.getOutputStream());
-      IOUtils.closeQuietly(process.getErrorStream());
     }
-
   }
 
   private static void createDirsFindFiles(final File baseDir, final File sourceDir,
diff --git a/azkaban-common/src/test/java/azkaban/utils/FileIOUtilsTest.java b/azkaban-common/src/test/java/azkaban/utils/FileIOUtilsTest.java
index 0b9b6c3..679fe33 100644
--- a/azkaban-common/src/test/java/azkaban/utils/FileIOUtilsTest.java
+++ b/azkaban-common/src/test/java/azkaban/utils/FileIOUtilsTest.java
@@ -16,13 +16,17 @@
 
 package azkaban.utils;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.comparator.NameFileComparator;
@@ -38,16 +42,46 @@ public class FileIOUtilsTest {
   public TemporaryFolder temp = new TemporaryFolder();
   private File sourceDir, destDir, baseDir;
 
+  /**
+   * Create a very big dir which would cause linux shell command to hard link the dir to
+   * exceed the allowed limit.
+   */
+  private void createBigDir(final String path) throws IOException {
+    final String verylongprefix =
+        "123123123123123123123123123123123113123111111111111111111111111111111111111111111111111111"
+            + "111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
+            + "11231312312312312312313121111111111111111111111111111111111111111111111111";
+
+    for (int i = 1; i <= 400; i++) {
+      final Path tmpDirPath = Paths.get(path, verylongprefix + "dir" + i);
+      Files.createDirectory(tmpDirPath);
+      for (int j = 1; j <= 100; j++) {
+        final Path tmp = Paths.get(tmpDirPath.toAbsolutePath().toString(), String.valueOf(j));
+        Files.createFile(tmp);
+      }
+    }
+  }
+
   @Before
   public void setUp() throws Exception {
     // setup base dir
+
     this.baseDir = this.temp.newFolder("base");
     final File file1 = new File(this.baseDir.getAbsolutePath() + "/a.out");
     final File file2 = new File(this.baseDir.getAbsolutePath() + "/testdir");
     final File file3 = new File(file2.getAbsolutePath() + "/b.out");
+    final File file4 = new File(file2.getAbsolutePath() + "/testdir");
+    final File file5 = new File(file2.getAbsolutePath() + "/c.out");
+    final File file6 = new File(file4.getAbsolutePath() + "/c1.out");
+    final File file7 = new File(file4.getAbsolutePath() + "/c2.out");
+
     file1.createNewFile();
     file2.mkdir();
     file3.createNewFile();
+    file4.mkdir();
+    file5.createNewFile();
+    file6.createNewFile();
+    file7.createNewFile();
 
     byte[] fileData = new byte[]{1, 2, 3};
     FileOutputStream out = new FileOutputStream(file1);
@@ -77,23 +111,28 @@ public class FileIOUtilsTest {
   @Test
   public void testHardlinkCopy() throws IOException {
     FileIOUtils.createDeepHardlink(this.sourceDir, this.destDir);
-    assertTrue(areDirsEqual(this.sourceDir, this.destDir, true));
+    assertThat(areDirsEqual(this.sourceDir, this.destDir, true)).isTrue();
     FileUtils.deleteDirectory(this.destDir);
-    assertTrue(areDirsEqual(this.baseDir, this.sourceDir, true));
+    assertThat(areDirsEqual(this.baseDir, this.sourceDir, true)).isTrue();
+  }
+
+  @Test
+  public void testHardlinkCopyOfBigDir() throws IOException {
+    final File bigDir = new File(this.baseDir.getAbsolutePath() + "/bigdir");
+    bigDir.mkdir();
+    createBigDir(bigDir.getAbsolutePath());
+
+    FileIOUtils.createDeepHardlink(bigDir, this.destDir);
+    assertThat(areDirsEqual(this.destDir, bigDir, true)).isTrue();
+    FileUtils.deleteDirectory(bigDir);
+
   }
 
   @Test
   public void testHardlinkCopyNonSource() {
-    boolean exception = false;
-    try {
+    assertThatThrownBy(() -> {
       FileIOUtils.createDeepHardlink(new File(this.sourceDir, "idonotexist"), this.destDir);
-    } catch (final IOException e) {
-      System.out.println(e.getMessage());
-      System.out.println("Handled this case nicely.");
-      exception = true;
-    }
-
-    assertTrue(exception);
+    }).isInstanceOf(IOException.class);
   }
 
   private boolean areDirsEqualUtil(final File file1, final File file2, final boolean isRoot,