This is an automated email from the ASF dual-hosted git repository.

jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 4a49226  GEODE-5459: Convert the classpath into a manifest jar for 
ProcessWrapper (#2168)
4a49226 is described below

commit 4a4922691aefb21ae634ab7ba200a3533a074a40
Author: Jens Deppe <[email protected]>
AuthorDate: Fri Jul 20 10:37:43 2018 -0700

    GEODE-5459: Convert the classpath into a manifest jar for ProcessWrapper 
(#2168)
    
    - This avoids too long command lines in Windows
---
 ...precatedCacheServerLauncherIntegrationTest.java | 22 ++++---
 .../apache/geode/test/process/ProcessWrapper.java  | 75 ++++++++++++++++++++--
 2 files changed, 85 insertions(+), 12 deletions(-)

diff --git 
a/geode-core/src/integrationTest/java/org/apache/geode/internal/cache/DeprecatedCacheServerLauncherIntegrationTest.java
 
b/geode-core/src/integrationTest/java/org/apache/geode/internal/cache/DeprecatedCacheServerLauncherIntegrationTest.java
index 447a097..5410eb7 100755
--- 
a/geode-core/src/integrationTest/java/org/apache/geode/internal/cache/DeprecatedCacheServerLauncherIntegrationTest.java
+++ 
b/geode-core/src/integrationTest/java/org/apache/geode/internal/cache/DeprecatedCacheServerLauncherIntegrationTest.java
@@ -37,6 +37,7 @@ import java.rmi.RemoteException;
 import java.rmi.registry.LocateRegistry;
 import java.rmi.registry.Registry;
 import java.rmi.server.UnicastRemoteObject;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Properties;
 import java.util.concurrent.TimeoutException;
@@ -163,7 +164,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
         "-J-Xmx" + Runtime.getRuntime().maxMemory(), 
"-J-Dgemfire.use-cluster-configuration=false",
         "-J-Dgemfire.locators=\"\"", "log-file=" + this.logFileName,
         "cache-xml-file=" + this.cacheXmlFileName, "-dir=" + 
this.directoryPath,
-        "-classpath=" + this.classpath);
+        "-classpath=" + getManifestJarFromClasspath());
 
     execAndValidate("CacheServer pid: \\d+ status: running", "status",
         "-dir=" + this.directoryPath);
@@ -181,7 +182,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
         "cache-xml-file=" + this.cacheXmlFile.getAbsolutePath(),
         "log-file=" + this.logFile.getAbsolutePath(), 
"-J-Dgemfire.use-cluster-configuration=false",
         "-J-Dgemfire.locators=\"\"", "-server-port=" + this.serverPort,
-        "-dir=" + this.directory.getAbsolutePath(), "-classpath=" + 
this.classpath);
+        "-dir=" + this.directory.getAbsolutePath(), "-classpath=" + 
getManifestJarFromClasspath());
 
     execAndValidate("CacheServer pid: \\d+ status: running", "status",
         "-dir=" + this.directory.getAbsolutePath());
@@ -204,7 +205,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
             "log-file=" + this.logFileName, "log-level=info",
             "-J-Dgemfire.use-cluster-configuration=false", 
"-J-Dgemfire.locators=\"\"",
             "-server-port=" + this.serverPort, "-dir=" + this.directoryPath,
-            "-classpath=" + this.classpath})
+            "-classpath=" + getManifestJarFromClasspath()})
         .build();
 
     this.processWrapper.execute();
@@ -261,7 +262,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
         "-J-Xmx" + Runtime.getRuntime().maxMemory(), 
"-J-Dgemfire.use-cluster-configuration=false",
         "-J-Dgemfire.locators=\"\"", "log-file=" + this.logFileName,
         "cache-xml-file=" + this.cacheXmlFileName, "-dir=" + 
this.directoryPath,
-        "-classpath=" + this.classpath, "-rebalance");
+        "-classpath=" + getManifestJarFromClasspath(), "-rebalance");
 
     await().atMost(2, MINUTES).until(() -> 
assertThat(this.status.isStarted()).isTrue());
     await().atMost(2, MINUTES).until(() -> 
assertThat(this.status.isFinished()).isTrue());
@@ -286,7 +287,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
         "-J-Xmx" + Runtime.getRuntime().maxMemory(), 
