Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8210050

Started Java Process with alternate JRE uses file 'lib/modules' in both JREs

XMLWordPrintable

    • x86_64
    • windows_7

      ADDITIONAL SYSTEM INFORMATION :
      Windows 7 64-Bit, JRE 10.0.2 (10.0.1)

      A DESCRIPTION OF THE PROBLEM :
      A Java main program (running with JRE-A e.g. 10.0.1, located at a/java) is starting a new Java process with an alternate JRE-B (e.g. 10.0.2 located at b/java) and terminates itself. The new process has still a handle to the modules file from JRE-A. Strangely, it has handles to both modules file in a/java/lib and b/java/lib.

      REGRESSION : Last worked in version 8u181

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      (1) create dir c:\temp\test\
      (2) copy JRE 10.0.1 to c:\temp\test\a\java\
      directories in c:\temp\test\:
      c:\temp\test\a\java\release
      c:\temp\test\a\java\README.html
      c:\temp\test\a\java\bin\*
      ...
      (3) copy JRE 10.0.2 to c:\temp\test\b\java\
      additional directories in c:\temp\test\:
      c:\temp\test\b\java\release
      c:\temp\test\b\java\README.html
      c:\temp\test\b\java\bin\*
      ...
      (4) start cmd terminal window and cd to c:\temp\test
      (5) set environment to JRE-A
      > set JAVA_HOME=c:\temp\test\a\java
      > set JRE_HOME=%JAVA_HOME%
      > set PATH=%JAVA_HOME%\bin\;%PATH%
      (6) copy TestRestartWithNewJre.class to c:\temp\test
      (7) run the test program with parameter -newjre C:\temp\test\b\java
      > java -cp . TestRestartWithNewJre -newjre C:\temp\test\b\java
      (8) check the result:
      (a) check p1.log: it shows the PID, the environment and properties of the starting process (JRE-A)
      (b) check the task-manager: the starting process (with JRE-A) does not exist anymore
      (c) check p2.log: it shows the PID, the environment and properties of the started process (JRE-B)
      There is also an AccessDeniedException. This is thrown, because the renaming of the JRE-A home dir (a\java -> a\java-<timestramp>) failed.
      (d) check the task-manager: the started process (with JRE-B) is still in process-list (sleeps for 1h)
      (e) The Process-Explorer (procexp.exe, from Windows SysInternals) shows, that the started Java process (JRE-B) has handles to both modules file (from JRE-A and JRE-B)


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The started process (using JRE-B) shouldn't have any reference to files in JRE-A
      ACTUAL -
      The Process-Explorer (procexp.exe, from Windows SysInternals) shows, that
      (a) the first process (using JRE-A) does not exist anymore
      (b) the started Java process (JRE-B) has handles to both modules file (from JRE-A and JRE-B)

      ---------- BEGIN SOURCE ----------
      import java.io.ByteArrayOutputStream;
      import java.io.File;
      import java.io.IOException;
      import java.io.PrintStream;
      import java.nio.file.Files;
      import java.nio.file.Path;
      import java.nio.file.Paths;
      import java.nio.file.StandardCopyOption;
      import java.nio.file.StandardOpenOption;
      import java.text.DateFormat;
      import java.text.SimpleDateFormat;
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.Collections;
      import java.util.Date;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;
      import java.util.Set;
      import java.util.stream.Collectors;
      import java.util.stream.Stream;

      public class TestRestartWithNewJre {

      private static String logFile = "p1.log";
      private static String oldJreHome = null;
      private static String newJreHome = null;

      public static void main(String[] args) {
      TestRestartWithNewJre t = new TestRestartWithNewJre();

      try {
      //get params
      setParams(args);

      //delete logfiles
      new File( logFile ).delete();

      //log params and all system properties
      log("args: " + Arrays.asList(args));
      dumpMap("properties: ", (Map) System.getProperties() );
      dumpMap("env: ", System.getenv() );

      // restart with alternate JRE or rename the old one
      t.start();
      } catch (IOException e) {
      log("IOException found", e);
      log("plese check first which file is locked..... (process sleep for 1h)");

      try {
      Thread.sleep(1000 * 60 * 60);
      } catch (InterruptedException e1) {
      log("sleep interrupted", e1);
      }
      } catch (Exception e) {
      log("error", e);
      }
      }


      private void start() throws IOException {

      // if new jre home is given, restart this job with the new JRE and put the reference to the old JRE as parameter "-oldjre" (for renaming later on)
      if( newJreHome != null ) {
      File newJreHomeDir = new File(newJreHome);
      if( newJreHomeDir.exists() && newJreHomeDir.isDirectory() ) {
      List<String> cmdItems = new ArrayList<String>();
      String javaExecutable = getJavaExe(newJreHomeDir);
      cmdItems.add(javaExecutable);
      cmdItems.add("-classpath");
      String jarName = getJarName();
      cmdItems.add(jarName);
      cmdItems.add(this.getClass().getName());
      cmdItems.add("-oldjre");
      String jreHome = System.getProperty("java.home");
      cmdItems.add(jreHome);
      cmdItems.add("-logfile");
      cmdItems.add("p2.log");

      invokeNewProcess(newJreHome, cmdItems);
      System.exit(1);
      } else {
      log("new JRE home '" + newJreHome + "' does not exist");
      System.exit(1);
      }

      } else if( oldJreHome != null ) {
      log("sleep for 2 seconds first, before trying to rename old/unused JRE-home");
      try {
      Thread.sleep(1000 * 2);
      } catch (InterruptedException e1) {
      log("sleep interrupted", e1);
      }

      // if old/unused jre home is given, try to rename it
      File oldJreHomeDir = new File(oldJreHome);
      if( oldJreHomeDir.exists() && oldJreHomeDir.isDirectory() ) {
      File targetPath = new File(oldJreHomeDir.getParentFile(), oldJreHomeDir.getName() + "-" + System.currentTimeMillis());

      log("try to move '" + oldJreHome + "' to '" + targetPath.getAbsolutePath() + "'");

      Path movedTargetPath = Files.move(oldJreHomeDir.toPath(), targetPath.toPath(), StandardCopyOption.ATOMIC_MOVE);
      if( movedTargetPath.toFile().exists() ) {
      log("rename '" + oldJreHome + "' to '" + movedTargetPath.toString() + "' done with success");
      } else {
      log("rename '" + oldJreHome + "' to '" + movedTargetPath.toString() + "' failed");
      }
      } else {
      log("wrong parameter -oldjre '" + oldJreHome + "', path does not exist");
      System.exit(1);
      }
      }


      }


      /*
      * get params
      */
      private static void setParams(String[] args) {
      for( int i=0; i<args.length; i++ ) {
      switch( args[i] ) {
      case "-oldjre":
      if( i+1 < args.length ) {
      oldJreHome = args[++i];
      }
      break;
      case "-newjre":
      if( i+1 < args.length ) {
      newJreHome = args[++i];
      }
      break;
      case "-logfile":
      if( i+1 < args.length ) {
      logFile = args[++i];
      }
      break;
      }
      }
      }


      /*
      * get the name of this jar (for restart with alternate jre)
      */
      private String getJarName() {
      return this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
      }

      /*
      * determine the path/name of the java executable for the given JRE home (the alternate jre for restart !)
      */
      private String getJavaExe(final File javaHomeDir) throws IOException {
      final Path start = javaHomeDir.toPath();
      final int maxDepth = 5;
      final StringBuilder sb = new StringBuilder("");
      try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) -> ((String.valueOf(path).endsWith("java.exe")||String.valueOf(path).endsWith("java"))&&path.toFile().isFile()&&(path.toFile().canExecute())))) {
      sb.append( stream.sorted().map(String::valueOf).collect(Collectors.joining(";")) );
      }
      String javaExe[] = sb.toString().split(";");
      if( javaExe.length != 1 ) {
      throw new RuntimeException("java executable not found: " + Arrays.asList(javaExe));
      }
      File javaExeFile = new File(javaExe[0]);
      boolean javaExecutableFound = javaExeFile != null && javaExeFile.exists() && javaExeFile.isFile() && javaExeFile.canRead() && javaExeFile.canExecute();
      if( !javaExecutableFound ) {
      throw new RuntimeException("java file not executable: " + javaExeFile.getAbsolutePath());
      }
      log("getJavaExe: " + javaExeFile.getCanonicalPath() );
      return javaExeFile.getCanonicalPath();
      }

      /*
      * invoke the new process (with alternate JRE)
      */
      private void invokeNewProcess(String newJavaHome, List<String> cmdItems) throws IOException {
      log("try to start process: " + cmdItems);
      final ProcessBuilder pb = new ProcessBuilder(cmdItems);
      Map<String,String> pbEnv = pb.environment();
      if( newJavaHome != null ) {
      pbEnv.clear();
      pbEnv.put("JRE_HOME", newJavaHome);
      pbEnv.put("JAVA_HOME", newJavaHome);
      }
      dumpMap("env for new process: ", pbEnv);

      pb.redirectErrorStream(true);
      pb.redirectInput();
      final Process process = pb.start();
      log("process is started: " + process.isAlive());
      }

      /*
      * log the current map (env or system properties)
      */
      private static void dumpMap(final String label, final Map<String,String> map) {
      Set<String> keys = map.keySet();
      java.util.List<String> sortedKeys = new ArrayList<String>(keys);
      Collections.sort(sortedKeys);
      StringBuilder buf = new StringBuilder(label).append(System.lineSeparator());
      for (Iterator<String> iter = sortedKeys.iterator(); iter.hasNext();) {
      String key = iter.next();
      String value = map.get(key);
      buf.append("\t" + key + "=" + value).append(System.lineSeparator());
      }
      log(buf.toString());
      }

      /*
      * log msg
      */
      private static void log(String msg) {
      log(msg, null);
      }

      /*
      * log msg and exception (if not null)
      */
      private static void log(String msg, Exception ex ) {
      final DateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss SSS ");
      String pid = " PID: " + getPID() + ", ";
      try {

      Files.write(Paths.get(logFile), (df.format(new Date()) + pid + msg + System.lineSeparator()).getBytes("UTF-8"),StandardOpenOption.CREATE,StandardOpenOption.APPEND);
      if( ex != null ) {
      ByteArrayOutputStream bo = new ByteArrayOutputStream();
      PrintStream ps = new PrintStream(bo);
      ex.printStackTrace(ps);
      Files.write(Paths.get(logFile), (df.format(new Date()) + pid + bo.toString() + System.lineSeparator()).getBytes("UTF-8"),StandardOpenOption.CREATE,StandardOpenOption.APPEND);
      }
      } catch (Exception e) {
      //ignore
      }
      }

      /*
      * determin the current PID
      */
      private static long getPID() {
      try {
      String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
      return Long.parseLong(processName.split("@")[0]);
      } catch( Exception ex ) {
      return -999;
      }
      }
      }

      ---------- END SOURCE ----------

      FREQUENCY : always


            psonal Pallavi Sonal (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: