| /* |
| * Copyright (c) 1998, 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. |
| */ |
| |
| /* |
| * This source code is provided to illustrate the usage of a given feature |
| * or technique and has been deliberately simplified. Additional steps |
| * required for a production-quality application, such as security checks, |
| * input validation and proper error handling, might not be present in |
| * this sample code. |
| */ |
| |
| |
| package com.sun.tools.example.debug.tty; |
| |
| import com.sun.jdi.*; |
| import com.sun.jdi.event.*; |
| import com.sun.jdi.request.EventRequest; |
| |
| public class EventHandler implements Runnable { |
| |
| EventNotifier notifier; |
| Thread thread; |
| volatile boolean connected = true; |
| boolean completed = false; |
| String shutdownMessageKey; |
| boolean stopOnVMStart; |
| |
| EventHandler(EventNotifier notifier, boolean stopOnVMStart) { |
| this.notifier = notifier; |
| this.stopOnVMStart = stopOnVMStart; |
| this.thread = new Thread(this, "event-handler"); |
| this.thread.start(); |
| } |
| |
| synchronized void shutdown() { |
| connected = false; // force run() loop termination |
| thread.interrupt(); |
| while (!completed) { |
| try {wait();} catch (InterruptedException exc) {} |
| } |
| } |
| |
| @Override |
| public void run() { |
| EventQueue queue = Env.vm().eventQueue(); |
| while (connected) { |
| try { |
| EventSet eventSet = queue.remove(); |
| boolean resumeStoppedApp = false; |
| EventIterator it = eventSet.eventIterator(); |
| while (it.hasNext()) { |
| resumeStoppedApp |= !handleEvent(it.nextEvent()); |
| } |
| |
| if (resumeStoppedApp) { |
| eventSet.resume(); |
| } else if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL) { |
| setCurrentThread(eventSet); |
| notifier.vmInterrupted(); |
| } |
| } catch (InterruptedException exc) { |
| // Do nothing. Any changes will be seen at top of loop. |
| } catch (VMDisconnectedException discExc) { |
| handleDisconnectedException(); |
| break; |
| } |
| } |
| synchronized (this) { |
| completed = true; |
| notifyAll(); |
| } |
| } |
| |
| private boolean handleEvent(Event event) { |
| notifier.receivedEvent(event); |
| |
| if (event instanceof ExceptionEvent) { |
| return exceptionEvent(event); |
| } else if (event instanceof BreakpointEvent) { |
| return breakpointEvent(event); |
| } else if (event instanceof WatchpointEvent) { |
| return fieldWatchEvent(event); |
| } else if (event instanceof StepEvent) { |
| return stepEvent(event); |
| } else if (event instanceof MethodEntryEvent) { |
| return methodEntryEvent(event); |
| } else if (event instanceof MethodExitEvent) { |
| return methodExitEvent(event); |
| } else if (event instanceof ClassPrepareEvent) { |
| return classPrepareEvent(event); |
| } else if (event instanceof ClassUnloadEvent) { |
| return classUnloadEvent(event); |
| } else if (event instanceof ThreadStartEvent) { |
| return threadStartEvent(event); |
| } else if (event instanceof ThreadDeathEvent) { |
| return threadDeathEvent(event); |
| } else if (event instanceof VMStartEvent) { |
| return vmStartEvent(event); |
| } else { |
| return handleExitEvent(event); |
| } |
| } |
| |
| private boolean vmDied = false; |
| private boolean handleExitEvent(Event event) { |
| if (event instanceof VMDeathEvent) { |
| vmDied = true; |
| return vmDeathEvent(event); |
| } else if (event instanceof VMDisconnectEvent) { |
| connected = false; |
| if (!vmDied) { |
| vmDisconnectEvent(event); |
| } |
| /* |
| * Inform jdb command line processor that jdb is being shutdown. JDK-8154144. |
| */ |
| ((TTY)notifier).setShuttingDown(true); |
| Env.shutdown(shutdownMessageKey); |
| return false; |
| } else { |
| throw new InternalError(MessageOutput.format("Unexpected event type", |
| new Object[] {event.getClass()})); |
| } |
| } |
| |
| 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(); |
| while (connected) { |
| try { |
| EventSet eventSet = queue.remove(); |
| EventIterator iter = eventSet.eventIterator(); |
| while (iter.hasNext()) { |
| handleExitEvent(iter.next()); |
| } |
| } catch (InterruptedException exc) { |
| // ignore |
| } catch (InternalError exc) { |
| // ignore |
| } |
| } |
| } |
| |
| private ThreadReference eventThread(Event event) { |
| if (event instanceof ClassPrepareEvent) { |
| return ((ClassPrepareEvent)event).thread(); |
| } else if (event instanceof LocatableEvent) { |
| return ((LocatableEvent)event).thread(); |
| } else if (event instanceof ThreadStartEvent) { |
| return ((ThreadStartEvent)event).thread(); |
| } else if (event instanceof ThreadDeathEvent) { |
| return ((ThreadDeathEvent)event).thread(); |
| } else if (event instanceof VMStartEvent) { |
| return ((VMStartEvent)event).thread(); |
| } else { |
| return null; |
| } |
| } |
| |
| private void setCurrentThread(EventSet set) { |
| ThreadReference thread; |
| if (set.size() > 0) { |
| /* |
| * If any event in the set has a thread associated with it, |
| * they all will, so just grab the first one. |
| */ |
| Event event = set.iterator().next(); // Is there a better way? |
| thread = eventThread(event); |
| } else { |
| thread = null; |
| } |
| setCurrentThread(thread); |
| } |
| |
| private void setCurrentThread(ThreadReference thread) { |
| ThreadInfo.invalidateAll(); |
| ThreadInfo.setCurrentThread(thread); |
| } |
| |
| private boolean vmStartEvent(Event event) { |
| VMStartEvent se = (VMStartEvent)event; |
| notifier.vmStartEvent(se); |
| return stopOnVMStart; |
| } |
| |
| private boolean breakpointEvent(Event event) { |
| BreakpointEvent be = (BreakpointEvent)event; |
| notifier.breakpointEvent(be); |
| return true; |
| } |
| |
| private boolean methodEntryEvent(Event event) { |
| MethodEntryEvent me = (MethodEntryEvent)event; |
| notifier.methodEntryEvent(me); |
| return true; |
| } |
| |
| private boolean methodExitEvent(Event event) { |
| MethodExitEvent me = (MethodExitEvent)event; |
| return notifier.methodExitEvent(me); |
| } |
| |
| private boolean fieldWatchEvent(Event event) { |
| WatchpointEvent fwe = (WatchpointEvent)event; |
| notifier.fieldWatchEvent(fwe); |
| return true; |
| } |
| |
| private boolean stepEvent(Event event) { |
| StepEvent se = (StepEvent)event; |
| notifier.stepEvent(se); |
| return true; |
| } |
| |
| private boolean classPrepareEvent(Event event) { |
| ClassPrepareEvent cle = (ClassPrepareEvent)event; |
| notifier.classPrepareEvent(cle); |
| |
| if (!Env.specList.resolve(cle)) { |
| MessageOutput.lnprint("Stopping due to deferred breakpoint errors."); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private boolean classUnloadEvent(Event event) { |
| ClassUnloadEvent cue = (ClassUnloadEvent)event; |
| notifier.classUnloadEvent(cue); |
| return false; |
| } |
| |
| private boolean exceptionEvent(Event event) { |
| ExceptionEvent ee = (ExceptionEvent)event; |
| notifier.exceptionEvent(ee); |
| return true; |
| } |
| |
| private boolean threadDeathEvent(Event event) { |
| ThreadDeathEvent tee = (ThreadDeathEvent)event; |
| ThreadInfo.removeThread(tee.thread()); |
| return false; |
| } |
| |
| private boolean threadStartEvent(Event event) { |
| ThreadStartEvent tse = (ThreadStartEvent)event; |
| ThreadInfo.addThread(tse.thread()); |
| notifier.threadStartEvent(tse); |
| return false; |
| } |
| |
| public boolean vmDeathEvent(Event event) { |
| shutdownMessageKey = "The application exited"; |
| notifier.vmDeathEvent((VMDeathEvent)event); |
| return false; |
| } |
| |
| public boolean vmDisconnectEvent(Event event) { |
| shutdownMessageKey = "The application has been disconnected"; |
| notifier.vmDisconnectEvent((VMDisconnectEvent)event); |
| return false; |
| } |
| } |