# HG changeset patch # User rfield # Date 1479871442 28800 # Node ID 82e273c4f2b3f1cf342cfb7c832dc9ba71bba03e # Parent 2537564a3031bb9184da922c5f703127c44d5d43 8169519: JShell: Handle start-up failures and hangs gracefully 8166581: JShell: locks forever if -R options is wrong 8169234: JShell: hangs on startup on some computers caused by hostname Reviewed-by: jlahoda diff -r 2537564a3031 -r 82e273c4f2b3 langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Tue Nov 22 16:31:03 2016 -0800 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Tue Nov 22 19:24:02 2016 -0800 @@ -719,11 +719,11 @@ /** * If there are classes to load, loads by calling the execution engine. - * @param classbytecoes names of the classes to load. + * @param classbytecodes names of the classes to load. */ - private void load(Collection classbytecoes) { - if (!classbytecoes.isEmpty()) { - ClassBytecodes[] cbcs = classbytecoes.toArray(new ClassBytecodes[classbytecoes.size()]); + private void load(Collection classbytecodes) { + if (!classbytecodes.isEmpty()) { + ClassBytecodes[] cbcs = classbytecodes.toArray(new ClassBytecodes[classbytecodes.size()]); try { state.executionControl().load(cbcs); state.classTracker.markLoaded(cbcs); @@ -731,6 +731,7 @@ state.classTracker.markLoaded(cbcs, ex.installed()); } catch (NotImplementedException ex) { state.debug(ex, "Seriously?!? load not implemented"); + state.closeDown(); } catch (EngineTerminationException ex) { state.closeDown(); } diff -r 2537564a3031 -r 82e273c4f2b3 langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Tue Nov 22 16:31:03 2016 -0800 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Tue Nov 22 19:24:02 2016 -0800 @@ -30,6 +30,7 @@ import java.io.InputStream; import java.io.InterruptedIOException; import java.io.PrintStream; +import java.net.InetAddress; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; @@ -92,7 +93,6 @@ final BiFunction idGenerator; final List extraRemoteVMOptions; final List extraCompilerOptions; - final ExecutionControl.Generator executionControlGenerator; private int nextKeyIndex = 1; @@ -102,13 +102,13 @@ private final Map> keyStatusListeners = new HashMap<>(); private boolean closed = false; - private ExecutionControl executionControl = null; + private final ExecutionControl executionControl; private SourceCodeAnalysisImpl sourceCodeAnalysis = null; private static final String L10N_RB_NAME = "jdk.jshell.resources.l10n"; private static ResourceBundle outputRB = null; - JShell(Builder b) { + JShell(Builder b) throws IllegalStateException { this.in = b.in; this.out = b.out; this.err = b.err; @@ -116,11 +116,18 @@ this.idGenerator = b.idGenerator; this.extraRemoteVMOptions = b.extraRemoteVMOptions; this.extraCompilerOptions = b.extraCompilerOptions; - this.executionControlGenerator = b.executionControlGenerator==null - ? failOverExecutionControlGenerator(JdiDefaultExecutionControl.launch(), - JdiDefaultExecutionControl.listen("localhost"), - JdiDefaultExecutionControl.listen(null)) + ExecutionControl.Generator executionControlGenerator = b.executionControlGenerator==null + ? failOverExecutionControlGenerator( + JdiDefaultExecutionControl.listen(InetAddress.getLoopbackAddress().getHostAddress()), + JdiDefaultExecutionControl.launch(), + JdiDefaultExecutionControl.listen(null) + ) : b.executionControlGenerator; + try { + executionControl = executionControlGenerator.generate(new ExecutionEnvImpl()); + } catch (Throwable ex) { + throw new IllegalStateException("Launching JShell execution engine threw: " + ex.getMessage(), ex); + } this.maps = new SnippetMaps(this); this.keyMap = new KeyMap(this); @@ -331,9 +338,10 @@ * functionality. This creates a remote process for execution. It is * thus important to close the returned instance. * + * @throws IllegalStateException if the {@code JShell} instance could not be created. * @return the state engine */ - public JShell build() { + public JShell build() throws IllegalStateException { return new JShell(this); } } @@ -345,9 +353,10 @@ * That is, create an instance of {@code JShell}. *

