You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mapreduce-commits@hadoop.apache.org by zs...@apache.org on 2010/06/25 03:06:27 UTC

svn commit: r957772 - in /hadoop/mapreduce/trunk: CHANGES.txt src/java/org/apache/hadoop/mapreduce/util/MRAsyncDiskService.java src/test/mapred/org/apache/hadoop/mapreduce/util/TestMRAsyncDiskService.java

Author: zshao
Date: Fri Jun 25 01:06:26 2010
New Revision: 957772

URL: http://svn.apache.org/viewvc?rev=957772&view=rev
Log:
MAPREDUCE-1887. MRAsyncDiskService now properly absolutizes volume root paths. (Aaron Kimball via zshao)

Modified:
    hadoop/mapreduce/trunk/CHANGES.txt
    hadoop/mapreduce/trunk/src/java/org/apache/hadoop/mapreduce/util/MRAsyncDiskService.java
    hadoop/mapreduce/trunk/src/test/mapred/org/apache/hadoop/mapreduce/util/TestMRAsyncDiskService.java

Modified: hadoop/mapreduce/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/mapreduce/trunk/CHANGES.txt?rev=957772&r1=957771&r2=957772&view=diff
==============================================================================
--- hadoop/mapreduce/trunk/CHANGES.txt (original)
+++ hadoop/mapreduce/trunk/CHANGES.txt Fri Jun 25 01:06:26 2010
@@ -117,6 +117,9 @@ Trunk (unreleased changes)
     MAPREDUCE-1857. Removes unused configuration parameters in streaming.
     (amareshwari)
 
+    MAPREDUCE-1887. MRAsyncDiskService now properly absolutizes volume root
+    paths. (Aaron Kimball via zshao)
+
 Release 0.21.0 - Unreleased
 
   INCOMPATIBLE CHANGES

