提交 7a70c520 编写于 作者: R rfield

8131029: JShell: recover from VMConnection launch failure

Reviewed-by: vromero
上级 a992a9f3
......@@ -28,17 +28,18 @@ import java.util.HashMap;
import java.util.Objects;
import com.sun.jdi.ReferenceType;
import java.util.List;
import com.sun.jdi.VirtualMachine;
/**
* Tracks the state of a class.
*/
class ClassTracker {
private final JDIEnv jdiEnv;
private final VirtualMachine vm;
private final HashMap<String, ClassInfo> map;
ClassTracker(JDIEnv jdiEnv) {
this.jdiEnv = jdiEnv;
ClassTracker(VirtualMachine vm) {
this.vm = vm;
this.map = new HashMap<>();
}
......@@ -96,7 +97,7 @@ class ClassTracker {
}
private ReferenceType nameToRef(String name) {
List<ReferenceType> rtl = jdiEnv.vm().classesByName(name);
List<ReferenceType> rtl = vm.classesByName(name);
if (rtl.size() != 1) {
return null;
}
......
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jshell.jdi;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import jdk.internal.jshell.debug.InternalDebugControl;
import jdk.jshell.JShellException;
import jdk.jshell.spi.ExecutionControl;
import jdk.jshell.spi.ExecutionEnv;
/**
* A meta implementation of ExecutionControl which cycles through the specified
* ExecutionControl instances until it finds one that starts.
*/
public class FailOverExecutionControl implements ExecutionControl {
private final List<ExecutionControl> ecl = new ArrayList<>();
private ExecutionControl active = null;
private final List<Exception> thrown = new ArrayList<>();
/**
* Create the ExecutionControl instance with at least one actual
* ExecutionControl instance.
*
* @param ec0 the first instance to try
* @param ecs the second and on instance to try
*/
public FailOverExecutionControl(ExecutionControl ec0, ExecutionControl... ecs) {
ecl.add(ec0);
for (ExecutionControl ec : ecs) {
ecl.add(ec);
}
}
@Override
public void start(ExecutionEnv env) throws Exception {
for (ExecutionControl ec : ecl) {
try {
ec.start(env);
// Success! This is our active ExecutionControl
active = ec;
return;
} catch (Exception ex) {
thrown.add(ex);
} catch (Throwable ex) {
thrown.add(new RuntimeException(ex));
}
InternalDebugControl.debug(env.state(), env.userErr(),
thrown.get(thrown.size() - 1), "failed one in FailOverExecutionControl");
}
// They have all failed -- rethrow the first exception we encountered
throw thrown.get(0);
}
@Override
public void close() {
active.close();
}
@Override
public boolean addToClasspath(String path) {
return active.addToClasspath(path);
}
@Override
public String invoke(String classname, String methodname) throws JShellException {
return active.invoke(classname, methodname);
}
@Override
public boolean load(Collection<String> classes) {
return active.load(classes);
}
@Override
public boolean redefine(Collection<String> classes) {
return active.redefine(classes);
}
@Override
public ClassStatus getClassStatus(String classname) {
return active.getClassStatus(classname);
}
@Override
public void stop() {
active.stop();
}
@Override
public String varValue(String classname, String varname) {
return active.varValue(classname, varname);
}
}
......@@ -49,6 +49,8 @@ import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
*/
class JDIConnection {
private static final String REMOTE_AGENT = "jdk.internal.jshell.remote.RemoteAgent";
private VirtualMachine vm;
private boolean active = true;
private Process process = null;
......@@ -59,12 +61,12 @@ class JDIConnection {
private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
private final int traceFlags;
synchronized void notifyOutputComplete() {
private synchronized void notifyOutputComplete() {
outputCompleteCount++;
notifyAll();
}
synchronized void waitOutputComplete() {
private synchronized void waitOutputComplete() {
// Wait for stderr and stdout
if (process != null) {
while (outputCompleteCount < 2) {
......@@ -102,61 +104,72 @@ class JDIConnection {
return arguments;
}
JDIConnection(JDIExecutionControl ec, String connectorName, Map<String, String> argumentName2Value, int traceFlags) {
this.ec = ec;
this.connector = findConnector(connectorName);
if (connector == null) {
throw new IllegalArgumentException("No connector named: " + connectorName);
}
connectorArgs = mergeConnectorArgs(connector, argumentName2Value);
this.traceFlags = traceFlags;
/**
* The JShell specific Connector args for the LaunchingConnector.
*
* @param portthe socket port for (non-JDI) commands
* @param remoteVMOptions any user requested VM options
* @return the argument map
*/
private static Map<String, String> launchArgs(int port, String remoteVMOptions) {
Map<String, String> argumentName2Value = new HashMap<>();
argumentName2Value.put("main", REMOTE_AGENT + " " + port);
argumentName2Value.put("options", remoteVMOptions);
return argumentName2Value;
}
synchronized VirtualMachine open() {
if (connector instanceof LaunchingConnector) {
/**
* Start the remote agent and establish a JDI connection to it.
*
* @param ec the execution control instance
* @param port the socket port for (non-JDI) commands
* @param remoteVMOptions any user requested VM options
* @param isLaunch does JDI do the launch? That is, LaunchingConnector,
* otherwise we start explicitly and use ListeningConnector
*/
JDIConnection(JDIExecutionControl ec, int port, List<String> remoteVMOptions, boolean isLaunch) {
this(ec,
isLaunch
? "com.sun.jdi.CommandLineLaunch"
: "com.sun.jdi.SocketListen",
isLaunch
? launchArgs(port, String.join(" ", remoteVMOptions))
: new HashMap<>(),
0);
if (isLaunch) {
vm = launchTarget();
} else if (connector instanceof AttachingConnector) {
vm = attachTarget();
} else if (connector instanceof ListeningConnector) {
vm = listenTarget();
} else {
throw new InternalError("Invalid connect type");
}
vm.setDebugTraceMode(traceFlags);
// Uncomment here and below to enable event requests
// installEventRequests(vm);
return vm;
}
synchronized boolean setConnectorArg(String name, String value) {
/*
* Too late if the connection already made
*/
if (vm != null) {
return false;
vm = listenTarget(port, remoteVMOptions);
}
Connector.Argument argument = connectorArgs.get(name);
if (argument == null) {
return false;
if (isOpen() && vm().canBeModified()) {
/*
* Connection opened on startup.
*/
new JDIEventHandler(vm(), (b) -> ec.handleVMExit())
.start();
}
argument.setValue(value);
return true;
}
String connectorArg(String name) {
Connector.Argument argument = connectorArgs.get(name);
if (argument == null) {
return "";
/**
* Base constructor -- set-up a JDI connection.
*
* @param ec the execution control instance
* @param connectorName the standardized name of the connector
* @param argumentName2Value the argument map
* @param traceFlags should we trace JDI behavior
*/
JDIConnection(JDIExecutionControl ec, String connectorName, Map<String, String> argumentName2Value, int traceFlags) {
this.ec = ec;
this.connector = findConnector(connectorName);
if (connector == null) {
throw new IllegalArgumentException("No connector named: " + connectorName);
}
return argument.value();
connectorArgs = mergeConnectorArgs(connector, argumentName2Value);
this.traceFlags = traceFlags;
}
public synchronized VirtualMachine vm() {
final synchronized VirtualMachine vm() {
if (vm == null) {
throw new JDINotConnectedException();
} else {
......@@ -164,14 +177,10 @@ class JDIConnection {
}
}
synchronized boolean isOpen() {
private synchronized boolean isOpen() {
return (vm != null);
}
boolean isLaunch() {
return (connector instanceof LaunchingConnector);
}
synchronized boolean isRunning() {
return process != null && process.isAlive();
}
......@@ -181,7 +190,7 @@ class JDIConnection {
active = false;
}
public synchronized void disposeVM() {
synchronized void disposeVM() {
try {
if (vm != null) {
vm.dispose(); // This could NPE, so it is caught below
......@@ -189,6 +198,8 @@ class JDIConnection {
}
} catch (VMDisconnectedException ex) {
// Ignore if already closed
} catch (Throwable e) {
ec.debug(DBG_GEN, null, "disposeVM threw: " + e);
} finally {
if (process != null) {
process.destroy();
......@@ -198,41 +209,6 @@ class JDIConnection {
}
}
/*** Preserved for possible future support of event requests
private void installEventRequests(VirtualMachine vm) {
if (vm.canBeModified()){
setEventRequests(vm);
resolveEventRequests();
}
}
private void setEventRequests(VirtualMachine vm) {
EventRequestManager erm = vm.eventRequestManager();
// Normally, we want all uncaught exceptions. We request them
// via the same mechanism as Commands.commandCatchException()
// so the user can ignore them later if they are not
// interested.
// FIXME: this works but generates spurious messages on stdout
// during startup:
// Set uncaught java.lang.Throwable
// Set deferred uncaught java.lang.Throwable
Commands evaluator = new Commands();
evaluator.commandCatchException
(new StringTokenizer("uncaught java.lang.Throwable"));
ThreadStartRequest tsr = erm.createThreadStartRequest();
tsr.enable();
ThreadDeathRequest tdr = erm.createThreadDeathRequest();
tdr.enable();
}
private void resolveEventRequests() {
Env.specList.resolveAll();
}
***/
private void dumpStream(InputStream inStream, final PrintStream pStream) throws IOException {
BufferedReader in =
new BufferedReader(new InputStreamReader(inStream));
......@@ -270,7 +246,7 @@ class JDIConnection {
dumpStream(inStream, pStream);
} catch (IOException ex) {
ec.debug(ex, "Failed reading output");
ec.jdiEnv.shutdown();
ec.handleVMExit();
} finally {
notifyOutputComplete();
}
......@@ -297,7 +273,7 @@ class JDIConnection {
}
} catch (IOException ex) {
ec.debug(ex, "Failed reading output");
ec.jdiEnv.shutdown();
ec.handleVMExit();
}
}
};
......@@ -305,15 +281,19 @@ class JDIConnection {
thr.start();
}
private void forwardIO() {
displayRemoteOutput(process.getErrorStream(), ec.execEnv.userErr());
displayRemoteOutput(process.getInputStream(), ec.execEnv.userOut());
readRemoteInput(process.getOutputStream(), ec.execEnv.userIn());
}
/* launch child target vm */
private VirtualMachine launchTarget() {
LaunchingConnector launcher = (LaunchingConnector)connector;
try {
VirtualMachine new_vm = launcher.launch(connectorArgs);
process = new_vm.process();
displayRemoteOutput(process.getErrorStream(), ec.execEnv.userErr());
displayRemoteOutput(process.getInputStream(), ec.execEnv.userOut());
readRemoteInput(process.getOutputStream(), ec.execEnv.userIn());
forwardIO();
return new_vm;
} catch (Exception ex) {
reportLaunchFail(ex, "launch");
......@@ -321,25 +301,35 @@ class JDIConnection {
return null;
}
/* JShell currently uses only launch, preserved for futures: */
/* attach to running target vm */
private VirtualMachine attachTarget() {
AttachingConnector attacher = (AttachingConnector)connector;
try {
return attacher.attach(connectorArgs);
} catch (Exception ex) {
reportLaunchFail(ex, "attach");
}
return null;
}
/* JShell currently uses only launch, preserved for futures: */
/* listen for connection from target vm */
private VirtualMachine listenTarget() {
ListeningConnector listener = (ListeningConnector)connector;
/**
* Directly launch the remote agent and connect JDI to it with a
* ListeningConnector.
*/
private VirtualMachine listenTarget(int port, List<String> remoteVMOptions) {
ListeningConnector listener = (ListeningConnector) connector;
try {
String retAddress = listener.startListening(connectorArgs);
ec.debug(DBG_GEN, "Listening at address: " + retAddress);
// Start listening, get the JDI connection address
String addr = listener.startListening(connectorArgs);
ec.debug(DBG_GEN, "Listening at address: " + addr);
// Launch the RemoteAgent requesting a connection on that address
String javaHome = System.getProperty("java.home");
List<String> args = new ArrayList<>();
args.add(javaHome == null
? "java"
: javaHome + File.separator + "bin" + File.separator + "java");
args.add("-agentlib:jdwp=transport=" + connector.transport().name() +
",address=" + addr);
args.addAll(remoteVMOptions);
args.add(REMOTE_AGENT);
args.add("" + port);
ProcessBuilder pb = new ProcessBuilder(args);
process = pb.start();
// Forward out, err, and in
forwardIO();
// Accept the connection from the remote agent
vm = listener.accept(connectorArgs);
listener.stopListening(connectorArgs);
return vm;
......@@ -353,4 +343,4 @@ class JDIConnection {
throw new InternalError("Failed remote " + context + ": " + connector +
" -- " + connectorArgs, ex);
}
}
}
\ No newline at end of file
......@@ -25,6 +25,7 @@
package jdk.internal.jshell.jdi;
import java.util.function.Consumer;
import com.sun.jdi.*;
import com.sun.jdi.event.*;
......@@ -34,16 +35,20 @@ import com.sun.jdi.event.*;
*/
class JDIEventHandler implements Runnable {
Thread thread;
volatile boolean connected = true;
boolean completed = false;
String shutdownMessageKey;
final JDIEnv env;
private final Thread thread;
private volatile boolean connected = true;
private boolean completed = false;
private final VirtualMachine vm;
private final Consumer<Boolean> reportVMExit;
JDIEventHandler(JDIEnv env) {
this.env = env;
JDIEventHandler(VirtualMachine vm, Consumer<Boolean> reportVMExit) {
this.vm = vm;
this.reportVMExit = reportVMExit;
this.thread = new Thread(this, "event-handler");
this.thread.start();
}
void start() {
thread.start();
}
synchronized void shutdown() {
......@@ -56,7 +61,7 @@ class JDIEventHandler implements Runnable {
@Override
public void run() {
EventQueue queue = env.vm().eventQueue();
EventQueue queue = vm.eventQueue();
while (connected) {
try {
EventSet eventSet = queue.remove();
......@@ -111,27 +116,23 @@ class JDIEventHandler implements Runnable {
private void handleExitEvent(Event event) {
if (event instanceof VMDeathEvent) {
vmDied = true;
shutdownMessageKey = "The application exited";
} else if (event instanceof VMDisconnectEvent) {
connected = false;
if (!vmDied) {
shutdownMessageKey = "The application has been disconnected";
}
} else {
throw new InternalError("Unexpected event type: " +
event.getClass());
}
env.shutdown();
reportVMExit.accept(vmDied);
}
synchronized void handleDisconnectedException() {
private synchronized void handleDisconnectedException() {
/*
* A VMDisconnectedException has happened while dealing with
* another event. We need to flush the event queue, dealing only
* with exit events (VMDeath, VMDisconnect) so that we terminate
* correctly.
*/
EventQueue queue = env.vm().eventQueue();
EventQueue queue = vm.eventQueue();
while (connected) {
try {
EventSet eventSet = queue.remove();
......
......@@ -34,13 +34,20 @@ import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import com.sun.jdi.*;
import java.io.EOFException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sun.jdi.BooleanValue;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VirtualMachine;
import static java.util.stream.Collectors.toList;
import jdk.jshell.JShellException;
import jdk.jshell.spi.ExecutionControl;
......@@ -59,13 +66,28 @@ import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
public class JDIExecutionControl implements ExecutionControl {
ExecutionEnv execEnv;
JDIEnv jdiEnv;
private ClassTracker tracker;
private JDIEventHandler handler;
private final boolean isLaunch;
private JDIConnection connection;
private ClassTracker classTracker;
private Socket socket;
private ObjectInputStream remoteIn;
private ObjectOutputStream remoteOut;
private String remoteVMOptions;
/**
* Creates an ExecutionControl instance based on JDI.
*
* @param isLaunch true for LaunchingConnector; false for ListeningConnector
*/
public JDIExecutionControl(boolean isLaunch) {
this.isLaunch = isLaunch;
}
/**
* Creates an ExecutionControl instance based on a JDI LaunchingConnector.
*/
public JDIExecutionControl() {
this.isLaunch = true;
}
/**
* Initializes the launching JDI execution engine. Initialize JDI and use it
......@@ -79,20 +101,12 @@ public class JDIExecutionControl implements ExecutionControl {
@Override
public void start(ExecutionEnv execEnv) throws IOException {
this.execEnv = execEnv;
this.jdiEnv = new JDIEnv(this);
this.tracker = new ClassTracker(jdiEnv);
StringBuilder sb = new StringBuilder();
execEnv.extraRemoteVMOptions().stream()
.forEach(s -> {
sb.append(" ");
sb.append(s);
});
this.remoteVMOptions = sb.toString();
try (ServerSocket listener = new ServerSocket(0)) {
// timeout after 60 seconds
listener.setSoTimeout(60000);
int port = listener.getLocalPort();
jdiGo(port);
connection = new JDIConnection(this, port, execEnv.extraRemoteVMOptions(), isLaunch);
this.socket = listener.accept();
// out before in -- match remote creation so we don't hang
this.remoteOut = new ObjectOutputStream(socket.getOutputStream());
......@@ -109,16 +123,15 @@ public class JDIExecutionControl implements ExecutionControl {
@Override
public void close() {
try {
JDIConnection c = jdiEnv.connection();
if (c != null) {
c.beginShutdown();
if (connection != null) {
connection.beginShutdown();
}
if (remoteOut != null) {
remoteOut.writeInt(CMD_EXIT);
remoteOut.flush();
}
if (c != null) {
c.disposeVM();
if (connection != null) {
connection.disposeVM();
}
} catch (IOException ex) {
debug(DBG_GEN, "Exception on JDI exit: %s\n", ex);
......@@ -185,9 +198,9 @@ public class JDIExecutionControl implements ExecutionControl {
return result;
}
} catch (IOException | RuntimeException ex) {
if (!jdiEnv.connection().isRunning()) {
if (!connection.isRunning()) {
// The JDI connection is no longer live, shutdown.
jdiEnv.shutdown();
handleVMExit();
} else {
debug(DBG_GEN, "Exception on remote invoke: %s\n", ex);
return "Execution failure: " + ex.getMessage();
......@@ -221,7 +234,7 @@ public class JDIExecutionControl implements ExecutionControl {
return result;
}
} catch (EOFException ex) {
jdiEnv.shutdown();
handleVMExit();
} catch (IOException ex) {
debug(DBG_GEN, "Exception on remote var value: %s\n", ex);
return "Execution failure: " + ex.getMessage();
......@@ -273,7 +286,7 @@ public class JDIExecutionControl implements ExecutionControl {
ci -> ci.getReferenceTypeOrNull(),
ci -> ci.getBytes()));
// Attempt redefine. Throws exceptions on failure.
jdiEnv.vm().redefineClasses(rmp);
connection.vm().redefineClasses(rmp);
// Successful: mark the bytes as loaded.
infos.stream()
.forEach(ci -> ci.markLoaded());
......@@ -287,6 +300,24 @@ public class JDIExecutionControl implements ExecutionControl {
}
}
// the VM has gone down in flames or because user evaled System.exit() or the like
void handleVMExit() {
if (connection != null) {
// If there is anything left dispose of it
connection.disposeVM();
}
// Tell JShell-core that the VM has died
execEnv.closeDown();
}
// Lazy init class tracker
private ClassTracker classTracker() {
if (classTracker == null) {
classTracker = new ClassTracker(connection.vm());
}
return classTracker;
}
/**
* Converts a collection of class names into ClassInfo instances associated
* with the most recently compiled class bytes.
......@@ -296,7 +327,7 @@ public class JDIExecutionControl implements ExecutionControl {
*/
private List<ClassInfo> withBytes(Collection<String> classes) {
return classes.stream()
.map(cn -> tracker.classInfo(cn, execEnv.getClassBytes(cn)))
.map(cn -> classTracker().classInfo(cn, execEnv.getClassBytes(cn)))
.collect(toList());
}
......@@ -310,7 +341,7 @@ public class JDIExecutionControl implements ExecutionControl {
*/
@Override
public ClassStatus getClassStatus(String classname) {
ClassInfo ci = tracker.get(classname);
ClassInfo ci = classTracker().get(classname);
if (ci.getReferenceTypeOrNull() == null) {
// If the class does not have a JDI ReferenceType it has not been loaded
return ClassStatus.UNKNOWN;
......@@ -403,37 +434,6 @@ public class JDIExecutionControl implements ExecutionControl {
return elems;
}
/**
* Launch the remote agent as a JDI connection.
*
* @param port the socket port for (non-JDI) commands
*/
private void jdiGo(int port) {
//MessageOutput.textResources = ResourceBundle.getBundle("impl.TTYResources",
// Locale.getDefault());
// Set-up for a fresh launch of a remote agent with any user-specified VM options.
String connectorName = "com.sun.jdi.CommandLineLaunch";
Map<String, String> argumentName2Value = new HashMap<>();
argumentName2Value.put("main", "jdk.internal.jshell.remote.RemoteAgent " + port);
argumentName2Value.put("options", remoteVMOptions);
boolean launchImmediately = true;
int traceFlags = 0;// VirtualMachine.TRACE_SENDS | VirtualMachine.TRACE_EVENTS;
// Launch.
jdiEnv.init(connectorName, argumentName2Value, launchImmediately, traceFlags);
if (jdiEnv.connection().isOpen() && jdiEnv.vm().canBeModified()) {
/*
* Connection opened on startup. Start event handler
* immediately, telling it (through arg 2) to stop on the
* VM start event.
*/
handler = new JDIEventHandler(jdiEnv);
}
}
private final Object STOP_LOCK = new Object();
private boolean userCodeRunning = false;
......@@ -447,7 +447,7 @@ public class JDIExecutionControl implements ExecutionControl {
return;
}
VirtualMachine vm = handler.env.vm();
VirtualMachine vm = connection.vm();
vm.suspend();
try {
OUTER:
......
......@@ -44,6 +44,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
import jdk.internal.jshell.debug.InternalDebugControl;
import jdk.internal.jshell.jdi.FailOverExecutionControl;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import static jdk.jshell.Util.expunge;
......@@ -112,7 +113,9 @@ public class JShell implements AutoCloseable {
this.idGenerator = b.idGenerator;
this.extraRemoteVMOptions = b.extraRemoteVMOptions;
this.executionControl = b.executionControl==null
? new JDIExecutionControl()
? new FailOverExecutionControl(
new JDIExecutionControl(),
new JDIExecutionControl(false))
: b.executionControl;
this.maps = new SnippetMaps(this);
......@@ -759,7 +762,11 @@ public class JShell implements AutoCloseable {
if (!closed) {
// Send only once
closed = true;
notifyShutdownEvent(this);
try {
notifyShutdownEvent(this);
} catch (Throwable thr) {
// Don't care about dying exceptions
}
}
}
......
......@@ -21,40 +21,14 @@
* questions.
*/
/*
* @test
* @bug 8156101
* @summary Tests for ExecutionControl SPI
* @build KullaTesting LocalExecutionControl
* @run testng ExecutionControlTest
*/
import javax.tools.Diagnostic;
import jdk.jshell.VarSnippet;
import org.testng.annotations.Test;
import static jdk.jshell.Snippet.Status.VALID;
import static jdk.jshell.Snippet.SubKind.*;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.BeforeMethod;
@Test
public class ExecutionControlTest extends KullaTesting {
@BeforeMethod
@Override
public void setUp() {
setUp(new LocalExecutionControl());
}
public void verifyLocal() throws ClassNotFoundException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
System.setProperty("LOCAL_CHECK", "TBD");
assertEquals(System.getProperty("LOCAL_CHECK"), "TBD");
assertEval("System.setProperty(\"LOCAL_CHECK\", \"local\")");
assertEquals(System.getProperty("LOCAL_CHECK"), "local");
}
public class ExecutionControlTestBase extends KullaTesting {
public void classesDeclaration() {
assertEval("interface A { }");
......@@ -71,7 +45,6 @@ public class ExecutionControlTest extends KullaTesting {
assertActiveKeys();
}
@Test
public void interfaceTest() {
String interfaceSource
= "interface A {\n"
......
/*
* 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
* @summary Test that fail-over works for FailOverExecutionControl
* @modules jdk.jshell/jdk.internal.jshell.jdi
* jdk.jshell/jdk.jshell.spi
* @build KullaTesting ExecutionControlTestBase
* @run testng FailOverExecutionControlTest
*/
import java.util.Collection;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
import jdk.internal.jshell.jdi.FailOverExecutionControl;
import jdk.internal.jshell.jdi.JDIExecutionControl;
import jdk.jshell.JShellException;
import jdk.jshell.spi.ExecutionControl;
import jdk.jshell.spi.ExecutionEnv;
@Test
public class FailOverExecutionControlTest extends ExecutionControlTestBase {
@BeforeMethod
@Override
public void setUp() {
setUp(new FailOverExecutionControl(
new AlwaysFailingExecutionControl(),
new AlwaysFailingExecutionControl(),
new JDIExecutionControl()));
}
class AlwaysFailingExecutionControl implements ExecutionControl {
@Override
public void start(ExecutionEnv env) throws Exception {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public void close() {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public boolean addToClasspath(String path) {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public String invoke(String classname, String methodname) throws JShellException {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public boolean load(Collection<String> classes) {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public boolean redefine(Collection<String> classes) {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public ClassStatus getClassStatus(String classname) {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public void stop() {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
@Override
public String varValue(String classname, String varname) {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
}
}
/*
* 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
* @summary Tests for alternate JDI connector -- listening
* @modules jdk.jshell/jdk.internal.jshell.jdi
* @build KullaTesting ExecutionControlTestBase
* @run testng JDIListeningExecutionControlTest
*/
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
import jdk.internal.jshell.jdi.JDIExecutionControl;
@Test
public class JDIListeningExecutionControlTest extends ExecutionControlTestBase {
@BeforeMethod
@Override
public void setUp() {
setUp(new JDIExecutionControl(false));
}
}
......@@ -560,6 +560,7 @@ public class ToolBasicTest extends ReplToolTesting {
);
}
@Test(enabled = false) // TODO 8158197
public void testHeadlessEditPad() {
String prevHeadless = System.getProperty("java.awt.headless");
try {
......
/*
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
* 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
......@@ -23,58 +21,33 @@
* questions.
*/
package jdk.internal.jshell.jdi;
import java.util.Map;
import com.sun.jdi.*;
import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
/**
* Representation of a Java Debug Interface environment
* Select methods extracted from jdb Env; shutdown() adapted to JShell shutdown.
/*
* @test
* @bug 8156101
* @summary Tests for ExecutionControl SPI
* @build KullaTesting LocalExecutionControl ExecutionControlTestBase
* @run testng UserExecutionControlTest
*/
class JDIEnv {
private JDIConnection connection;
private final JDIExecutionControl ec;
JDIEnv(JDIExecutionControl ec) {
this.ec = ec;
}
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.BeforeMethod;
void init(String connectorName, Map<String, String> argumentName2Value, boolean openNow, int flags) {
connection = new JDIConnection(ec, connectorName, argumentName2Value, flags);
if (!connection.isLaunch() || openNow) {
connection.open();
}
}
@Test
public class UserExecutionControlTest extends ExecutionControlTestBase {
JDIConnection connection() {
return connection;
@BeforeMethod
@Override
public void setUp() {
setUp(new LocalExecutionControl());
}
VirtualMachine vm() {
return connection.vm();
public void verifyLocal() throws ClassNotFoundException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
System.setProperty("LOCAL_CHECK", "TBD");
assertEquals(System.getProperty("LOCAL_CHECK"), "TBD");
assertEval("System.setProperty(\"LOCAL_CHECK\", \"local\")");
assertEquals(System.getProperty("LOCAL_CHECK"), "local");
}
void shutdown() {
if (connection != null) {
try {
connection.disposeVM();
} catch (VMDisconnectedException e) {
// Shutting down after the VM has gone away. This is
// not an error, and we just ignore it.
} catch (Throwable e) {
ec.debug(DBG_GEN, null, "disposeVM threw: " + e);
}
}
if (ec.execEnv.state() != null) { // If state has been set-up
try {
ec.execEnv.closeDown();
} catch (Throwable e) {
ec.debug(DBG_GEN, null, "state().closeDown() threw: " + e);
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册