1/* 2 * Copyright (c) 1998, 2016, 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 26/* 27 * This source code is provided to illustrate the usage of a given feature 28 * or technique and has been deliberately simplified. Additional steps 29 * required for a production-quality application, such as security checks, 30 * input validation and proper error handling, might not be present in 31 * this sample code. 32 */ 33 34 35package com.sun.tools.example.debug.tty; 36 37import com.sun.jdi.*; 38import com.sun.jdi.event.*; 39import com.sun.jdi.request.EventRequest; 40 41public class EventHandler implements Runnable { 42 43 EventNotifier notifier; 44 Thread thread; 45 volatile boolean connected = true; 46 boolean completed = false; 47 String shutdownMessageKey; 48 boolean stopOnVMStart; 49 50 EventHandler(EventNotifier notifier, boolean stopOnVMStart) { 51 this.notifier = notifier; 52 this.stopOnVMStart = stopOnVMStart; 53 this.thread = new Thread(this, "event-handler"); 54 this.thread.start(); 55 } 56 57 synchronized void shutdown() { 58 connected = false; // force run() loop termination 59 thread.interrupt(); 60 while (!completed) { 61 try {wait();} catch (InterruptedException exc) {} 62 } 63 } 64 65 @Override 66 public void run() { 67 EventQueue queue = Env.vm().eventQueue(); 68 while (connected) { 69 try { 70 EventSet eventSet = queue.remove(); 71 boolean resumeStoppedApp = false; 72 EventIterator it = eventSet.eventIterator(); 73 while (it.hasNext()) { 74 resumeStoppedApp |= !handleEvent(it.nextEvent()); 75 } 76 77 if (resumeStoppedApp) { 78 eventSet.resume(); 79 } else if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL) { 80 setCurrentThread(eventSet); 81 notifier.vmInterrupted(); 82 } 83 } catch (InterruptedException exc) { 84 // Do nothing. Any changes will be seen at top of loop. 85 } catch (VMDisconnectedException discExc) { 86 handleDisconnectedException(); 87 break; 88 } 89 } 90 synchronized (this) { 91 completed = true; 92 notifyAll(); 93 } 94 } 95 96 private boolean handleEvent(Event event) { 97 notifier.receivedEvent(event); 98 99 if (event instanceof ExceptionEvent) { 100 return exceptionEvent(event); 101 } else if (event instanceof BreakpointEvent) { 102 return breakpointEvent(event); 103 } else if (event instanceof WatchpointEvent) { 104 return fieldWatchEvent(event); 105 } else if (event instanceof StepEvent) { 106 return stepEvent(event); 107 } else if (event instanceof MethodEntryEvent) { 108 return methodEntryEvent(event); 109 } else if (event instanceof MethodExitEvent) { 110 return methodExitEvent(event); 111 } else if (event instanceof ClassPrepareEvent) { 112 return classPrepareEvent(event); 113 } else if (event instanceof ClassUnloadEvent) { 114 return classUnloadEvent(event); 115 } else if (event instanceof ThreadStartEvent) { 116 return threadStartEvent(event); 117 } else if (event instanceof ThreadDeathEvent) { 118 return threadDeathEvent(event); 119 } else if (event instanceof VMStartEvent) { 120 return vmStartEvent(event); 121 } else { 122 return handleExitEvent(event); 123 } 124 } 125 126 private boolean vmDied = false; 127 private boolean handleExitEvent(Event event) { 128 if (event instanceof VMDeathEvent) { 129 vmDied = true; 130 return vmDeathEvent(event); 131 } else if (event instanceof VMDisconnectEvent) { 132 connected = false; 133 if (!vmDied) { 134 vmDisconnectEvent(event); 135 } 136 /* 137 * Inform jdb command line processor that jdb is being shutdown. JDK-8154144. 138 */ 139 ((TTY)notifier).setShuttingDown(true); 140 Env.shutdown(shutdownMessageKey); 141 return false; 142 } else { 143 throw new InternalError(MessageOutput.format("Unexpected event type", 144 new Object[] {event.getClass()})); 145 } 146 } 147 148 synchronized void handleDisconnectedException() { 149 /* 150 * A VMDisconnectedException has happened while dealing with 151 * another event. We need to flush the event queue, dealing only 152 * with exit events (VMDeath, VMDisconnect) so that we terminate 153 * correctly. 154 */ 155 EventQueue queue = Env.vm().eventQueue(); 156 while (connected) { 157 try { 158 EventSet eventSet = queue.remove(); 159 EventIterator iter = eventSet.eventIterator(); 160 while (iter.hasNext()) { 161 handleExitEvent(iter.next()); 162 } 163 } catch (InterruptedException exc) { 164 // ignore 165 } catch (InternalError exc) { 166 // ignore 167 } 168 } 169 } 170 171 private ThreadReference eventThread(Event event) { 172 if (event instanceof ClassPrepareEvent) { 173 return ((ClassPrepareEvent)event).thread(); 174 } else if (event instanceof LocatableEvent) { 175 return ((LocatableEvent)event).thread(); 176 } else if (event instanceof ThreadStartEvent) { 177 return ((ThreadStartEvent)event).thread(); 178 } else if (event instanceof ThreadDeathEvent) { 179 return ((ThreadDeathEvent)event).thread(); 180 } else if (event instanceof VMStartEvent) { 181 return ((VMStartEvent)event).thread(); 182 } else { 183 return null; 184 } 185 } 186 187 private void setCurrentThread(EventSet set) { 188 ThreadReference thread; 189 if (set.size() > 0) { 190 /* 191 * If any event in the set has a thread associated with it, 192 * they all will, so just grab the first one. 193 */ 194 Event event = set.iterator().next(); // Is there a better way? 195 thread = eventThread(event); 196 } else { 197 thread = null; 198 } 199 setCurrentThread(thread); 200 } 201 202 private void setCurrentThread(ThreadReference thread) { 203 ThreadInfo.invalidateAll(); 204 ThreadInfo.setCurrentThread(thread); 205 } 206 207 private boolean vmStartEvent(Event event) { 208 VMStartEvent se = (VMStartEvent)event; 209 notifier.vmStartEvent(se); 210 return stopOnVMStart; 211 } 212 213 private boolean breakpointEvent(Event event) { 214 BreakpointEvent be = (BreakpointEvent)event; 215 notifier.breakpointEvent(be); 216 return true; 217 } 218 219 private boolean methodEntryEvent(Event event) { 220 MethodEntryEvent me = (MethodEntryEvent)event; 221 notifier.methodEntryEvent(me); 222 return true; 223 } 224 225 private boolean methodExitEvent(Event event) { 226 MethodExitEvent me = (MethodExitEvent)event; 227 return notifier.methodExitEvent(me); 228 } 229 230 private boolean fieldWatchEvent(Event event) { 231 WatchpointEvent fwe = (WatchpointEvent)event; 232 notifier.fieldWatchEvent(fwe); 233 return true; 234 } 235 236 private boolean stepEvent(Event event) { 237 StepEvent se = (StepEvent)event; 238 notifier.stepEvent(se); 239 return true; 240 } 241 242 private boolean classPrepareEvent(Event event) { 243 ClassPrepareEvent cle = (ClassPrepareEvent)event; 244 notifier.classPrepareEvent(cle); 245 246 if (!Env.specList.resolve(cle)) { 247 MessageOutput.lnprint("Stopping due to deferred breakpoint errors."); 248 return true; 249 } else { 250 return false; 251 } 252 } 253 254 private boolean classUnloadEvent(Event event) { 255 ClassUnloadEvent cue = (ClassUnloadEvent)event; 256 notifier.classUnloadEvent(cue); 257 return false; 258 } 259 260 private boolean exceptionEvent(Event event) { 261 ExceptionEvent ee = (ExceptionEvent)event; 262 notifier.exceptionEvent(ee); 263 return true; 264 } 265 266 private boolean threadDeathEvent(Event event) { 267 ThreadDeathEvent tee = (ThreadDeathEvent)event; 268 ThreadInfo.removeThread(tee.thread()); 269 return false; 270 } 271 272 private boolean threadStartEvent(Event event) { 273 ThreadStartEvent tse = (ThreadStartEvent)event; 274 ThreadInfo.addThread(tse.thread()); 275 notifier.threadStartEvent(tse); 276 return false; 277 } 278 279 public boolean vmDeathEvent(Event event) { 280 shutdownMessageKey = "The application exited"; 281 notifier.vmDeathEvent((VMDeathEvent)event); 282 return false; 283 } 284 285 public boolean vmDisconnectEvent(Event event) { 286 shutdownMessageKey = "The application has been disconnected"; 287 notifier.vmDisconnectEvent((VMDisconnectEvent)event); 288 return false; 289 } 290} 291