# HG changeset patch # User amurillo # Date 1433787106 25200 # Node ID 03705c62af66f4eabaf54652ace1ef1017dcaa10 # Parent 5b500c93ce4822d47061cd518ff3f72d9d8cb5b5# Parent 56e7d9f565a32146525cecb691cd0ea0d934f8ca Merge diff -r 5b500c93ce48 -r 03705c62af66 test/lib-test/jdk/test/lib/apps/LingeredAppTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib-test/jdk/test/lib/apps/LingeredAppTest.java Mon Jun 08 11:11:46 2015 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Unit test for LingeredApp + * @compile LingeredAppTest.java + * @compile LingeredApp.java + * @run main LingeredAppTest + */ + +package jdk.test.lib.apps; + +import java.io.IOException; +import java.util.ArrayList; + +public class LingeredAppTest { + + public static void main(String[] args) { + try { + System.out.println("Starting LingeredApp with default parameters"); + + ArrayList cmd = new ArrayList(); + + // Propagate test.vm.options to LingeredApp, filter out possible empty options + String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+"); + for (String s : testVmOpts) { + if (!s.equals("")) { + cmd.add(s); + } + } + + cmd.add("-XX:+PrintFlagsFinal"); + + LingeredApp a = LingeredApp.startApp(cmd); + System.out.printf("App pid: %d\n", a.getPid()); + a.stopApp(); + + System.out.println("App output:"); + int count = 0; + for (String line : a.getAppOutput()) { + count += 1; + } + System.out.println("Found " + count + " lines in VM output"); + System.out.println("Test PASSED"); + } catch (IOException ex) { + ex.printStackTrace(); + System.out.println("Test ERROR"); + System.exit(3); + } + } +} diff -r 5b500c93ce48 -r 03705c62af66 test/lib/Makefile --- a/test/lib/Makefile Wed Jul 05 20:37:12 2017 +0200 +++ b/test/lib/Makefile Mon Jun 08 11:11:46 2015 -0700 @@ -42,7 +42,8 @@ JAR = $(JDK_HOME)/bin/jar WB_SRC_FILES = $(shell find $(SRC_DIR)/sun/hotspot -name '*.java') -SHARE_SRC_FILES = $(shell find $(SRC_DIR)/share/classes -name '*.java') +# test-lib.jar will contain only hprof classes until JDK-8081381 is resolved +SHARE_SRC_FILES = $(shell find $(SRC_DIR)/share/classes/jdk/test/lib/hprof -name '*.java') .PHONY: wb.filelist share.filelist clean cleantmp diff -r 5b500c93ce48 -r 03705c62af66 test/lib/share/classes/jdk/test/lib/apps/LingeredApp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/lib/share/classes/jdk/test/lib/apps/LingeredApp.java Mon Jun 08 11:11:46 2015 -0700 @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.apps; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * This is a framework to launch an app that could be synchronized with caller + * to make further attach actions reliable across supported platforms + + * Caller example: + * SmartTestApp a = SmartTestApp.startApp(cmd); + * // do something + * a.stopApp(); + * + * or fine grained control + * + * a = new SmartTestApp("MyLock.lck"); + * a.createLock(); + * a.runApp(); + * a.waitAppReady(); + * // do something + * a.deleteLock(); + * a.waitAppTerminate(); + * + * Then you can work with app output and process object + * + * output = a.getAppOutput(); + * process = a.getProcess(); + * + */ +public class LingeredApp { + + private static final long spinDelay = 1000; + + private final String lockFileName; + private long lockCreationTime; + private Process appProcess; + private final ArrayList storedAppOutput; + + /* + * Drain child process output, store it into string array + */ + class InputGobbler extends Thread { + + InputStream is; + List astr; + + InputGobbler(InputStream is, List astr) { + this.is = is; + this.astr = astr; + } + + public void run() { + try { + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line = null; + while ((line = br.readLine()) != null) { + astr.add(line); + } + } catch (IOException ex) { + // pass + } + } + } + + /** + * Create LingeredApp object on caller side. Lock file have be a valid filename + * at writable location + * + * @param lockFileName - the name of lock file + */ + public LingeredApp(String lockFileName) { + this.lockFileName = lockFileName; + this.storedAppOutput = new ArrayList(); + } + + /** + * + * @return name of lock file + */ + public String getLockFileName() { + return this.lockFileName; + } + + /** + * + * @return name of testapp + */ + public String getAppName() { + return this.getClass().getName(); + } + + /** + * + * @return pid of java process running testapp + */ + public long getPid() { + if (appProcess == null) { + throw new RuntimeException("Process is not alive"); + } + return appProcess.getPid(); + } + + /** + * + * @return process object + */ + public Process getProcess() { + return appProcess; + } + + /** + * + * @return application output as string array. Empty array if application produced no output + */ + public List getAppOutput() { + if (appProcess.isAlive()) { + throw new RuntimeException("Process is still alive. Can't get its output."); + } + return storedAppOutput; + } + + /* Make sure all part of the app use the same method to get dates, + as different methods could produce different results + */ + private static long epoch() { + return new Date().getTime(); + } + + private static long lastModified(String fileName) throws IOException { + Path path = Paths.get(fileName); + BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class); + return attr.lastModifiedTime().toMillis(); + } + + private static void setLastModified(String fileName, long newTime) throws IOException { + Path path = Paths.get(fileName); + FileTime fileTime = FileTime.fromMillis(newTime); + Files.setLastModifiedTime(path, fileTime); + } + + /** + * create lock + * + * @throws IOException + */ + public void createLock() throws IOException { + Path path = Paths.get(lockFileName); + // Files.deleteIfExists(path); + Files.createFile(path); + lockCreationTime = lastModified(lockFileName); + } + + /** + * Delete lock + * + * @throws IOException + */ + public void deleteLock() throws IOException { + try { + Path path = Paths.get(lockFileName); + Files.delete(path); + } catch (NoSuchFileException ex) { + // Lock already deleted. Ignore error + } + } + + public void waitAppTerminate() { + while (true) { + try { + appProcess.waitFor(); + break; + } catch (InterruptedException ex) { + // pass + } + } + } + + /** + * The app touches the lock file when it's started + * wait while it happens. Caller have to delete lock on wait error. + * + * @param timeout + * @throws java.io.IOException + */ + public void waitAppReady(long timeout) throws IOException { + long here = epoch(); + while (true) { + long epoch = epoch(); + if (epoch - here > (timeout * 1000)) { + throw new IOException("App waiting timeout"); + } + + // Live process should touch lock file every second + long lm = lastModified(lockFileName); + if (lm > lockCreationTime) { + break; + } + + // Make sure process didn't already exit + if (!appProcess.isAlive()) { + throw new IOException("App exited unexpectedly with " + appProcess.exitValue()); + } + + try { + Thread.sleep(spinDelay); + } catch (InterruptedException ex) { + // pass + } + } + } + + /** + * Run the app + * + * @param vmArguments + * @throws IOException + */ + public void runApp(List vmArguments) + throws IOException { + + // We should always use testjava or throw an exception, + // so we can't use JDKToolFinder.getJDKTool("java"); + // that falls back to compile java on error + String jdkPath = System.getProperty("test.jdk"); + if (jdkPath == null) { + // we are not under jtreg, try env + Map env = System.getenv(); + jdkPath = env.get("TESTJAVA"); + } + + if (jdkPath == null) { + throw new RuntimeException("Can't determine jdk path neither test.jdk property no TESTJAVA env are set"); + } + + String osname = System.getProperty("os.name"); + String javapath = jdkPath + ((osname.startsWith("window")) ? "/bin/java.exe" : "/bin/java"); + + List cmd = new ArrayList(); + cmd.add(javapath); + + + if (vmArguments == null) { + // Propagate test.vm.options to LingeredApp, filter out possible empty options + String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+"); + for (String s : testVmOpts) { + if (!s.equals("")) { + cmd.add(s); + } + } + } + else{ + // Lets user manage LingerApp options + cmd.addAll(vmArguments); + } + + // Make sure we set correct classpath to run the app + cmd.add("-cp"); + String classpath = System.getProperty("test.class.path"); + cmd.add((classpath == null) ? "." : classpath); + + cmd.add(this.getAppName()); + cmd.add(lockFileName); + + // Reporting + StringBuilder cmdLine = new StringBuilder(); + for (String strCmd : cmd) { + cmdLine.append("'").append(strCmd).append("' "); + } + + // A bit of verbosity + System.out.println("Command line: [" + cmdLine.toString() + "]"); + + ProcessBuilder pb = new ProcessBuilder(cmd); + // we don't expect any error output but make sure we are not stuck on pipe + // pb.redirectErrorStream(false); + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + + appProcess = pb.start(); + + // Create pipe reader for process, and read stdin and stderr to array of strings + InputGobbler gb = new InputGobbler(appProcess.getInputStream(), storedAppOutput); + gb.start(); + } + + /** + * High level interface for test writers + */ + /** + * Factory method that creates SmartAppTest object with ready to use application + * lock name is autogenerated, wait timeout is hardcoded + * @param cmd - vm options, could be null to auto add testvm.options + * @return LingeredApp object + * @throws IOException + */ + public static LingeredApp startApp(List cmd) throws IOException { + final String lockName = UUID.randomUUID().toString() + ".lck"; + final int waitTime = 10; + + LingeredApp a = new LingeredApp(lockName); + a.createLock(); + try { + a.runApp(cmd); + a.waitAppReady(waitTime); + } catch (Exception ex) { + a.deleteLock(); + throw ex; + } + + return a; + } + + public static LingeredApp startApp() throws IOException { + return startApp(null); + } + + /** + * Delete lock file that signal app to terminate, then + * waits until app is actually terminated. + * @throws IOException + */ + public void stopApp() throws IOException { + deleteLock(); + waitAppTerminate(); + int exitcode = appProcess.exitValue(); + if (exitcode != 0) { + throw new IOException("LingeredApp terminated with non-zero exit code " + exitcode); + } + } + + /** + * LastModified time might not work correctly in some cases it might + * cause later failures + */ + + public static boolean isLastModifiedWorking() { + boolean sane = true; + try { + long lm = lastModified("."); + if (lm == 0) { + System.err.println("SANITY Warning! The lastModifiedTime() doesn't work on this system, it returns 0"); + sane = false; + } + + long now = epoch(); + if (lm > now) { + System.err.println("SANITY Warning! The Clock is wrong on this system lastModifiedTime() > getTime()"); + sane = false; + } + + setLastModified(".", epoch()); + long lm1 = lastModified("."); + if (lm1 <= lm) { + System.err.println("SANITY Warning! The setLastModified doesn't work on this system"); + sane = false; + } + } + catch(IOException e) { + System.err.println("SANITY Warning! IOException during sanity check " + e); + sane = false; + } + + return sane; + } + + /** + * This part is the application it self + */ + public static void main(String args[]) { + + if (args.length != 1) { + System.err.println("Lock file name is not specified"); + System.exit(7); + } + + String theLockFileName = args[0]; + + try { + Path path = Paths.get(theLockFileName); + + while (Files.exists(path)) { + // Touch the lock to indicate our readiness + setLastModified(theLockFileName, epoch()); + Thread.sleep(spinDelay); + } + } catch (NoSuchFileException ex) { + // Lock deleted while we are setting last modified time. + // Ignore error and lets the app exits + } catch (Exception ex) { + System.err.println("LingeredApp ERROR: " + ex); + // Leave exit_code = 1 to Java launcher + System.exit(3); + } + + System.exit(0); + } +}