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 
  26 package jdk.internal.jshell.jdi;
  27 
  28 import java.util.function.Consumer;
  29 import com.sun.jdi.*;
  30 import com.sun.jdi.event.*;
  31 
  32 /**
  33  * Handler of Java Debug Interface events.
  34  * Adapted from jdb EventHandler; Handling of events not used by JShell stubbed out.
  35  */
  36 class JDIEventHandler implements Runnable {
  37 
  38     private final Thread thread;
  39     private volatile boolean connected = true;
  40     private boolean completed = false;
  41     private final VirtualMachine vm;
  42     private final Consumer<Boolean> reportVMExit;
  43 
  44     JDIEventHandler(VirtualMachine vm, Consumer<Boolean> reportVMExit) {
  45         this.vm = vm;
  46         this.reportVMExit = reportVMExit;
  47         this.thread = new Thread(this, "event-handler");
  48     }
  49 
  50     void start() {
  51         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     @Override
  63     public void run() {
  64         EventQueue queue = vm.eventQueue();
  65         while (connected) {
  66             try {
  67                 EventSet eventSet = queue.remove();
  68                 boolean resumeStoppedApp = false;
  69                 EventIterator it = eventSet.eventIterator();
  70                 while (it.hasNext()) {
  71                     resumeStoppedApp |= handleEvent(it.nextEvent());
  72                 }
  73 
  74                 if (resumeStoppedApp) {
  75                     eventSet.resume();
  76                 }
  77             } catch (InterruptedException exc) {
  78                 // Do nothing. Any changes will be seen at top of loop.
  79             } catch (VMDisconnectedException discExc) {
  80                 handleDisconnectedException();
  81                 break;
  82             }
  83         }
  84         synchronized (this) {
  85             completed = true;
  86             notifyAll();
  87         }
  88     }
  89 
  90     private boolean handleEvent(Event event) {
  91         if (event instanceof ExceptionEvent) {
  92             exceptionEvent(event);
  93         } else if (event instanceof WatchpointEvent) {
  94             fieldWatchEvent(event);
  95         } else if (event instanceof MethodEntryEvent) {
  96             methodEntryEvent(event);
  97         } else if (event instanceof MethodExitEvent) {
  98             methodExitEvent(event);
  99         } else if (event instanceof ClassPrepareEvent) {
 100             classPrepareEvent(event);
 101         } else if (event instanceof ThreadStartEvent) {
 102             threadStartEvent(event);
 103         } else if (event instanceof ThreadDeathEvent) {
 104             threadDeathEvent(event);
 105         } else if (event instanceof VMStartEvent) {
 106             vmStartEvent(event);
 107             return true;
 108         } else {
 109             handleExitEvent(event);
 110         }
 111         return true;
 112     }
 113 
 114     private boolean vmDied = false;
 115 
 116     private void handleExitEvent(Event event) {
 117         if (event instanceof VMDeathEvent) {
 118             vmDied = true;
 119         } else if (event instanceof VMDisconnectEvent) {
 120             connected = false;
 121         } else {
 122             throw new InternalError("Unexpected event type: " +
 123                     event.getClass());
 124         }
 125         reportVMExit.accept(vmDied);
 126     }
 127 
 128     private synchronized void handleDisconnectedException() {
 129         /*
 130          * A VMDisconnectedException has happened while dealing with
 131          * another event. We need to flush the event queue, dealing only
 132          * with exit events (VMDeath, VMDisconnect) so that we terminate
 133          * correctly.
 134          */
 135         EventQueue queue = vm.eventQueue();
 136         while (connected) {
 137             try {
 138                 EventSet eventSet = queue.remove();
 139                 EventIterator iter = eventSet.eventIterator();
 140                 while (iter.hasNext()) {
 141                     handleExitEvent(iter.next());
 142                 }
 143             } catch (InterruptedException exc) {
 144                 // ignore
 145             } catch (InternalError exc) {
 146                 // ignore
 147             }
 148         }
 149     }
 150 
 151     private void vmStartEvent(Event event)  {
 152         VMStartEvent se = (VMStartEvent)event;
 153     }
 154 
 155     private void methodEntryEvent(Event event)  {
 156         MethodEntryEvent me = (MethodEntryEvent)event;
 157     }
 158 
 159     private void methodExitEvent(Event event)  {
 160         MethodExitEvent me = (MethodExitEvent)event;
 161     }
 162 
 163     private void fieldWatchEvent(Event event)  {
 164         WatchpointEvent fwe = (WatchpointEvent)event;
 165     }
 166 
 167     private void classPrepareEvent(Event event)  {
 168         ClassPrepareEvent cle = (ClassPrepareEvent)event;
 169     }
 170 
 171     private void exceptionEvent(Event event) {
 172         ExceptionEvent ee = (ExceptionEvent)event;
 173     }
 174 
 175     private void threadDeathEvent(Event event) {
 176         ThreadDeathEvent tee = (ThreadDeathEvent)event;
 177     }
 178 
 179     private void threadStartEvent(Event event) {
 180         ThreadStartEvent tse = (ThreadStartEvent)event;
 181     }
 182 }