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