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
--- 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<ClassBytecodes> classbytecoes) {
- if (!classbytecoes.isEmpty()) {
- ClassBytecodes[] cbcs = classbytecoes.toArray(new ClassBytecodes[classbytecoes.size()]);
+ private void load(Collection<ClassBytecodes> 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();
}
--- 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<Snippet, Integer, String> idGenerator;
final List<String> extraRemoteVMOptions;
final List<String> extraCompilerOptions;
- final ExecutionControl.Generator executionControlGenerator;
private int nextKeyIndex = 1;
@@ -102,13 +102,13 @@
private final Map<Subscription, Consumer<SnippetEvent>> 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}.
* <p>
* 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;
}
--- 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();
--- 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<String> 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<VirtualMachine> creator,
+ Callable<Integer> processComplete) throws Exception {
+ VirtualMachine result;
+ ExecutorService executor = Executors.newCachedThreadPool(runnable -> {
+ Thread thread = Executors.defaultThreadFactory().newThread(runnable);
+ thread.setDaemon(true);
+ return thread;
+ });
+ try {
+ Future<VirtualMachine> 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);
}
--- /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();
+ }
+}
--- /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())));
+ }
+}
--- /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())));
+ }
+}
--- /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))));
+ }
+}
--- /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();
+ }
+
+}
--- /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");
+ }
+}
--- /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");
+ }
+}
--- /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");
+ }
+}
--- /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");
+ }
+}
--- /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");
+ }
+}
--- /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");
+ }
+}
--- /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");
+ }
+}
--- 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");
}
--- 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();