"-J-Dgemfire.use-cluster-configuration=false",
         "-J-Dgemfire.locators=\"\"", "log-file=" + this.logFileName,
         "cache-xml-file=" + this.cacheXmlFileName, "-dir=" + 
this.directoryPath,
-        "-classpath=" + this.classpath, "-rebalance");
+        "-classpath=" + getManifestJarFromClasspath(), "-rebalance");
 
     await().atMost(2, MINUTES).until(() -> 
assertThat(this.status.isStarted()).isTrue());
     await().atMost(2, MINUTES).until(() -> 
assertThat(this.status.isFinished()).isTrue());
@@ -306,7 +307,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
         "-J-Xmx" + Runtime.getRuntime().maxMemory(), 
"-J-Dgemfire.use-cluster-configuration=false",
         "-J-Dgemfire.locators=\"\"", "log-file=" + this.logFileName,
         "cache-xml-file=" + this.cacheXmlFileName, "-dir=" + 
this.directoryPath,
-        "-classpath=" + this.classpath);
+        "-classpath=" + getManifestJarFromClasspath());
 
     execAndValidate("CacheServer pid: \\d+ status: running", "status", "-dir=" 
+ this.directory);
 
@@ -336,7 +337,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
         "-J-Xmx" + Runtime.getRuntime().maxMemory(), 
"-J-Dgemfire.use-cluster-configuration=false",
         "-J-Dgemfire.locators=\"\"", "log-file=" + this.logFileName,
         "cache-xml-file=" + this.cacheXmlFileName, "-dir=" + 
this.directoryPath,
-        "-classpath=" + this.classpath, "-server-port=" + this.commandPort);
+        "-classpath=" + getManifestJarFromClasspath(), "-server-port=" + 
this.commandPort);
 
     execAndValidate("CacheServer pid: \\d+ status: running", "status", "-dir=" 
+ this.directory);
 
@@ -368,7 +369,7 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
         "-J-Xmx" + Runtime.getRuntime().maxMemory(), 
"-J-Dgemfire.use-cluster-configuration=false",
         "-J-Dgemfire.locators=\"\"", "log-file=" + this.logFileName,
         "cache-xml-file=" + this.cacheXmlFileName, "-dir=" + 
this.directoryPath,
-        "-classpath=" + this.classpath, "-server-port=" + this.commandPort,
+        "-classpath=" + getManifestJarFromClasspath(), "-server-port=" + 
this.commandPort,
         "-server-bind-address=" + InetAddress.getLocalHost().getHostName());
 
     execAndValidate("CacheServer pid: \\d+ status: running", "status", "-dir=" 
+ this.directory);
@@ -390,6 +391,11 @@ public class DeprecatedCacheServerLauncherIntegrationTest {
     execAndValidate("The CacheServer has stopped\\.", "stop", "-dir=" + 
this.directory);
   }
 