Modified: hadoop/mapreduce/trunk/src/java/org/apache/hadoop/mapreduce/util/MRAsyncDiskService.java
URL: http://svn.apache.org/viewvc/hadoop/mapreduce/trunk/src/java/org/apache/hadoop/mapreduce/util/MRAsyncDiskService.java?rev=957772&r1=957771&r2=957772&view=diff
==============================================================================
--- hadoop/mapreduce/trunk/src/java/org/apache/hadoop/mapreduce/util/MRAsyncDiskService.java (original)
+++ hadoop/mapreduce/trunk/src/java/org/apache/hadoop/mapreduce/util/MRAsyncDiskService.java Fri Jun 25 01:06:26 2010
@@ -63,20 +63,24 @@ public class MRAsyncDiskService {
    * Create a AsyncDiskServices with a set of volumes (specified by their
    * root directories).
    * 
-   * The AsyncDiskServices uses one ThreadPool per volume to do the async
-   * disk operations.
+   * The AsyncDiskServices uses one ThreadPool per volume to do the async disk
+   * operations.
    * 
    * @param localFileSystem The localFileSystem used for deletions.
-   * @param volumes The roots of the file system volumes.
+   * @param nonCanonicalVols The roots of the file system volumes, which may
+   * be absolte paths, or paths relative to the ${user.dir} system property
+   * ("cwd").
    */
-  public MRAsyncDiskService(FileSystem localFileSystem, String... volumes)
-      throws IOException {
+  public MRAsyncDiskService(FileSystem localFileSystem,
+      String... nonCanonicalVols) throws IOException {
     
-    this.volumes = new String[volumes.length];
-    for (int v = 0; v < volumes.length; v++) {
-      this.volumes[v] = normalizePath(volumes[v]);
-    }  
     this.localFileSystem = localFileSystem;
+    this.volumes = new String[nonCanonicalVols.length];
+    for (int v = 0; v < nonCanonicalVols.length; v++) {
+      this.volumes[v] = normalizePath(nonCanonicalVols[v]);
+      LOG.debug("Normalized volume: " + nonCanonicalVols[v]
+          + " -> " + this.volumes[v]);
+    }  
     
     asyncDiskService = new AsyncDiskService(this.volumes);
     
@@ -85,7 +89,8 @@ public class MRAsyncDiskService {
       // Create the root for file deletion
       Path absoluteSubdir = new Path(volumes[v], TOBEDELETED);
       if (!localFileSystem.mkdirs(absoluteSubdir)) {
-        throw new IOException("Cannot create " + TOBEDELETED + " in " + volumes[v]);
+        throw new IOException("Cannot create " + TOBEDELETED + " in "
+            + volumes[v]);
       }
     }
     
@@ -312,8 +317,9 @@ public class MRAsyncDiskService {
   /**
    * Returns the normalized path of a path.
    */
-  private static String normalizePath(String path) {
-    return (new Path(path)).toUri().getPath();
+  private String normalizePath(String path) {
+    return (new Path(path)).makeQualified(this.localFileSystem)
+        .toUri().getPath();
   }
   
   /**
@@ -322,7 +328,7 @@ public class MRAsyncDiskService {
    * @param volume Root of the volume.
    * @return null if the absolute path name is outside of the volume.
    */
-  private static String getRelativePathName(String absolutePathName,
+  private String getRelativePathName(String absolutePathName,
       String volume) {
     
     absolutePathName = normalizePath(absolutePathName);

Modified: hadoop/mapreduce/trunk/src/test/mapred/org/apache/hadoop/mapreduce/util/TestMRAsyncDiskService.java
URL: http://svn.apache.org/viewvc/hadoop/mapreduce/trunk/src/test/mapred/org/apache/hadoop/mapreduce/util/TestMRAsyncDiskService.java?rev=957772&r1=957771&r2=957772&view=diff
==============================================================================
--- hadoop/mapreduce/trunk/src/test/mapred/org/apache/hadoop/mapreduce/util/TestMRAsyncDiskService.java (original)
+++ hadoop/mapreduce/trunk/src/test/mapred/org/apache/hadoop/mapreduce/util/TestMRAsyncDiskService.java Fri Jun 25 01:06:26 2010
@@ -21,6 +21,10 @@ import java.io.File;
 import java.io.IOException;
 
 import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -31,11 +35,124 @@ import org.junit.Test;
  * A test for MRAsyncDiskService.
  */
 public class TestMRAsyncDiskService extends TestCase {
+
+  public static final Log LOG = LogFactory.getLog(TestMRAsyncDiskService.class);
   
   private static String TEST_ROOT_DIR = new Path(System.getProperty(
       "test.build.data", "/tmp")).toString();
 
   /**
+   * Given 'pathname', compute an equivalent path relative to the cwd.
+   * @param pathname the path to a directory.
+   * @return the path to that same directory, relative to ${user.dir}.
+   */
+  private String relativeToWorking(String pathname) {
+    String cwd = System.getProperty("user.dir", "/");
+
+    // normalize pathname and cwd into full directory paths.
+    pathname = (new Path(pathname)).toUri().getPath();
+    cwd = (new Path(cwd)).toUri().getPath();
+
+    String [] cwdParts = cwd.split(File.separator);
+    String [] pathParts = pathname.split(File.separator);
+
+    // There are three possible cases:
+    // 1) pathname and cwd are equal. Return '.'
+    // 2) pathname is under cwd. Return the components that are under it.
+    //     e.g., cwd = /a/b, path = /a/b/c, return 'c'
+    // 3) pathname is outside of cwd. Find the common components, if any,
+    //    and subtract them from the returned path, then return enough '..'
+    //    components to "undo" the non-common components of cwd, then all 
+    //    the remaining parts of pathname.
+    //    e.g., cwd = /a/b, path = /a/c, return '../c'
+
+    if (cwd.equals(pathname)) {
+      LOG.info("relative to working: " + pathname + " -> .");
+      return "."; // They match exactly.
+    }
+
+    // Determine how many path components are in common between cwd and path.
+    int common = 0;
+    for (int i = 0; i < Math.min(cwdParts.length, pathParts.length); i++) {
+      if (cwdParts[i].equals(pathParts[i])) {
+        common++;
+      } else {
+        break;
+      }
+    }
+
+    // output path stringbuilder.
+    StringBuilder sb = new StringBuilder();
+
+    // For everything in cwd that isn't in pathname, add a '..' to undo it.
+    int parentDirsRequired = cwdParts.length - common;
+    for (int i = 0; i < parentDirsRequired; i++) {
+      sb.append("..");
+      sb.append(File.separator);
+    }
+
+    // Then append all non-common parts of 'pathname' itself.
+    for (int i = common; i < pathParts.length; i++) {
+      sb.append(pathParts[i]);
+      sb.append(File.separator);
+    }
+
+    // Don't end with a '/'.
+    String s = sb.toString();
+    if (s.endsWith(File.separator)) {
+      s = s.substring(0, s.length() - 1);
+    }
+
+    LOG.info("relative to working: " + pathname + " -> " + s);
+    return s;
+  }
+
+  @Test
+  /** Test that the relativeToWorking() method above does what we expect. */
+  public void testRelativeToWorking() {
+    assertEquals(".", relativeToWorking(System.getProperty("user.dir", ".")));
+
+    String cwd = System.getProperty("user.dir", ".");
+    Path cwdPath = new Path(cwd);
+
+    Path subdir = new Path(cwdPath, "foo");
+    assertEquals("foo", relativeToWorking(subdir.toUri().getPath()));
+
+    Path subsubdir = new Path(subdir, "bar");
+    assertEquals("foo/bar", relativeToWorking(subsubdir.toUri().getPath()));
+
+    Path parent = new Path(cwdPath, "..");
+    assertEquals("..", relativeToWorking(parent.toUri().getPath()));
+
+    Path sideways = new Path(parent, "baz");
+    assertEquals("../baz", relativeToWorking(sideways.toUri().getPath()));
+  }
+
+
+  @Test
+  /** Test that volumes specified as relative paths are handled properly
+   * by MRAsyncDiskService (MAPREDUCE-1887).
+   */
+  public void testVolumeNormalization() throws Throwable {
+    LOG.info("TEST_ROOT_DIR is " + TEST_ROOT_DIR);
+
+    String relativeTestRoot = relativeToWorking(TEST_ROOT_DIR);
+
+    FileSystem localFileSystem = FileSystem.getLocal(new Configuration());
+    String [] vols = new String[] { relativeTestRoot + "/0",
+        relativeTestRoot + "/1" };
+
+    // Put a file in one of the volumes to be cleared on startup.
+    Path delDir = new Path(vols[0], MRAsyncDiskService.TOBEDELETED);
+    localFileSystem.mkdirs(delDir);
+    localFileSystem.create(new Path(delDir, "foo")).close();
+
+    MRAsyncDiskService service = new MRAsyncDiskService(
+        localFileSystem, vols);
+    makeSureCleanedUp(vols, service);
+  }
+
+  /**
    * This test creates some directories and then removes them through 
    * MRAsyncDiskService. 
    */