From 7cd01773104a6e69ac2a6326d98a18e02990aef3 Mon Sep 17 00:00:00 2001 From: Arpit Agarwal Date: Wed, 27 Apr 2016 10:06:54 -0700 Subject: [PATCH] HADOOP-5470. RunJar.unJar() should write the last modified time found in the jar entry to the uncompressed file. (Contributed by Andras Bakor) --- .../java/org/apache/hadoop/util/RunJar.java | 12 ++++++++++ .../org/apache/hadoop/util/TestRunJar.java | 23 +++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java index ccb114baaef..52cf05c0477 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/RunJar.java @@ -43,12 +43,16 @@ import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** Run a Hadoop job jar. */ @InterfaceAudience.Private @InterfaceStability.Unstable public class RunJar { + private static final Logger LOG = LoggerFactory.getLogger(RunJar.class); + /** Pattern that matches any string */ public static final Pattern MATCH_ANY = Pattern.compile(".*"); @@ -93,6 +97,7 @@ public class RunJar { throws IOException { JarFile jar = new JarFile(jarFile); try { + int numOfFailedLastModifiedSet = 0; Enumeration entries = jar.entries(); while (entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); @@ -108,11 +113,18 @@ public class RunJar { } finally { out.close(); } + if (!file.setLastModified(entry.getTime())) { + numOfFailedLastModifiedSet++; + } } finally { in.close(); } } } + if (numOfFailedLastModifiedSet > 0) { + LOG.warn("Could not set last modfied time for {} file(s)", + numOfFailedLastModifiedSet); + } } finally { jar.close(); } diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java index f592d0400a4..a9275c8f906 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestRunJar.java @@ -41,6 +41,8 @@ public class TestRunJar extends TestCase { private static final String TEST_JAR_NAME="test-runjar.jar"; private static final String TEST_JAR_2_NAME = "test-runjar2.jar"; + private static final long MOCKED_NOW = 1_460_389_972_000L; + private static final long MOCKED_NOW_PLUS_TWO_SEC = MOCKED_NOW + 2_000; @Override @Before @@ -70,9 +72,13 @@ public class TestRunJar extends TestCase { File jarFile = new File(TEST_ROOT_DIR, TEST_JAR_NAME); JarOutputStream jstream = new JarOutputStream(new FileOutputStream(jarFile)); - jstream.putNextEntry(new ZipEntry("foobar.txt")); + ZipEntry zipEntry1 = new ZipEntry("foobar.txt"); + zipEntry1.setTime(MOCKED_NOW); + jstream.putNextEntry(zipEntry1); jstream.closeEntry(); - jstream.putNextEntry(new ZipEntry("foobaz.txt")); + ZipEntry zipEntry2 = new ZipEntry("foobaz.txt"); + zipEntry2.setTime(MOCKED_NOW_PLUS_TWO_SEC); + jstream.putNextEntry(zipEntry2); jstream.closeEntry(); jstream.close(); } @@ -115,6 +121,19 @@ public class TestRunJar extends TestCase { } + public void testUnJarDoesNotLooseLastModify() throws Exception { + File unjarDir = new File(TEST_ROOT_DIR, "unjar-lastmod"); + assertFalse("unjar dir shouldn't exist at test start", + new File(unjarDir, "foobar.txt").exists()); + + // Unjar everything + RunJar.unJar(new File(TEST_ROOT_DIR, TEST_JAR_NAME), + unjarDir); + + assertEquals("Last modify time was lost during unJar", MOCKED_NOW, new File(unjarDir, "foobar.txt").lastModified()); + assertEquals("Last modify time was lost during unJar", MOCKED_NOW_PLUS_TWO_SEC, new File(unjarDir, "foobaz.txt").lastModified()); + } + /** * Tests the client classloader to verify the main class and its dependent * class are loaded correctly by the application classloader, and others are