* Equivalent to {@link JShell#builder() JShell.builder()}{@link JShell.Builder#build() .build()}. + * @throws IllegalStateException if the {@code JShell} instance could not be created. * @return an instance of {@code JShell}. */ - public static JShell create() { + public static JShell create() throws IllegalStateException { return builder().build(); } @@ -458,6 +467,7 @@ * Note that the unnamed package is not accessible from the package in which * {@link JShell#eval(String)} code is placed. * @param path the path to add to the classpath. + * @throws IllegalStateException if this {@code JShell} instance is closed. */ public void addToClasspath(String path) { // Compiler @@ -504,7 +514,11 @@ public void close() { if (!closed) { closeDown(); - executionControl().close(); + try { + executionControl().close(); + } catch (Throwable ex) { + // don't care about exceptions on close + } if (sourceCodeAnalysis != null) { sourceCodeAnalysis.close(); } @@ -733,14 +747,8 @@ } // --- private / package-private implementation support --- + ExecutionControl executionControl() { - if (executionControl == null) { - try { - executionControl = executionControlGenerator.generate(new ExecutionEnvImpl()); - } catch (Throwable ex) { - throw new InternalError("Launching execution engine threw: " + ex.getMessage(), ex); - } - } return executionControl; } diff -r 2537564a3031 -r 82e273c4f2b3 langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java Tue Nov 22 16:31:03 2016 -0800 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java Tue Nov 22 19:24:02 2016 -0800 @@ -63,7 +63,10 @@ */ public class JdiDefaultExecutionControl extends JdiExecutionControl { - private static final String REMOTE_AGENT = RemoteExecutionControl.class.getName(); + /** + * Default time-out expressed in milliseconds. + */ + private static final int DEFAULT_TIMEOUT = 5000; private VirtualMachine vm; private Process process; @@ -73,24 +76,60 @@ /** * Creates an ExecutionControl instance based on a JDI - * {@code LaunchingConnector}. + * {@code LaunchingConnector}. Same as + * {@code JdiDefaultExecutionControl.create(defaultRemoteAgent(), true, null, defaultTimeout())}. * * @return the generator */ public static ExecutionControl.Generator launch() { - return env -> create(env, true, null); + return create(defaultRemoteAgent(), true, null, defaultTimeout()); } /** * Creates an ExecutionControl instance based on a JDI - * {@code ListeningConnector}. + * {@code ListeningConnector}. Same as + * {@code JdiDefaultExecutionControl.create(defaultRemoteAgent(), false, host, defaultTimeout())}. * * @param host explicit hostname to use, if null use discovered * hostname, applies to listening only (!isLaunch) * @return the generator */ public static ExecutionControl.Generator listen(String host) { - return env -> create(env, false, host); + return create(defaultRemoteAgent(), false, host, defaultTimeout()); + } + + /** + * Creates a JDI based ExecutionControl instance. + * + * @param remoteAgent the remote agent to launch + * @param isLaunch does JDI do the launch? That is, LaunchingConnector, + * otherwise we start explicitly and use ListeningConnector + * @param host explicit hostname to use, if null use discovered + * hostname, applies to listening only (!isLaunch) + * @param timeout the start-up time-out in milliseconds + * @return the generator + */ + public static ExecutionControl.Generator create(String remoteAgent, + boolean isLaunch, String host, int timeout) { + return env -> create(env, remoteAgent, isLaunch, host, timeout); + } + + /** + * Default remote agent. + * + * @return the name of the standard remote agent + */ + public static String defaultRemoteAgent() { + return RemoteExecutionControl.class.getName(); + } + + /** + * Default remote connection time-out + * + * @return time to wait for connection before failing, expressed in milliseconds. + */ + public static int defaultTimeout() { + return DEFAULT_TIMEOUT; } /** @@ -103,6 +142,7 @@ * * @param env the context passed by * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) } + * @param remoteAgent the remote agent to launch * @param isLaunch does JDI do the launch? That is, LaunchingConnector, * otherwise we start explicitly and use ListeningConnector * @param host explicit hostname to use, if null use discovered @@ -110,16 +150,16 @@ * @return the channel * @throws IOException if there are errors in set-up */ - private static ExecutionControl create(ExecutionEnv env, - boolean isLaunch, String host) throws IOException { + private static ExecutionControl create(ExecutionEnv env, String remoteAgent, + boolean isLaunch, String host, int timeout) throws IOException { try (final ServerSocket listener = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) { - // timeout after 60 seconds - listener.setSoTimeout(60000); + // timeout on I/O-socket + listener.setSoTimeout(timeout); int port = listener.getLocalPort(); // Set-up the JDI connection JdiInitiator jdii = new JdiInitiator(port, - env.extraRemoteVMOptions(), REMOTE_AGENT, isLaunch, host); + env.extraRemoteVMOptions(), remoteAgent, isLaunch, host, timeout); VirtualMachine vm = jdii.vm(); Process process = jdii.process(); @@ -197,7 +237,7 @@ for (ThreadReference thread : vm().allThreads()) { // could also tag the thread (e.g. using name), to find it easier for (StackFrame frame : thread.frames()) { - if (REMOTE_AGENT.equals(frame.location().declaringType().name()) && + if (defaultRemoteAgent().equals(frame.location().declaringType().name()) && ( "invoke".equals(frame.location().method().name()) || "varValue".equals(frame.location().method().name()))) { ObjectReference thiz = frame.thisObject(); diff -r 2537564a3031 -r 82e273c4f2b3 langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java Tue Nov 22 16:31:03 2016 -0800 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java Tue Nov 22 19:24:02 2016 -0800 @@ -25,6 +25,7 @@ package jdk.jshell.execution; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -35,6 +36,13 @@ import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.LaunchingConnector; import com.sun.jdi.connect.ListeningConnector; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import com.sun.jdi.connect.IllegalConnectorArgumentsException; /** * Sets up a JDI connection, providing the resulting JDI {@link VirtualMachine} @@ -42,6 +50,12 @@ */ public class JdiInitiator { + // factor for the timeout on all of connect + private static final double CONNECT_TIMEOUT_FACTOR = 1.5; + + // Over-all connect time-out + private final int connectTimeout; + private VirtualMachine vm; private Process process = null; private final Connector connector; @@ -58,10 +72,12 @@ * otherwise we start explicitly and use ListeningConnector * @param host explicit hostname to use, if null use discovered * hostname, applies to listening only (!isLaunch) + * @param timeout the start-up time-out in milliseconds */ public JdiInitiator(int port, List remoteVMOptions, String remoteAgent, - boolean isLaunch, String host) { + boolean isLaunch, String host, int timeout) { this.remoteAgent = remoteAgent; + this.connectTimeout = (int) (timeout * CONNECT_TIMEOUT_FACTOR); String connectorName = isLaunch ? "com.sun.jdi.CommandLineLaunch" @@ -74,8 +90,11 @@ = isLaunch ? launchArgs(port, String.join(" ", remoteVMOptions)) : new HashMap<>(); - if (host != null && !isLaunch) { - argumentName2Value.put("localAddress", host); + if (!isLaunch) { + argumentName2Value.put("timeout", ""+timeout); + if (host != null && !isLaunch) { + argumentName2Value.put("localAddress", host); + } } this.connectorArgs = mergeConnectorArgs(connector, argumentName2Value); this.vm = isLaunch @@ -106,13 +125,12 @@ private VirtualMachine launchTarget() { LaunchingConnector launcher = (LaunchingConnector) connector; try { - VirtualMachine new_vm = launcher.launch(connectorArgs); + VirtualMachine new_vm = timedVirtualMachineCreation(() -> launcher.launch(connectorArgs), null); process = new_vm.process(); return new_vm; - } catch (Exception ex) { - reportLaunchFail(ex, "launch"); + } catch (Throwable ex) { + throw reportLaunchFail(ex, "launch"); } - return null; } /** @@ -140,15 +158,52 @@ ProcessBuilder pb = new ProcessBuilder(args); process = pb.start(); - // Forward out, err, and in // Accept the connection from the remote agent - vm = listener.accept(connectorArgs); - listener.stopListening(connectorArgs); + vm = timedVirtualMachineCreation(() -> listener.accept(connectorArgs), + () -> process.waitFor()); return vm; - } catch (Exception ex) { - reportLaunchFail(ex, "listen"); + } catch (Throwable ex) { + if (process != null) { + process.destroyForcibly(); + } + throw reportLaunchFail(ex, "listen"); + } finally { + try { + listener.stopListening(connectorArgs); + } catch (IOException | IllegalConnectorArgumentsException ex) { + // ignore + } } - return null; + } + + VirtualMachine timedVirtualMachineCreation(Callable creator, + Callable processComplete) throws Exception { + VirtualMachine result; + ExecutorService executor = Executors.newCachedThreadPool(runnable -> { + Thread thread = Executors.defaultThreadFactory().newThread(runnable); + thread.setDaemon(true); + return thread; + }); + try { + Future future = executor.submit(creator); + if (processComplete != null) { + executor.submit(() -> { + Integer i = processComplete.call(); + future.cancel(true); + return i; + }); + } + + try { + result = future.get(connectTimeout, TimeUnit.MILLISECONDS); + } catch (TimeoutException ex) { + future.cancel(true); + throw ex; + } + } finally { + executor.shutdownNow(); + } + return result; } private Connector findConnector(String name) { @@ -194,8 +249,10 @@ return argumentName2Value; } - private void reportLaunchFail(Exception ex, String context) { - throw new InternalError("Failed remote " + context + ": " + connector + + private InternalError reportLaunchFail(Throwable ex, String context) { + return new InternalError("Failed remote " + context + ": " + + ex.toString() + + " @ " + connector + " -- " + connectorArgs, ex); } diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/DyingRemoteAgent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/DyingRemoteAgent.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, 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. + */ + +import jdk.jshell.JShell; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import jdk.jshell.execution.RemoteExecutionControl; +import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout; + +class DyingRemoteAgent extends RemoteExecutionControl { + + static final boolean INFRA_VERIFY = false; + + public static void main(String[] args) throws Exception { + if (INFRA_VERIFY) { + RemoteExecutionControl.main(args); + } else { + System.exit(1); + } + } + + static JShell state(boolean isLaunch, String host) { + return JShell.builder().executionEngine( + JdiDefaultExecutionControl.create( + DyingRemoteAgent.class.getName(), + isLaunch, + host, + defaultTimeout())).build(); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8131029 8160127 8159935 8169519 + * @summary Test that fail-over works for fail-over ExecutionControl generators. + * @modules jdk.jshell/jdk.jshell.execution + * jdk.jshell/jdk.jshell.spi + * @build KullaTesting ExecutionControlTestBase + * @run testng FailOverExecutionControlDyingLaunchTest + */ + +import org.testng.annotations.Test; +import org.testng.annotations.BeforeMethod; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout; +import static jdk.jshell.execution.Util.failOverExecutionControlGenerator; + +@Test +public class FailOverExecutionControlDyingLaunchTest extends ExecutionControlTestBase { + + @BeforeMethod + @Override + public void setUp() { + setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator( + JdiDefaultExecutionControl.create( + DyingRemoteAgent.class.getName(), + true, + null, + defaultTimeout()), + JdiDefaultExecutionControl.launch()))); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8131029 8160127 8159935 8169519 + * @summary Test that fail-over works for fail-over ExecutionControl generators. + * @modules jdk.jshell/jdk.jshell.execution + * jdk.jshell/jdk.jshell.spi + * @build KullaTesting ExecutionControlTestBase + * @run testng FailOverExecutionControlHangingLaunchTest + */ + +import org.testng.annotations.Test; +import org.testng.annotations.BeforeMethod; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout; +import static jdk.jshell.execution.Util.failOverExecutionControlGenerator; + +@Test +public class FailOverExecutionControlHangingLaunchTest extends ExecutionControlTestBase { + + @BeforeMethod + @Override + public void setUp() { + setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator( + JdiDefaultExecutionControl.create( + HangingRemoteAgent.class.getName(), + true, + null, + defaultTimeout()), + JdiDefaultExecutionControl.launch()))); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/FailOverExecutionControlHangingListenTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/FailOverExecutionControlHangingListenTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8131029 8160127 8159935 8169519 + * @summary Test that fail-over works for fail-over ExecutionControl generators. + * @modules jdk.jshell/jdk.jshell.execution + * jdk.jshell/jdk.jshell.spi + * @build KullaTesting ExecutionControlTestBase + * @run testng FailOverExecutionControlHangingListenTest + */ + +import java.net.InetAddress; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeMethod; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import static jdk.jshell.execution.JdiDefaultExecutionControl.defaultTimeout; +import static jdk.jshell.execution.Util.failOverExecutionControlGenerator; + +@Test +public class FailOverExecutionControlHangingListenTest extends ExecutionControlTestBase { + + @BeforeMethod + @Override + public void setUp() { + String loopback = InetAddress.getLoopbackAddress().getHostAddress(); + setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator( + JdiDefaultExecutionControl.create( + HangingRemoteAgent.class.getName(), + false, + loopback, + defaultTimeout()), + JdiDefaultExecutionControl.listen(loopback)))); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/HangingRemoteAgent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/HangingRemoteAgent.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016, 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. + */ + +import jdk.jshell.JShell; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import jdk.jshell.execution.RemoteExecutionControl; + +/** + * Hang for three minutes (long enough to cause a timeout). + */ +class HangingRemoteAgent extends RemoteExecutionControl { + + private static final long DELAY = 4000L; + private static final int TIMEOUT = 2000; + private static final boolean INFRA_VERIFY = false; + + public static void main(String[] args) throws Exception { + if (INFRA_VERIFY) { + RemoteExecutionControl.main(args); + } else { + long end = System.currentTimeMillis() + DELAY; + long remaining; + while ((remaining = end - System.currentTimeMillis()) > 0L) { + try { + Thread.sleep(remaining); + } catch (InterruptedException ex) { + // loop again + } + } + } + } + + static JShell state(boolean isLaunch, String host) { + return JShell.builder().executionEngine( + JdiDefaultExecutionControl.create( + HangingRemoteAgent.class.getName(), + isLaunch, + host, + TIMEOUT)).build(); + } + +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/JdiBadOptionLaunchExecutionControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/JdiBadOptionLaunchExecutionControlTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8169519 8166581 + * @summary Tests for JDI connector failure + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @run testng JdiBadOptionLaunchExecutionControlTest + */ + +import org.testng.annotations.Test; +import jdk.jshell.JShell; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +@Test +public class JdiBadOptionLaunchExecutionControlTest { + + private static final String EXPECTED_ERROR = + "Launching JShell execution engine threw: Failed remote launch: java.util.concurrent.ExecutionException: com.sun.jdi.connect.VMStartException: VM initialization failed"; + + public void badOptionLaunchTest() { + try { + JShell.builder() + .executionEngine(JdiDefaultExecutionControl.launch()) + .remoteVMOptions("-BadBadOption") + .build(); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage()); + return; + } + fail("Expected IllegalStateException"); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/JdiBadOptionListenExecutionControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/JdiBadOptionListenExecutionControlTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8169519 8166581 + * @summary Tests for JDI connector failure + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @run testng JdiBadOptionListenExecutionControlTest + */ + +import org.testng.annotations.Test; +import jdk.jshell.JShell; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +@Test +public class JdiBadOptionListenExecutionControlTest { + + private static final String EXPECTED_ERROR = + "Launching JShell execution engine threw: Failed remote listen:"; + + public void badOptionListenTest() { + try { + JShell.builder() + .executionEngine(JdiDefaultExecutionControl.listen(null)) + .remoteVMOptions("-BadBadOption") + .build(); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage()); + return; + } + fail("Expected IllegalStateException"); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/JdiBogusHostListenExecutionControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/JdiBogusHostListenExecutionControlTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8169519 + * @summary Tests for JDI connector failure + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @run testng JdiBogusHostListenExecutionControlTest + */ + +import org.testng.annotations.Test; +import jdk.jshell.JShell; +import jdk.jshell.execution.JdiDefaultExecutionControl; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +@Test +public class JdiBogusHostListenExecutionControlTest { + + private static final String EXPECTED_ERROR = + "Launching JShell execution engine threw: Failed remote listen: java.net.SocketException: Unresolved address @ com.sun.jdi.SocketListe"; + + public void badOptionListenTest() { + try { + JShell.builder() + .executionEngine(JdiDefaultExecutionControl.listen("BattyRumbleBuckets-Snurfle-99-Blip")) + .build(); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage()); + return; + } + fail("Expected IllegalStateException"); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/JdiFailingLaunchExecutionControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/JdiFailingLaunchExecutionControlTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8169519 + * @summary Tests for JDI connector failure + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @build DyingRemoteAgent + * @run testng JdiFailingLaunchExecutionControlTest + */ + +import org.testng.annotations.Test; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +@Test +public class JdiFailingLaunchExecutionControlTest { + + private static final String EXPECTED_ERROR = + "Launching JShell execution engine threw: Accept timed out"; + + public void failLaunchTest() { + try { + System.err.printf("Unexpected return value: %s\n", DyingRemoteAgent.state(true, null).eval("33;")); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage()); + return; + } + fail("Expected IllegalStateException"); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/JdiFailingListenExecutionControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/JdiFailingListenExecutionControlTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8169519 + * @summary Tests for JDI connector failure + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @build DyingRemoteAgent + * @run testng JdiFailingListenExecutionControlTest + */ + +import org.testng.annotations.Test; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +@Test +public class JdiFailingListenExecutionControlTest { + + private static final String EXPECTED_ERROR = + "Launching JShell execution engine threw: Accept timed out"; + + public void failListenTest() { + try { + System.err.printf("Unexpected return value: %s\n", DyingRemoteAgent.state(true, null).eval("33;")); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage()); + return; + } + fail("Expected IllegalStateException"); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/JdiHangingLaunchExecutionControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/JdiHangingLaunchExecutionControlTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8169519 + * @summary Tests for JDI connector timeout failure + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @build HangingRemoteAgent + * @run testng JdiHangingLaunchExecutionControlTest + */ + +import org.testng.annotations.Test; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +@Test +public class JdiHangingLaunchExecutionControlTest { + + private static final String EXPECTED_ERROR = + "Launching JShell execution engine threw: Accept timed out"; + + public void hangLaunchTimeoutTest() { + try { + System.err.printf("Unexpected return value: %s\n", + HangingRemoteAgent.state(true, null).eval("33;")); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage()); + return; + } + fail("Expected IllegalStateException"); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/JdiHangingListenExecutionControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/JdiHangingListenExecutionControlTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, 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 + * @bug 8169519 + * @summary Tests for JDI connector timeout failure + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @build HangingRemoteAgent + * @run testng JdiHangingListenExecutionControlTest + */ + +import org.testng.annotations.Test; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +@Test +public class JdiHangingListenExecutionControlTest { + + private static final String EXPECTED_ERROR = + "Launching JShell execution engine threw: Accept timed out"; + + public void hangListenTimeoutTest() { + try { + System.err.printf("Unexpected return value: %s\n", + HangingRemoteAgent.state(false, null).eval("33;")); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().startsWith(EXPECTED_ERROR), ex.getMessage()); + return; + } + fail("Expected IllegalStateException"); + } +} diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/StartOptionTest.java --- a/langtools/test/jdk/jshell/StartOptionTest.java Tue Nov 22 16:31:03 2016 -0800 +++ b/langtools/test/jdk/jshell/StartOptionTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -22,7 +22,7 @@ */ /* - * @test 8151754 8080883 8160089 + * @test 8151754 8080883 8160089 8166581 * @summary Testing start-up options. * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -48,6 +48,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; @Test public class StartOptionTest { @@ -135,6 +136,17 @@ start("", "Argument to startup missing.", "--no-startup", "--startup"); } + public void testStartupFailedOption() throws Exception { + try { + start("", "", "-R-hoge-foo-bar"); + } catch (IllegalStateException ex) { + String s = ex.getMessage(); + assertTrue(s.startsWith("Launching JShell execution engine threw: Failed remote"), s); + return; + } + fail("Expected IllegalStateException"); + } + public void testStartupUnknown() throws Exception { start("", "File 'UNKNOWN' for '--startup' is not found.", "--startup", "UNKNOWN"); } diff -r 2537564a3031 -r 82e273c4f2b3 langtools/test/jdk/jshell/UserJdiUserRemoteTest.java --- a/langtools/test/jdk/jshell/UserJdiUserRemoteTest.java Tue Nov 22 16:31:03 2016 -0800 +++ b/langtools/test/jdk/jshell/UserJdiUserRemoteTest.java Tue Nov 22 19:24:02 2016 -0800 @@ -118,6 +118,7 @@ class MyExecutionControl extends JdiExecutionControl { private static final String REMOTE_AGENT = MyRemoteExecutionControl.class.getName(); + private static final int TIMEOUT = 2000; private VirtualMachine vm; private Process process; @@ -147,8 +148,8 @@ */ static ExecutionControl make(ExecutionEnv env, UserJdiUserRemoteTest test) throws IOException { try (final ServerSocket listener = new ServerSocket(0)) { - // timeout after 60 seconds - listener.setSoTimeout(60000); + // timeout for socket + listener.setSoTimeout(TIMEOUT); int port = listener.getLocalPort(); // Set-up the JDI connection @@ -158,7 +159,7 @@ + System.getProperty("path.separator") + System.getProperty("user.dir")); JdiInitiator jdii = new JdiInitiator(port, - opts, REMOTE_AGENT, true, null); + opts, REMOTE_AGENT, true, null, TIMEOUT); VirtualMachine vm = jdii.vm(); Process process = jdii.process();