1/* 2 * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.jshell.execution; 27 28import java.util.function.Consumer; 29import com.sun.jdi.*; 30import com.sun.jdi.event.*; 31 32/** 33 * Handler of Java Debug Interface events. 34 * Adapted from jdb EventHandler. 35 * Only exit and disconnect events processed. 36 */ 37class JdiEventHandler implements Runnable { 38 39 private final Thread thread; 40 private volatile boolean connected = true; 41 private boolean completed = false; 42 private final VirtualMachine vm; 43 private final Consumer<String> reportVMExit; 44 45 /** 46 * Creates an event handler. Start with {@code start()}. 47 * 48 * @param vm the virtual machine for which to handle events 49 * @param reportVMExit callback to report exit/disconnect 50 * (passed true if the VM has died) 51 */ 52 JdiEventHandler(VirtualMachine vm, Consumer<String> reportVMExit) { 53 this.vm = vm; 54 this.reportVMExit = reportVMExit; 55 this.thread = new Thread(this, "event-handler"); 56 this.thread.setDaemon(true); 57 } 58 59 /** 60 * Starts the event handler. 61 */ 62 void start() { 63 thread.start(); 64 } 65 66 synchronized void shutdown() { 67 connected = false; // force run() loop termination 68 thread.interrupt(); 69 while (!completed) { 70 try {wait();} catch (InterruptedException exc) {} 71 } 72 } 73 74 @Override 75 public void run() { 76 EventQueue queue = vm.eventQueue(); 77 while (connected) { 78 try { 79 EventSet eventSet = queue.remove(); 80 boolean resumeStoppedApp = false; 81 EventIterator it = eventSet.eventIterator(); 82 while (it.hasNext()) { 83 resumeStoppedApp |= handleEvent(it.nextEvent()); 84 } 85 86 if (resumeStoppedApp) { 87 eventSet.resume(); 88 } 89 } catch (InterruptedException exc) { 90 // Do nothing. Any changes will be seen at top of loop. 91 } catch (VMDisconnectedException discExc) { 92 handleDisconnectedException(); 93 break; 94 } 95 } 96 synchronized (this) { 97 completed = true; 98 notifyAll(); 99 } 100 } 101 102 private boolean handleEvent(Event event) { 103 handleExitEvent(event); 104 return true; 105 } 106 107 private void handleExitEvent(Event event) { 108 if (event instanceof VMDeathEvent) { 109 reportVMExit.accept("VM Died"); 110 } else if (event instanceof VMDisconnectEvent) { 111 connected = false; 112 reportVMExit.accept("VM Disconnected"); 113 } else { 114 // ignore everything else 115 } 116 } 117 118 private synchronized void handleDisconnectedException() { 119 /* 120 * A VMDisconnectedException has happened while dealing with 121 * another event. We need to flush the event queue, dealing only 122 * with exit events (VMDeath, VMDisconnect) so that we terminate 123 * correctly. 124 */ 125 EventQueue queue = vm.eventQueue(); 126 while (connected) { 127 try { 128 EventSet eventSet = queue.remove(); 129 EventIterator iter = eventSet.eventIterator(); 130 while (iter.hasNext()) { 131 handleExitEvent(iter.next()); 132 } 133 } catch (InterruptedException exc) { 134 // ignore 135 } catch (InternalError exc) { 136 // ignore 137 } 138 } 139 } 140} 141