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