+  private String getManifestJarFromClasspath() throws IOException {
+    List<String> parts = 
Arrays.asList(this.classpath.split(File.pathSeparator));
+    return ProcessWrapper.createManifestJar(parts, 
temporaryFolder.newFolder().getAbsolutePath());
+  }
+
   private void unexportObject(final Remote object) {
     if (object == null) {
       return;
diff --git 
a/geode-core/src/test/java/org/apache/geode/test/process/ProcessWrapper.java 
b/geode-core/src/test/java/org/apache/geode/test/process/ProcessWrapper.java
index a02f298..6fcc9dd 100644
--- a/geode-core/src/test/java/org/apache/geode/test/process/ProcessWrapper.java
+++ b/geode-core/src/test/java/org/apache/geode/test/process/ProcessWrapper.java
@@ -18,7 +18,13 @@ import static 
org.apache.geode.distributed.ConfigurationProperties.LOG_FILE;
 import static org.junit.Assert.fail;
 
 import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -26,11 +32,15 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.UUID;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
 import java.util.regex.Pattern;
 
 import org.apache.logging.log4j.Logger;
@@ -315,7 +325,8 @@ public class ProcessWrapper {
     try {
       synchronized (this.exitValue) {
         final String[] command =
-            defineCommand(jvmArgumentsList.toArray(new 
String[jvmArgumentsList.size()]));
+            defineCommand(jvmArgumentsList.toArray(new 
String[jvmArgumentsList.size()]),
+                workingDirectory.getCanonicalPath());
         this.process = new 
ProcessBuilder(command).directory(workingDirectory).start();
 
         final StringBuilder processCommand = new StringBuilder();
@@ -364,14 +375,19 @@ public class ProcessWrapper {
     }
   }
 
-  private String[] defineCommand(final String[] jvmArguments) {
+  private String[] defineCommand(final String[] jvmArguments, String 
workingDir)
+      throws IOException {
     final File javaBinDir = new File(System.getProperty("java.home"), "bin");
     final File javaExe = new File(javaBinDir, "java");
 
-    final List<String> argumentList = new ArrayList<String>();
+    String classPath = System.getProperty("java.class.path");
+    List<String> parts = Arrays.asList(classPath.split(File.pathSeparator));
+    String manifestJar = createManifestJar(parts, workingDir);
+
+    final List<String> argumentList = new ArrayList<>();
     argumentList.add(javaExe.getPath());
     argumentList.add("-classpath");
-    argumentList.add(System.getProperty("java.class.path"));
+    argumentList.add(manifestJar);
 
     // -d64 is not a valid option for windows and results in failure
     final int bits = Integer.getInteger("sun.arch.data.model", 0).intValue();
@@ -439,6 +455,57 @@ public class ProcessWrapper {
     return this.process;
   }
 
+  /**
+   * Method to create a manifest jar from a list of jars or directories. The 
provided entries are
+   * first converted to absolute paths and then converted to relative paths, 
relative to the
+   * location provided. This is to support the Manifest's requirement that 
class-paths only be
+   * relative. For example, if a jar is given as /a/b/c/foo.jar and the 
location is /tmp/app, the
+   * following will happen:
+   * - the manifest jar will be created as /tmp/app/manifest.jar
+   * - the class-path attribute will be ../../a/b/c/foo.jar
+   *
+   * @return the path to the created manifest jar
+   */
+  public static String createManifestJar(List<String> entries, String 
location) throws IOException {
+    // Must use the canonical path so that symbolic links are resolved 
correctly
+    Path locationPath = new File(location).getCanonicalFile().toPath();
+    Files.createDirectories(locationPath);
+
+    List<String> manifestEntries = new ArrayList<>();
+    for (String jar : entries) {
+      Path absPath = Paths.get(jar).toAbsolutePath();
+      Path relPath = locationPath.relativize(absPath);
+      if (absPath.toFile().isDirectory()) {
+        manifestEntries.add(relPath.toString() + "/");
+      } else {
+        manifestEntries.add(relPath.toString());
+      }
+    }
+
+    Manifest manifest = new Manifest();
+    Attributes global = manifest.getMainAttributes();
+    global.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
+    global.put(new Attributes.Name("Class-Path"), String.join(" ", 
manifestEntries));
+
+    // Generate a 'unique' 8 char name
+    String uuid = UUID.randomUUID().toString().substring(0, 8);
+    Path manifestJar = Paths.get(location, "manifest-" + uuid + ".jar");
+    JarOutputStream jos = null;
+    try {
+      File jarFile = manifestJar.toFile();
+      OutputStream os = new FileOutputStream(jarFile);
+      jos = new JarOutputStream(os, manifest);
+    } catch (IOException e) {
+      e.printStackTrace();
+    } finally {
+      if (jos != null) {
+        jos.close();
+      }
+    }
+
+    return manifestJar.toString();
+  }
+
   public static class Builder {
     private String[] jvmArguments = null;
     private Class<?> mainClass;

Reply via email to