1 /* 2 * Copyright (c) 1998, 2011, 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 35 package com.sun.tools.example.debug.gui; 36 37 import java.io.*; 38 import java.util.*; 39 40 import com.sun.jdi.*; 41 import com.sun.tools.example.debug.event.*; 42 import com.sun.tools.example.debug.bdi.*; 43 44 public class ContextManager { 45 46 private ClassManager classManager; 47 private ExecutionManager runtime; 48 49 private String mainClassName; 50 private String vmArguments; 51 private String commandArguments; 52 private String remotePort; 53 54 private ThreadReference currentThread; 55 56 private boolean verbose; 57 58 private ArrayList<ContextListener> contextListeners = new ArrayList<ContextListener>(); 59 60 public ContextManager(Environment env) { 61 classManager = env.getClassManager(); 62 runtime = env.getExecutionManager(); 63 mainClassName = ""; 64 vmArguments = ""; 65 commandArguments = ""; 66 currentThread = null; 67 68 ContextManagerListener listener = new ContextManagerListener(); 69 runtime.addJDIListener(listener); 70 runtime.addSessionListener(listener); 71 } 72 73 // Program execution defaults. 74 75 //### Should there be change listeners for these? 76 //### They would be needed if we expected a dialog to be 77 //### synchronized with command input while it was open. 78 79 public String getMainClassName() { 80 return mainClassName; 81 } 82 83 public void setMainClassName(String mainClassName) { 84 this.mainClassName = mainClassName; 85 } 86 87 public String getVmArguments() { 88 return processClasspathDefaults(vmArguments); 89 } 90 91 public void setVmArguments(String vmArguments) { 92 this.vmArguments = vmArguments; 93 } 94 95 public String getProgramArguments() { 96 return commandArguments; 97 } 98 99 public void setProgramArguments(String commandArguments) { 100 this.commandArguments = commandArguments; 101 } 102 103 public String getRemotePort() { 104 return remotePort; 105 } 106 107 public void setRemotePort(String remotePort) { 108 this.remotePort = remotePort; 109 110 } 111 112 113 // Miscellaneous debugger session preferences. 114 115 public boolean getVerboseFlag() { 116 return verbose; 117 } 118 119 public void setVerboseFlag(boolean verbose) { 120 this.verbose = verbose; 121 } 122 123 124 // Thread focus. 125 126 public ThreadReference getCurrentThread() { 127 return currentThread; 128 } 129 130 public void setCurrentThread(ThreadReference t) { 131 if (t != currentThread) { 132 currentThread = t; 133 notifyCurrentThreadChanged(t); 134 } 135 } 136 137 public void setCurrentThreadInvalidate(ThreadReference t) { 138 currentThread = t; 139 notifyCurrentFrameChanged(runtime.threadInfo(t), 140 0, true); 141 } 142 143 public void invalidateCurrentThread() { 144 notifyCurrentFrameChanged(null, 0, true); 145 } 146 147 148 // If a view is displaying the current thread, it may 149 // choose to indicate which frame is current in the 150 // sense of the command-line UI. It may also "warp" the 151 // selection to that frame when changed by an 'up' or 'down' 152 // command. Hence, a notifier is provided. 153 154 /****** 155 public int getCurrentFrameIndex() { 156 return getCurrentFrameIndex(currentThreadInfo); 157 } 158 ******/ 159 160 public int getCurrentFrameIndex(ThreadReference t) { 161 return getCurrentFrameIndex(runtime.threadInfo(t)); 162 } 163 164 //### Used in StackTraceTool. 165 public int getCurrentFrameIndex(ThreadInfo tinfo) { 166 if (tinfo == null) { 167 return 0; 168 } 169 Integer currentFrame = (Integer)tinfo.getUserObject(); 170 if (currentFrame == null) { 171 return 0; 172 } else { 173 return currentFrame.intValue(); 174 } 175 } 176 177 public int moveCurrentFrameIndex(ThreadReference t, int count) throws VMNotInterruptedException { 178 return setCurrentFrameIndex(t,count, true); 179 } 180 181 public int setCurrentFrameIndex(ThreadReference t, int newIndex) throws VMNotInterruptedException { 182 return setCurrentFrameIndex(t, newIndex, false); 183 } 184 185 public int setCurrentFrameIndex(int newIndex) throws VMNotInterruptedException { 186 if (currentThread == null) { 187 return 0; 188 } else { 189 return setCurrentFrameIndex(currentThread, newIndex, false); 190 } 191 } 192 193 private int setCurrentFrameIndex(ThreadReference t, int x, boolean relative) throws VMNotInterruptedException { 194 boolean sameThread = t.equals(currentThread); 195 ThreadInfo tinfo = runtime.threadInfo(t); 196 if (tinfo == null) { 197 return 0; 198 } 199 int maxIndex = tinfo.getFrameCount()-1; 200 int oldIndex = getCurrentFrameIndex(tinfo); 201 int newIndex = relative? oldIndex + x : x; 202 if (newIndex > maxIndex) { 203 newIndex = maxIndex; 204 } else if (newIndex < 0) { 205 newIndex = 0; 206 } 207 if (!sameThread || newIndex != oldIndex) { // don't recurse 208 setCurrentFrameIndex(tinfo, newIndex); 209 } 210 return newIndex - oldIndex; 211 } 212 213 private void setCurrentFrameIndex(ThreadInfo tinfo, int index) { 214 tinfo.setUserObject(index); 215 //### In fact, the value may not have changed at this point. 216 //### We need to signal that the user attempted to change it, 217 //### however, so that the selection can be "warped" to the 218 //### current location. 219 notifyCurrentFrameChanged(tinfo.thread(), index); 220 } 221 222 public StackFrame getCurrentFrame() throws VMNotInterruptedException { 223 return getCurrentFrame(runtime.threadInfo(currentThread)); 224 } 225 226 public StackFrame getCurrentFrame(ThreadReference t) throws VMNotInterruptedException { 227 return getCurrentFrame(runtime.threadInfo(t)); 228 } 229 230 public StackFrame getCurrentFrame(ThreadInfo tinfo) throws VMNotInterruptedException { 231 int index = getCurrentFrameIndex(tinfo); 232 try { 233 // It is possible, though unlikely, that the VM was interrupted 234 // before the thread created its Java stack. 235 return tinfo.getFrame(index); 236 } catch (FrameIndexOutOfBoundsException e) { 237 return null; 238 } 239 } 240 241 public void addContextListener(ContextListener cl) { 242 contextListeners.add(cl); 243 } 244 245 public void removeContextListener(ContextListener cl) { 246 contextListeners.remove(cl); 247 } 248 249 //### These notifiers are fired only in response to USER-INITIATED changes 250 //### to the current thread and current frame. When the current thread is set automatically 251 //### after a breakpoint hit or step completion, no event is generated. Instead, 252 //### interested parties are expected to listen for the BreakpointHit and StepCompleted 253 //### events. This convention is unclean, and I believe that it reflects a defect in 254 //### in the current architecture. Unfortunately, however, we cannot guarantee the 255 //### order in which various listeners receive a given event, and the handlers for 256 //### the very same events that cause automatic changes to the current thread may also 257 //### need to know the current thread. 258 259 private void notifyCurrentThreadChanged(ThreadReference t) { 260 ThreadInfo tinfo = null; 261 int index = 0; 262 if (t != null) { 263 tinfo = runtime.threadInfo(t); 264 index = getCurrentFrameIndex(tinfo); 265 } 266 notifyCurrentFrameChanged(tinfo, index, false); 267 } 268 269 private void notifyCurrentFrameChanged(ThreadReference t, int index) { 270 notifyCurrentFrameChanged(runtime.threadInfo(t), 271 index, false); 272 } 273 274 private void notifyCurrentFrameChanged(ThreadInfo tinfo, int index, 275 boolean invalidate) { 276 ArrayList<ContextListener> l = new ArrayList<ContextListener>(contextListeners); 277 CurrentFrameChangedEvent evt = 278 new CurrentFrameChangedEvent(this, tinfo, index, invalidate); 279 for (int i = 0; i < l.size(); i++) { 280 l.get(i).currentFrameChanged(evt); 281 } 282 } 283 284 private class ContextManagerListener extends JDIAdapter 285 implements SessionListener, JDIListener { 286 287 // SessionListener 288 289 @Override 290 public void sessionStart(EventObject e) { 291 invalidateCurrentThread(); 292 } 293 294 @Override 295 public void sessionInterrupt(EventObject e) { 296 setCurrentThreadInvalidate(currentThread); 297 } 298 299 @Override 300 public void sessionContinue(EventObject e) { 301 invalidateCurrentThread(); 302 } 303 304 // JDIListener 305 306 @Override 307 public void locationTrigger(LocationTriggerEventSet e) { 308 setCurrentThreadInvalidate(e.getThread()); 309 } 310 311 @Override 312 public void exception(ExceptionEventSet e) { 313 setCurrentThreadInvalidate(e.getThread()); 314 } 315 316 @Override 317 public void vmDisconnect(VMDisconnectEventSet e) { 318 invalidateCurrentThread(); 319 } 320 321 } 322 323 324 /** 325 * Add a -classpath argument to the arguments passed to the exec'ed 326 * VM with the contents of CLASSPATH environment variable, 327 * if -classpath was not already specified. 328 * 329 * @param javaArgs the arguments to the VM being exec'd that 330 * potentially has a user specified -classpath argument. 331 * @return a javaArgs whose -classpath option has been added 332 */ 333 334 private String processClasspathDefaults(String javaArgs) { 335 if (javaArgs.indexOf("-classpath ") == -1) { 336 StringBuilder munged = new StringBuilder(javaArgs); 337 SearchPath classpath = classManager.getClassPath(); 338 if (classpath.isEmpty()) { 339 String envcp = System.getProperty("env.class.path"); 340 if ((envcp != null) && (envcp.length() > 0)) { 341 munged.append(" -classpath " + envcp); 342 } 343 } else { 344 munged.append(" -classpath " + classpath.asString()); 345 } 346 return munged.toString(); 347 } else { 348 return javaArgs; 349 } 350 } 351 352 private String appendPath(String path1, String path2) { 353 if (path1 == null || path1.length() == 0) { 354 return path2 == null ? "." : path2; 355 } else if (path2 == null || path2.length() == 0) { 356 return path1; 357 } else { 358 return path1 + File.pathSeparator + path2; 359 } 360 } 361 362 }