KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > debug > core > hcr > JavaHotCodeReplaceManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.debug.core.hcr;
12
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.Collections JavaDoc;
17 import java.util.Date JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import org.eclipse.core.resources.IFile;
24 import org.eclipse.core.resources.IMarker;
25 import org.eclipse.core.resources.IProject;
26 import org.eclipse.core.resources.IResource;
27 import org.eclipse.core.resources.IResourceChangeEvent;
28 import org.eclipse.core.resources.IResourceChangeListener;
29 import org.eclipse.core.resources.IResourceDelta;
30 import org.eclipse.core.resources.IResourceDeltaVisitor;
31 import org.eclipse.core.resources.IWorkspace;
32 import org.eclipse.core.resources.IncrementalProjectBuilder;
33 import org.eclipse.core.resources.ResourcesPlugin;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.IAdaptable;
36 import org.eclipse.core.runtime.IPath;
37 import org.eclipse.core.runtime.IStatus;
38 import org.eclipse.core.runtime.ListenerList;
39 import org.eclipse.core.runtime.MultiStatus;
40 import org.eclipse.core.runtime.Path;
41 import org.eclipse.core.runtime.Status;
42 import org.eclipse.debug.core.DebugEvent;
43 import org.eclipse.debug.core.DebugException;
44 import org.eclipse.debug.core.DebugPlugin;
45 import org.eclipse.debug.core.IDebugEventSetListener;
46 import org.eclipse.debug.core.ILaunch;
47 import org.eclipse.debug.core.ILaunchListener;
48 import org.eclipse.debug.core.ILaunchManager;
49 import org.eclipse.debug.core.model.IDebugTarget;
50 import org.eclipse.debug.core.model.ISourceLocator;
51 import org.eclipse.debug.core.model.IThread;
52 import org.eclipse.jdt.core.ICompilationUnit;
53 import org.eclipse.jdt.core.IJavaElement;
54 import org.eclipse.jdt.core.IJavaModelMarker;
55 import org.eclipse.jdt.core.IJavaProject;
56 import org.eclipse.jdt.core.IMethod;
57 import org.eclipse.jdt.core.IType;
58 import org.eclipse.jdt.core.JavaCore;
59 import org.eclipse.jdt.core.JavaModelException;
60 import org.eclipse.jdt.core.Signature;
61 import org.eclipse.jdt.core.ToolFactory;
62 import org.eclipse.jdt.core.util.IClassFileReader;
63 import org.eclipse.jdt.core.util.ISourceAttribute;
64 import org.eclipse.jdt.debug.core.IJavaDebugTarget;
65 import org.eclipse.jdt.debug.core.IJavaHotCodeReplaceListener;
66 import org.eclipse.jdt.debug.core.IJavaStackFrame;
67 import org.eclipse.jdt.debug.core.IJavaThread;
68 import org.eclipse.jdt.debug.core.JDIDebugModel;
69 import org.eclipse.jdt.internal.core.util.Util;
70 import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
71 import org.eclipse.jdt.internal.debug.core.JavaDebugUtils;
72 import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
73 import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
74 import org.eclipse.jdt.internal.debug.core.model.JDIThread;
75
76 import com.ibm.icu.text.MessageFormat;
77 import com.sun.jdi.IncompatibleThreadStateException;
78 import com.sun.jdi.ReferenceType;
79 import com.sun.jdi.VirtualMachine;
80
81 /**
82  * The hot code replace manager listens for changes to
83  * class files and notifies running debug targets of the changes.
84  * <p>
85  * Currently, replacing .jar files has no effect on running targets.
86  */

87 public class JavaHotCodeReplaceManager implements IResourceChangeListener, ILaunchListener, IDebugEventSetListener {
88     /**
89      * Singleton
90      */

91     private static JavaHotCodeReplaceManager fgInstance= null;
92     /**
93      * The class file extension
94      */

95     private static final String JavaDoc CLASS_FILE_EXTENSION= "class"; //$NON-NLS-1$
96

97     /**
98      * The list of <code>IJavaHotCodeReplaceListeners</code> which this hot code replace
99      * manager will notify about hot code replace attempts.
100      */

101     private ListenerList fHotCodeReplaceListeners= new ListenerList();
102     
103     /**
104      * The lists of hot swap targets which support HCR and those which don't
105      */

106     private ArrayList JavaDoc fHotSwapTargets= new ArrayList JavaDoc(1);
107     private ArrayList JavaDoc fNoHotSwapTargets= new ArrayList JavaDoc(1);
108     
109     /**
110      * A mapping of the last time projects were built.
111      * <ol>
112      * <li>key: project (IProject)</li>
113      * <li>value: build date (ProjectBuildTime)</li>
114      * </ol>
115      */

116     private Map JavaDoc fProjectBuildTimes= new HashMap JavaDoc();
117     private static Date JavaDoc fStartupDate= new Date JavaDoc();
118     
119     /**
120      * Cache of compilation unit deltas renewed on each HCR attempt.
121      */

122     private Map JavaDoc fDeltaCache = new HashMap JavaDoc();
123     
124     /**
125      * Utility object used for tracking build times of projects.
126      * The HCR manager receives notification of builds AFTER
127      * the build has occurred but BEFORE the classfile
128      * resource changed deltas are fired. Thus, when the
129      * current build time is set, we need to hang onto
130      * the last build time so that we can use the last build
131      * time for comparing changes to compilation units (for smart
132      * drop to frame).
133      */

134     class ProjectBuildTime {
135         private Date JavaDoc fCurrentDate= new Date JavaDoc();
136         private Date JavaDoc fPreviousDate= new Date JavaDoc();
137         
138         public void setCurrentBuildDate(Date JavaDoc date) {
139             fPreviousDate= fCurrentDate;
140             fCurrentDate= date;
141         }
142         
143         public void setLastBuildDate(Date JavaDoc date) {
144             fPreviousDate= date;
145             if (fPreviousDate.getTime() > fCurrentDate.getTime()) {
146                 // If the previous date is set later than the current
147
// date, move the current date up to the previous.
148
fCurrentDate= fPreviousDate;
149             }
150         }
151         
152         /**
153          * Returns the last build time
154          */

155         public Date JavaDoc getLastBuildDate() {
156             return fPreviousDate;
157         }
158     }
159     
160     /**
161      * Visitor for resource deltas.
162      */

163     protected ChangedClassFilesVisitor fClassfileVisitor = new ChangedClassFilesVisitor();
164     
165     /**
166      * Creates a new HCR manager
167      */

168     private JavaHotCodeReplaceManager() {
169     }
170     /**
171      * Returns the singleton HCR manager
172      */

173     public static synchronized JavaHotCodeReplaceManager getDefault() {
174         if (fgInstance == null) {
175             fgInstance= new JavaHotCodeReplaceManager();
176         }
177         return fgInstance;
178     }
179     /**
180      * Registers this HCR manager as a resource change listener. This method
181      * is called by the JDI debug model plugin on startup.
182      */

183     public void startup() {
184         DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
185         DebugPlugin.getDefault().addDebugEventListener(this);
186     }
187     
188     /**
189      * Deregisters this HCR manager as a resource change listener. Removes all hot
190      * code replace listeners. This method is called by the JDI debug model plugin
191      * on shutdown.
192      */

193     public void shutdown() {
194         DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
195         DebugPlugin.getDefault().removeDebugEventListener(this);
196         getWorkspace().removeResourceChangeListener(this);
197         fHotCodeReplaceListeners = new ListenerList();
198         fHotSwapTargets= null;
199         fNoHotSwapTargets= null;
200     }
201     /**
202      * Returns the workspace.
203      */

204     protected IWorkspace getWorkspace() {
205         return ResourcesPlugin.getWorkspace();
206     }
207     
208     /**
209      * Returns the launch manager.
210      */

211     protected ILaunchManager getLaunchManager() {
212         return DebugPlugin.getDefault().getLaunchManager();
213     }
214     /**
215      * @see IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
216      */

217     public void resourceChanged(IResourceChangeEvent event) {
218         List JavaDoc projects= getBuiltProjects(event);
219         if (!projects.isEmpty()) {
220             updateProjectBuildTime(projects);
221         }
222         if (fHotSwapTargets.isEmpty() && fNoHotSwapTargets.isEmpty()) {
223             // If there are no targets to notify, only update the build times.
224
return;
225         }
226         ChangedClassFilesVisitor visitor = getChangedClassFiles(event);
227         if (visitor != null) {
228             List JavaDoc resources = visitor.getChangedClassFiles();
229             List JavaDoc names = visitor.getQualifiedNamesList();
230             if (!resources.isEmpty()) {
231                 notifyTargets(resources, names);
232             }
233         }
234     }
235     
236     /**
237      * Returns all projects which this event says may have been built.
238      */

239     protected List JavaDoc getBuiltProjects(IResourceChangeEvent event) {
240         IResourceDelta delta= event.getDelta();
241         if (event.getType() != IResourceChangeEvent.POST_BUILD || delta == null || event.getBuildKind() == 0) {
242             return Collections.EMPTY_LIST;
243         }
244         if (event.getBuildKind() == IncrementalProjectBuilder.AUTO_BUILD && !ResourcesPlugin.getWorkspace().isAutoBuilding()) {
245             // If this is an auto build and the workspace is not autobuilding,
246
// no projects will actually be compiled.
247
return Collections.EMPTY_LIST;
248         }
249         Object JavaDoc source = event.getSource();
250         if (source instanceof IProject) {
251             List JavaDoc list= new ArrayList JavaDoc();
252             list.add(source);
253             return list;
254         } else if (source instanceof IWorkspace){
255             IProject[] allProjects = ((IWorkspace) source).getRoot().getProjects();
256             return Arrays.asList(allProjects);
257         }
258         return Collections.EMPTY_LIST;
259     }
260     
261     /**
262      * If the given event contains a build notification, update the
263      * last build time of the corresponding project
264      */

265     private void updateProjectBuildTime(List JavaDoc projects) {
266         Iterator JavaDoc iter= projects.iterator();
267         IProject project= null;
268         Date JavaDoc currentDate= new Date JavaDoc();
269         ProjectBuildTime buildTime= null;
270         while (iter.hasNext()) {
271             project= (IProject) iter.next();
272             buildTime= (ProjectBuildTime)fProjectBuildTimes.get(project);
273             if (buildTime == null) {
274                 buildTime= new ProjectBuildTime();
275                 fProjectBuildTimes.put(project, buildTime);
276             }
277             buildTime.setCurrentBuildDate(currentDate);
278         }
279     }
280     
281     /**
282      * Returns the last known build time for the given project.
283      * If no build time is known for the given project, the
284      * last known build time for the project is set to the
285      * hot code replace manager's startup time.
286      */

287     protected long getLastProjectBuildTime(IProject project) {
288         ProjectBuildTime time= (ProjectBuildTime)fProjectBuildTimes.get(project);
289         if (time == null) {
290             time= new ProjectBuildTime();
291             time.setLastBuildDate(fStartupDate);
292             fProjectBuildTimes.put(project, time);
293         }
294         return time.getLastBuildDate().getTime();
295     }
296     
297     /**
298      * Notifies the targets of the changed types
299      */

300     private void notifyTargets(final List JavaDoc resources, final List JavaDoc qualifiedNames) {
301         final List JavaDoc hotSwapTargets= getHotSwapTargets();
302         final List JavaDoc noHotSwapTargets= getNoHotSwapTargets();
303         if (!hotSwapTargets.isEmpty()) {
304             Runnable JavaDoc runnable= new Runnable JavaDoc() {
305                 public void run() {
306                     doHotCodeReplace(hotSwapTargets, resources, qualifiedNames);
307                 }
308             };
309             DebugPlugin.getDefault().asyncExec(runnable);
310         }
311         if (!noHotSwapTargets.isEmpty()) {
312             Runnable JavaDoc runnable= new Runnable JavaDoc() {
313                 public void run() {
314                     notifyUnsupportedHCR(noHotSwapTargets, resources, qualifiedNames);
315                 }
316             };
317             DebugPlugin.getDefault().asyncExec(runnable);
318         }
319     }
320     
321     /**
322      * Filters elements out of the given collections of resources and qualified names
323      * if there is no type corresponding tyep loaded in the given debug target. This
324      * method allows us to avoid bogus HCR attempts and "HCR failed" notifications.
325      *
326      * @param target the debug target
327      * @param resources the list of resources to filter
328      * @param qualifiedNames the list of qualified names to filter, which corresponds
329      * to the list of resources on a one-to-one-basis
330      */

331     private void filterUnloadedTypes(JDIDebugTarget target, List JavaDoc resources, List JavaDoc qualifiedNames) {
332         for (int i= 0, numElements= qualifiedNames.size(); i < numElements; i++) {
333             String JavaDoc name= (String JavaDoc) qualifiedNames.get(i);
334             List JavaDoc list = target.jdiClassesByName(name);
335             if (list.isEmpty()) {
336                 // If no classes with the given name are loaded in the VM, don't waste
337
// cycles trying to replace.
338
qualifiedNames.remove(i);
339                 resources.remove(i);
340                 // Decrement the index and number of elements to compensate for item removal
341
i--;
342                 numElements--;
343             }
344         }
345     }
346     /**
347      * Notify the given targets that HCR failed for classes
348      * with the given fully qualified names.
349      */

350     protected void notifyUnsupportedHCR(List JavaDoc targets, List JavaDoc resources, List JavaDoc qualifiedNames) {
351         Iterator JavaDoc iter= targets.iterator();
352         JDIDebugTarget target= null;
353         while (iter.hasNext()) {
354             target= (JDIDebugTarget) iter.next();
355             if (target.isAvailable()) {
356                 // Make a local copy of the resources/names to swap so we can filter
357
// unloaded types on a per-target basis.
358
List JavaDoc resourcesToReplace= new ArrayList JavaDoc(resources);
359                 List JavaDoc qualifiedNamesToReplace= new ArrayList JavaDoc(qualifiedNames);
360                 filterUnloadedTypes(target, resourcesToReplace, qualifiedNamesToReplace);
361                 
362                 if (!qualifiedNamesToReplace.isEmpty()) {
363                     // Don't notify if the changed types aren't loaded.
364
fireHCRFailed(target, null);
365                     notifyFailedHCR(target, qualifiedNamesToReplace);
366                 }
367             } else {
368                 // Targets should be unregistered when they terminate,
369
// but this is a fallback.
370
deregisterTarget(target);
371             }
372         }
373     }
374     
375     protected void notifyFailedHCR(JDIDebugTarget target, List JavaDoc qualifiedNames) {
376         if (target.isAvailable()) {
377             target.addOutOfSynchTypes(qualifiedNames);
378             target.fireChangeEvent(DebugEvent.STATE);
379         }
380     }
381     
382     /**
383      * Returns the currently registered debug targets that support
384      * hot code replace.
385      */

386     protected List JavaDoc getHotSwapTargets() {
387         return (List JavaDoc) fHotSwapTargets.clone();
388     }
389     
390     /**
391      * Returns the currently registered debug targets that do
392      * not support hot code replace.
393      */

394     protected List JavaDoc getNoHotSwapTargets() {
395         return (List JavaDoc) fNoHotSwapTargets.clone();
396     }
397     
398     /**
399      * Perform a hot code replace with the given resources.
400      * For a JDK 1.4 compliant VM this involves:
401      * <ol>
402      * <li>Popping all frames from all thread stacks which will be affected by reloading the given resources</li>
403      * <li>Telling the VirtualMachine to redefine the affected classes</li>
404      * <li>Performing a step-into operation on all threads which were affected by the class redefinition.
405      * This returns execution to the first (deepest) affected method on the stack</li>
406      * </ol>
407      * For a J9 compliant VM this involves:
408      * <ol>
409      * <li>Telling the VirtualMachine to redefine the affected classes</li>
410      * <li>Popping all frames from all thread stacks which were affected by reloading the given resources and then
411      * performing a step-into operation on all threads which were affected by the class redefinition.</li>
412      * </ol>
413      *
414      * @param targets the targets in which to perform HCR
415      * @param resources the resources which correspond to the changed classes
416      */

417     private void doHotCodeReplace(List JavaDoc targets, List JavaDoc resources, List JavaDoc qualifiedNames) {
418         MultiStatus ms= new MultiStatus(JDIDebugPlugin.getUniqueIdentifier(), DebugException.TARGET_REQUEST_FAILED, "At least one target failed to drop to frame after successful hot code replace.", null); //$NON-NLS-1$
419
Iterator JavaDoc iter= targets.iterator();
420         while (iter.hasNext()) {
421             JDIDebugTarget target= (JDIDebugTarget) iter.next();
422             if (!target.isAvailable()) {
423                 deregisterTarget(target);
424                 continue;
425             }
426             // Make a local copy of the resources/names to swap so we can filter
427
// unloaded types on a per-target basis.
428
List JavaDoc resourcesToReplace= new ArrayList JavaDoc(resources);
429             List JavaDoc qualifiedNamesToReplace= new ArrayList JavaDoc(qualifiedNames);
430             filterUnloadedTypes(target, resourcesToReplace, qualifiedNamesToReplace);
431             if (qualifiedNamesToReplace.isEmpty()) {
432                 // If none of the changed types are loaded, do nothing.
433
continue;
434             }
435             
436             List JavaDoc poppedThreads= new ArrayList JavaDoc();
437             target.setIsPerformingHotCodeReplace(true);
438             try {
439                 boolean framesPopped= false;
440                 if (target.canPopFrames()) {
441                     // JDK 1.4 drop to frame support:
442
// JDK 1.4 spec is faulty around methods that have
443
// been rendered obsolete after class redefinition.
444
// Thus, pop the frames that contain affected methods
445
// *before* the class redefinition to avoid problems.
446
try {
447                         attemptPopFrames(target, resourcesToReplace, qualifiedNamesToReplace, poppedThreads);
448                         framesPopped= true; // No exception occurred
449
} catch (DebugException de) {
450                         if (shouldLogHCRException(de)) {
451                             ms.merge(de.getStatus());
452                         }
453                     }
454                 }
455                 target.removeOutOfSynchTypes(qualifiedNamesToReplace);
456                 if (target.supportsJDKHotCodeReplace()) {
457                     redefineTypesJDK(target, resourcesToReplace, qualifiedNamesToReplace);
458                 } else if (target.supportsJ9HotCodeReplace()) {
459                     redefineTypesJ9(target, qualifiedNamesToReplace);
460                 }
461                 if (containsObsoleteMethods(target)) {
462                     fireObsoleteMethods(target);
463                 }
464                 try {
465                     if (target.canPopFrames() && framesPopped) {
466                         // Second half of JDK 1.4 drop to frame support:
467
// All affected frames have been popped and the classes
468
// have been reloaded. Step into the first changed
469
// frame of each affected thread.
470
// must re-set 'is doing HCR' to be able to step
471
target.setIsPerformingHotCodeReplace(false);
472                             attemptStepIn(poppedThreads);
473                     } else {
474                         // J9 drop to frame support:
475
// After redefining classes, drop to frame
476
attemptDropToFrame(target, resourcesToReplace, qualifiedNamesToReplace);
477                     }
478                 } catch (DebugException de) {
479                     if (shouldLogHCRException(de)) {
480                         ms.merge(de.getStatus());
481                     }
482                 }
483                 fireHCRSucceeded(target);
484             } catch (DebugException de) {
485                 // target update failed
486
fireHCRFailed(target, de);
487             }
488             // also re-set 'is doing HCR' here incase HCR failed
489
target.setIsPerformingHotCodeReplace(false);
490             target.fireChangeEvent(DebugEvent.CONTENT);
491         }
492         if (!ms.isOK()) {
493             JDIDebugPlugin.log(ms);
494         }
495         fDeltaCache.clear();
496     }
497     
498     /**
499      * Returns whether the given exception, which occurred during HCR, should be logged.
500      * We anticipate that we can get IncompatibleThreadStateExceptions if the user happens
501      * to resume a thread at just the right moment. Since this has no ill effects for HCR,
502      * we don't log these exceptions.
503      */

504     private boolean shouldLogHCRException(DebugException exception) {
505         return !(exception.getStatus().getException() instanceof IncompatibleThreadStateException ||
506                  exception.getStatus().getCode() == IJavaThread.ERR_INCOMPATIBLE_THREAD_STATE ||
507                  exception.getStatus().getCode() == IJavaThread.ERR_THREAD_NOT_SUSPENDED);
508     }
509
510     /**
511      * Replaces the given types in the given J9 debug target.
512      * A fully qualified name of each type must be supplied.
513      *
514      * Breakpoints are reinstalled automatically when the new
515      * types are loaded.
516      *
517      * @exception DebugException if this method fails. Reasons include:
518      * <ul>
519      * <li>Failure communicating with the VM. The DebugException's
520      * status code contains the underlying exception responsible for
521      * the failure.</li>
522      * <li>The target VM was unable to reload a type due to a shape
523      * change</li>
524      * </ul>
525      */

526     private void redefineTypesJ9(JDIDebugTarget target, List JavaDoc qualifiedNames) throws DebugException {
527         String JavaDoc[] typeNames = (String JavaDoc[]) qualifiedNames.toArray(new String JavaDoc[qualifiedNames.size()]);
528         if (target.supportsJ9HotCodeReplace()) {
529             target.setHCROccurred(true);
530             org.eclipse.jdi.hcr.VirtualMachine vm= (org.eclipse.jdi.hcr.VirtualMachine) target.getVM();
531             if (vm == null) {
532                 target.requestFailed(JDIDebugHCRMessages.JavaHotCodeReplaceManager_Hot_code_replace_failed___VM_disconnected__1, null);
533             }
534             int result= org.eclipse.jdi.hcr.VirtualMachine.RELOAD_FAILURE;
535             try {
536                 result= vm.classesHaveChanged(typeNames);
537             } catch (RuntimeException JavaDoc e) {
538                 target.targetRequestFailed(MessageFormat.format(JDIDebugHCRMessages.JavaHotCodeReplaceManager_exception_replacing_types, new String JavaDoc[] {e.toString()}), e);
539             }
540             switch (result) {
541                 case org.eclipse.jdi.hcr.VirtualMachine.RELOAD_SUCCESS:
542                     break;
543                 case org.eclipse.jdi.hcr.VirtualMachine.RELOAD_IGNORED:
544                     target.targetRequestFailed(JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_ignored, null);
545                     break;
546                 case org.eclipse.jdi.hcr.VirtualMachine.RELOAD_FAILURE:
547                     target.targetRequestFailed(JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_failed, null);
548                     target.addOutOfSynchTypes(qualifiedNames);
549                     break;
550             }
551         } else {
552             target.notSupported(JDIDebugHCRMessages.JavaHotCodeReplaceManager_does_not_support_hcr);
553             target.addOutOfSynchTypes(qualifiedNames);
554         }
555     }
556     
557     /**
558      * Replaces the given types in the given JDK-compliant debug target.
559      *
560      * This method is to be used for JDK hot code replace.
561      */

562     private void redefineTypesJDK(JDIDebugTarget target, List JavaDoc resources, List JavaDoc qualifiedNames) throws DebugException {
563         if (target.supportsJDKHotCodeReplace()) {
564             target.setHCROccurred(true);
565             Map JavaDoc typesToBytes= getTypesToBytes(target, resources, qualifiedNames);
566             try {
567                 VirtualMachine vm = target.getVM();
568                 if (vm == null) {
569                     target.requestFailed(JDIDebugHCRMessages.JavaHotCodeReplaceManager_Hot_code_replace_failed___VM_disconnected__2, null);
570                 }
571                 vm.redefineClasses(typesToBytes);
572             } catch (UnsupportedOperationException JavaDoc exception) {
573                 String JavaDoc detail= exception.getMessage();
574                 if (detail != null) {
575                     redefineTypesFailedJDK(target, qualifiedNames, MessageFormat.format(JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_unsupported_operation, new String JavaDoc[] {detail}), exception);
576                 } else {
577                     redefineTypesFailedJDK(target, qualifiedNames, JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_unsupported_redefinition, exception);
578                 }
579             } catch (NoClassDefFoundError JavaDoc exception) {
580                 redefineTypesFailedJDK(target, qualifiedNames, JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_bad_bytes, exception);
581             } catch (VerifyError JavaDoc exception) {
582                 redefineTypesFailedJDK(target, qualifiedNames, JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_verify_error, exception);
583             } catch (UnsupportedClassVersionError JavaDoc exception) {
584                 redefineTypesFailedJDK(target, qualifiedNames, JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_unsupported_class_version, exception);
585             } catch (ClassFormatError JavaDoc exception) {
586                 redefineTypesFailedJDK(target, qualifiedNames, JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_class_format_error, exception);
587             } catch (ClassCircularityError JavaDoc exception) {
588                 redefineTypesFailedJDK(target, qualifiedNames, JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_class_circularity_error, exception);
589             } catch (RuntimeException JavaDoc exception) {
590                 redefineTypesFailedJDK(target, qualifiedNames, JDIDebugHCRMessages.JavaHotCodeReplaceManager_hcr_failed, exception);
591             }
592             target.reinstallBreakpointsIn(resources, qualifiedNames);
593         } else {
594             target.notSupported(JDIDebugHCRMessages.JavaHotCodeReplaceManager_does_not_support_hcr);
595         }
596     }
597     
598     /**
599      * Error handling for JDK hot code replace.
600      *
601      * The given exception occurred when redefinition was attempted
602      * for the given types.
603      */

604     private void redefineTypesFailedJDK(JDIDebugTarget target, List JavaDoc qualifiedNames, String JavaDoc message, Throwable JavaDoc exception) throws DebugException {
605         target.addOutOfSynchTypes(qualifiedNames);
606         target.jdiRequestFailed(message, exception);
607     }
608     
609     /**
610      * Returns a mapping of class files to the bytes that make up those
611      * class files.
612      *
613      * @param target the debug target to query
614      * @param resources the classfiles
615      * @param qualifiedNames the fully qualified type names corresponding to the
616      * classfiles. The typeNames correspond to the resources on a one-to-one
617      * basis.
618      * @return a mapping of class files to bytes
619      * key: class file
620      * value: the bytes which make up that classfile
621      */

622     private Map JavaDoc getTypesToBytes(JDIDebugTarget target, List JavaDoc resources, List JavaDoc qualifiedNames) {
623         Map JavaDoc typesToBytes= new HashMap JavaDoc(resources.size());
624         Iterator JavaDoc resourceIter= resources.iterator();
625         Iterator JavaDoc nameIter= qualifiedNames.iterator();
626         IResource resource;
627         String JavaDoc name;
628         while (resourceIter.hasNext()) {
629             resource= (IResource) resourceIter.next();
630             name= (String JavaDoc) nameIter.next();
631             List JavaDoc classes= target.jdiClassesByName(name);
632             byte[] bytes= null;
633             try {
634                 bytes= Util.getResourceContentsAsByteArray((IFile) resource);
635             } catch (JavaModelException jme) {
636                 continue;
637             }
638             Iterator JavaDoc classIter= classes.iterator();
639             while (classIter.hasNext()) {
640                 ReferenceType type= (ReferenceType) classIter.next();
641                 typesToBytes.put(type, bytes);
642             }
643         }
644         return typesToBytes;
645     }
646     
647     /**
648      * Notifies listeners that a hot code replace attempt succeeded
649      */

650     private void fireHCRSucceeded(IJavaDebugTarget target) {
651         Object JavaDoc[] listeners= fHotCodeReplaceListeners.getListeners();
652         for (int i=0; i<listeners.length; i++) {
653             ((IJavaHotCodeReplaceListener)listeners[i]).hotCodeReplaceSucceeded(target);
654         }
655     }
656     
657     /**
658      * Notifies listeners that a hot code replace attempt failed with the given exception
659      */

660     private void fireHCRFailed(JDIDebugTarget target, DebugException exception) {
661         Object JavaDoc[] listeners= fHotCodeReplaceListeners.getListeners();
662         for (int i=0; i<listeners.length; i++) {
663             ((IJavaHotCodeReplaceListener)listeners[i]).hotCodeReplaceFailed(target, exception);
664         }
665     }
666     
667     /**
668      * Notifies listeners that obsolete methods remain on the stack
669      */

670     private void fireObsoleteMethods(JDIDebugTarget target) {
671         Object JavaDoc[] listeners= fHotCodeReplaceListeners.getListeners();
672         for (int i=0; i<listeners.length; i++) {
673             ((IJavaHotCodeReplaceListener)listeners[i]).obsoleteMethods(target);
674         }
675     }
676     
677     /**
678      * Looks for the deepest effected stack frame in the stack
679      * and forces a drop to frame. Does this for all of the active
680      * stack frames in the target.
681      *
682      * @param target the debug target in which frames are to be dropped
683      * @param replacedClassNames the classes that have been redefined
684      */

685     protected void attemptDropToFrame(JDIDebugTarget target, List JavaDoc resources, List JavaDoc replacedClassNames) throws DebugException {
686         List JavaDoc dropFrames= getAffectedFrames(target.getThreads(), resources, replacedClassNames);
687
688         // All threads that want to drop to frame are able. Proceed with the drop
689
JDIStackFrame dropFrame= null;
690         Iterator JavaDoc iter= dropFrames.iterator();
691         while (iter.hasNext()) {
692             try {
693                 dropFrame= ((JDIStackFrame)iter.next());
694                 dropFrame.dropToFrame();
695             } catch (DebugException de) {
696                 notifyFailedDrop(((JDIThread)dropFrame.getThread()).computeStackFrames(), replacedClassNames);
697             }
698         }
699     }
700     
701     /**
702      * Looks for the deepest effected stack frame in the stack
703      * and forces a drop to frame. Does this for all of the active
704      * stack frames in the target.
705      *
706      * @param target the debug target in which frames are to be dropped
707      * @param replacedClassNames the classes that have been redefined
708      * @param poppedThreads a list of the threads in which frames
709      * were popped.This parameter may have entries added by this method
710      */

711     protected void attemptPopFrames(JDIDebugTarget target, List JavaDoc resources, List JavaDoc replacedClassNames, List JavaDoc poppedThreads) throws DebugException {
712         List JavaDoc popFrames= getAffectedFrames(target.getThreads(), resources, replacedClassNames);
713
714         // All threads that want to drop to frame are able. Proceed with the drop
715
JDIStackFrame popFrame= null;
716         Iterator JavaDoc iter= popFrames.iterator();
717         while (iter.hasNext()) {
718             try {
719                 popFrame= ((JDIStackFrame)iter.next());
720                 popFrame.popFrame();
721                 poppedThreads.add(popFrame.getThread());
722             } catch (DebugException de) {
723                 poppedThreads.remove(popFrame.getThread());
724                 notifyFailedDrop(((JDIThread)popFrame.getThread()).computeStackFrames(), replacedClassNames);
725             }
726         }
727     }
728     
729     /**
730      * Returns whether or not the given target contains stack frames with obsolete
731      * methods.
732      */

733     protected boolean containsObsoleteMethods(JDIDebugTarget target) throws DebugException {
734         IThread[] threads=target.getThreads();
735         List JavaDoc frames= null;
736         Iterator JavaDoc iter= null;
737         for (int i= 0, numThreads= threads.length; i < numThreads; i++) {
738             frames= ((JDIThread)threads[i]).computeNewStackFrames();
739             iter= frames.iterator();
740             while (iter.hasNext()) {
741                 if (((JDIStackFrame)iter.next()).isObsolete()) {
742                     return true;
743                 }
744             }
745         }
746         return false;
747     }
748     
749     /**
750      * Returns a list of frames which should be popped in the given threads.
751      */

752     protected List JavaDoc getAffectedFrames(IThread[] threads, List JavaDoc resourceList, List JavaDoc replacedClassNames) throws DebugException {
753         JDIThread thread= null;
754         JDIStackFrame affectedFrame= null;
755         List JavaDoc popFrames= new ArrayList JavaDoc();
756         int numThreads= threads.length;
757         IResource[] resources= new IResource[resourceList.size()];
758         resourceList.toArray(resources);
759         for (int i = 0; i < numThreads; i++) {
760             thread= (JDIThread) threads[i];
761             if (thread.isSuspended()) {
762                 affectedFrame= getAffectedFrame(thread, replacedClassNames);
763                 if (affectedFrame == null) {
764                     // No frame to drop to in this thread
765
continue;
766                 }
767                 if (affectedFrame.supportsDropToFrame()) {
768                     popFrames.add(affectedFrame);
769                 } else {
770                     // if any thread that should drop does not support the drop,
771
// do not drop in any threads.
772
for (int j= 0; j < numThreads; j++) {
773                         notifyFailedDrop(((JDIThread)threads[i]).computeStackFrames(), replacedClassNames);
774                     }
775                     throw new DebugException(new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(),
776                         DebugException.NOT_SUPPORTED, JDIDebugHCRMessages.JavaHotCodeReplaceManager_Drop_to_frame_not_supported, null));
777                 }
778             }
779         }
780         return popFrames;
781     }
782     
783     /**
784      * Returns the stack frame that should be dropped to in the
785      * given thread after a hot code replace.
786      * This is calculated by determining if the threads contain stack frames
787      * that reside in one of the given replaced class names. If possible, only
788      * stack frames whose methods were directly affected (and not simply all frames
789      * in affected types) will be returned.
790      */

791     protected JDIStackFrame getAffectedFrame(JDIThread thread, List JavaDoc replacedClassNames) throws DebugException {
792         List JavaDoc frames= thread.computeStackFrames();
793         JDIStackFrame affectedFrame= null;
794         JDIStackFrame frame= null;
795         ICompilationUnit compilationUnit= null;
796         CompilationUnitDelta delta= null;
797         IProject project= null;
798         for (int j= frames.size() - 1; j >= 0; j--) {
799             frame= (JDIStackFrame) frames.get(j);
800             if (containsChangedType(frame, replacedClassNames)) {
801                 // smart drop to frame support
802
compilationUnit= getCompilationUnit(frame);
803                 // if we can't find the source, then do type-based drop
804
if (compilationUnit != null) {
805                     try {
806                         project= compilationUnit.getCorrespondingResource().getProject();
807                         delta = getDelta(compilationUnit, getLastProjectBuildTime(project));
808                         if (!delta.hasChanged(frame.getName(), frame.getSignature())) {
809                             continue;
810                         }
811                     } catch (CoreException exception) {
812                         // If smart drop to frame fails, just do type-based drop
813
}
814                 }
815
816                 if (frame.supportsDropToFrame()) {
817                     affectedFrame= frame;
818                     break;
819                 }
820                 // The frame we wanted to drop to cannot be popped.
821
// Set the affected frame to the next lowest poppable
822
// frame on the stack.
823
while (j > 0) {
824                     j--;
825                     frame= (JDIStackFrame) frames.get(j);
826                     if (frame.supportsDropToFrame()) {
827                         affectedFrame= frame;
828                         break;
829                     }
830                 }
831                 break;
832             }
833         }
834         return affectedFrame;
835     }
836     
837     /**
838      * Returns the delta object for the given compilation unit
839      *
840      * @param cu compilation unit
841      * @param time time to compare to (i.e. compare to first version before this time)
842      * @return delta object
843      */

844     private CompilationUnitDelta getDelta(ICompilationUnit cu, long time) throws CoreException {
845         CompilationUnitDelta delta = (CompilationUnitDelta) fDeltaCache.get(cu);
846         if (delta == null) {
847             delta= new CompilationUnitDelta(cu, time);
848             fDeltaCache.put(cu, delta);
849         }
850         return delta;
851     }
852     
853     /**
854      * Returns whether the given frame's declaring type was changed
855      * based on the given list of changed class names.
856      */

857     protected boolean containsChangedType(JDIStackFrame frame, List JavaDoc replacedClassNames) throws DebugException {
858         String JavaDoc declaringTypeName= frame.getDeclaringTypeName();
859         // Check if the frame's declaring type was changed
860
if (replacedClassNames.contains(declaringTypeName)) {
861             return true;
862         }
863         // Check if one of the frame's declaring type's inner classes have changed
864
Iterator JavaDoc iter= replacedClassNames.iterator();
865         int index;
866         String JavaDoc className= null;
867         while (iter.hasNext()) {
868             className= (String JavaDoc) iter.next();
869             index= className.indexOf('$');
870             if (index > -1 && declaringTypeName.equals(className.substring(0, index))) {
871                 return true;
872             }
873         }
874         return false;
875     }
876     
877     /**
878      * Performs a "step into" operation on the given threads.
879      */

880     protected void attemptStepIn(List JavaDoc threads) throws DebugException {
881         Iterator JavaDoc iter= threads.iterator();
882         while (iter.hasNext()) {
883             ((JDIThread) iter.next()).stepInto();
884         }
885     }
886     
887     /**
888      * Returns the compilation unit associated with this
889      * Java stack frame. Returns <code>null</code> for a binary
890      * stack frame.
891      */

892     protected ICompilationUnit getCompilationUnit(IJavaStackFrame frame) {
893         ILaunch launch= frame.getLaunch();
894         if (launch == null) {
895             return null;
896         }
897         ISourceLocator locator= launch.getSourceLocator();
898         if (locator == null) {
899             return null;
900         }
901         IJavaDebugTarget target = (IJavaDebugTarget) frame.getDebugTarget();
902         String JavaDoc def = target.getDefaultStratum();
903         target.setDefaultStratum("Java"); //$NON-NLS-1$
904
Object JavaDoc sourceElement= locator.getSourceElement(frame);
905         target.setDefaultStratum(def);
906         if (!(sourceElement instanceof IJavaElement) && sourceElement instanceof IAdaptable) {
907             sourceElement = ((IAdaptable)sourceElement).getAdapter(IJavaElement.class);
908         }
909         if (sourceElement instanceof IType) {
910             return ((IType)sourceElement).getCompilationUnit();
911         }
912         if (sourceElement instanceof ICompilationUnit) {
913             return (ICompilationUnit)sourceElement;
914         }
915         return null;
916     }
917     
918     /**
919      * Returns the method in which this stack frame is
920      * suspended or <code>null</code> if none can be found
921      */

922     public IMethod getMethod(JDIStackFrame frame, ICompilationUnit unit) throws CoreException {
923         String JavaDoc declaringTypeName= frame.getDeclaringTypeName();
924         String JavaDoc methodName= frame.getMethodName();
925         String JavaDoc[] arguments= null;
926         try {
927             arguments= Signature.getParameterTypes(frame.getSignature());
928         } catch (IllegalArgumentException JavaDoc exception) {
929             // If Signature can't parse the signature, we can't
930
// create the method
931
return null;
932         }
933         String JavaDoc typeName = getUnqualifiedName(declaringTypeName);
934         int index = typeName.indexOf('$');
935         IType type = null;
936         if (index > 0) {
937             String JavaDoc remaining = typeName.substring(index + 1);
938             typeName = typeName.substring(0, index);
939             type = unit.getType(typeName);
940             while (remaining != null) {
941                 index = remaining.indexOf('$');
942                 if (index > 0) {
943                     typeName = remaining.substring(0, index);
944                     remaining = remaining.substring(index + 1);
945                 } else {
946                     typeName = remaining;
947                     remaining = null;
948                 }
949                 type = type.getType(typeName);
950             }
951         } else {
952             type = unit.getType(typeName);
953         }
954         if (type != null) {
955             return type.getMethod(methodName, arguments);
956         }
957         return null;
958     }
959     
960     /**
961      * Given a fully qualified name, return the unqualified name.
962      */

963     protected String JavaDoc getUnqualifiedName(String JavaDoc qualifiedName) {
964         int index= qualifiedName.lastIndexOf('.');
965         return qualifiedName.substring(index + 1);
966     }
967     
968     /**
969      * Notify the given frames that a drop to frame has failed after
970      * an HCR with the given class names.
971      */

972     private void notifyFailedDrop(List JavaDoc frames, List JavaDoc replacedClassNames) throws DebugException {
973         JDIStackFrame frame;
974         Iterator JavaDoc iter= frames.iterator();
975         while (iter.hasNext()) {
976             frame= (JDIStackFrame) iter.next();
977             if (replacedClassNames.contains(frame.getDeclaringTypeName())) {
978                 frame.setOutOfSynch(true);
979             }
980         }
981     }
982     /**
983      * Returns the class file visitor after visiting the resource change.
984      * The visitor contains the changed class files and qualified type names.
985      * Returns <code>null</code> if the visitor encounters an exception,
986      * or the delta is not a POST_BUILD.
987      */

988     protected ChangedClassFilesVisitor getChangedClassFiles(IResourceChangeEvent event) {
989         IResourceDelta delta= event.getDelta();
990         if (event.getType() != IResourceChangeEvent.POST_BUILD || delta == null) {
991             return null;
992         }
993         fClassfileVisitor.reset();
994         try {
995             delta.accept(fClassfileVisitor);
996         } catch (CoreException e) {
997             JDIDebugPlugin.log(e);
998             return null; // quiet failure
999
}
1000        return fClassfileVisitor;
1001    }
1002    
1003    /**
1004     * A visitor which collects changed class files.
1005     */

1006    class ChangedClassFilesVisitor implements IResourceDeltaVisitor {
1007        /**
1008         * The collection of changed class files.
1009         */

1010        protected List JavaDoc fFiles= null;
1011        
1012        /**
1013         * Collection of qualified type names, corresponding to class files.
1014         */

1015        protected List JavaDoc fNames= null;
1016        
1017        /**
1018         * Answers whether children should be visited.
1019         * <p>
1020         * If the associated resource is a class file which
1021         * has been changed, record it.
1022         */

1023        public boolean visit(IResourceDelta delta) {
1024            if (delta == null || 0 == (delta.getKind() & IResourceDelta.CHANGED)) {
1025                return false;
1026            }
1027            IResource resource= delta.getResource();
1028            if (resource != null) {
1029                switch (resource.getType()) {
1030                    case IResource.FILE :
1031                        if (0 == (delta.getFlags() & IResourceDelta.CONTENT))
1032                            return false;
1033                        if (CLASS_FILE_EXTENSION.equals(resource.getFullPath().getFileExtension())) {
1034                            IPath localLocation = resource.getLocation();
1035                            if (localLocation != null) {
1036                                String JavaDoc path = localLocation.toOSString();
1037                                IClassFileReader reader = ToolFactory.createDefaultClassFileReader(path, IClassFileReader.CLASSFILE_ATTRIBUTES);
1038                                if (reader != null) {
1039                                    // this name is slash-delimited
1040
String JavaDoc qualifiedName = new String JavaDoc(reader.getClassName());
1041                                    boolean hasBlockingErrors= false;
1042                                    try {
1043                                        if (!JDIDebugModel.getPreferences().getBoolean(JDIDebugModel.PREF_HCR_WITH_COMPILATION_ERRORS)) {
1044                                            // If the user doesn't want to replace classfiles containing
1045
// compilation errors, get the source file associated with
1046
// the class file and query it for compilation errors
1047
IJavaProject pro = JavaCore.create(resource.getProject());
1048                                            ISourceAttribute sourceAttribute = reader.getSourceFileAttribute();
1049                                            String JavaDoc sourceName = null;
1050                                            if (sourceAttribute != null) {
1051                                                sourceName = new String JavaDoc(sourceAttribute.getSourceFileName());
1052                                            }
1053                                            IResource sourceFile= getSourceFile(pro, qualifiedName, sourceName);
1054                                            if (sourceFile != null) {
1055                                                IMarker[] problemMarkers= null;
1056                                                problemMarkers= sourceFile.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
1057                                                for (int i= 0; i < problemMarkers.length; i++) {
1058                                                    if (problemMarkers[i].getAttribute(IMarker.SEVERITY, -1) == IMarker.SEVERITY_ERROR) {
1059                                                        hasBlockingErrors= true;
1060                                                        break;
1061                                                    }
1062                                                }
1063                                            }
1064                                        }
1065                                    } catch (CoreException e) {
1066                                        JDIDebugPlugin.log(e);
1067                                    }
1068                                    if (!hasBlockingErrors) {
1069                                        fFiles.add(resource);
1070                                        // dot-delimit the name
1071
fNames.add(qualifiedName.replace('/','.'));
1072                                    }
1073                                }
1074                            }
1075                        }
1076                        return false;
1077                        
1078                    default :
1079                        return true;
1080                }
1081            }
1082            return true;
1083        }
1084        /**
1085         * Resets the file collection to empty
1086         */

1087        public void reset() {
1088            fFiles = new ArrayList JavaDoc();
1089            fNames = new ArrayList JavaDoc();
1090        }
1091        
1092        /**
1093         * Answers a collection of changed class files or <code>null</code>
1094         */

1095        public List JavaDoc getChangedClassFiles() {
1096            return fFiles;
1097        }
1098        
1099        /**
1100         * Returns a collection of qualified type names corresponding to the
1101         * changed class files.
1102         *
1103         * @return List
1104         */

1105        public List JavaDoc getQualifiedNamesList() {
1106            return fNames;
1107        }
1108        
1109        /**
1110         * Returns the source file associated with the given type, or
1111         * <code>null</code> if no source file could be found.
1112         *
1113         * @param project the java project containing the classfile
1114         * @param qualifiedName fully qualified name of the type, slash
1115         * delimited
1116         * @param sourceAttribute debug source attribute, or <code>null</code>
1117         * if none
1118         */

1119        private IResource getSourceFile(IJavaProject project, String JavaDoc qualifiedName, String JavaDoc sourceAttribute) {
1120            String JavaDoc name = null;
1121            IJavaElement element = null;
1122            try {
1123                if (sourceAttribute == null) {
1124                    element = JavaDebugUtils.findElement(qualifiedName, project);
1125                } else {
1126                    int i = qualifiedName.lastIndexOf('/');
1127                    if (i > 0) {
1128                        name = qualifiedName.substring(0, i + 1);
1129                        name = name + sourceAttribute;
1130                    } else {
1131                        name = sourceAttribute;
1132                    }
1133                    element = project.findElement(new Path(name));
1134                }
1135                if (element instanceof ICompilationUnit) {
1136                    ICompilationUnit cu = (ICompilationUnit) element;
1137                    return cu.getCorrespondingResource();
1138                }
1139            } catch (CoreException e) {
1140            }
1141            return null;
1142        }
1143    }
1144
1145    
1146    /**
1147     * Adds the given listener to the collection of hot code replace listeners.
1148     * Listeners are notified when hot code replace attempts succeed or fail.
1149     */

1150    public void addHotCodeReplaceListener(IJavaHotCodeReplaceListener listener) {
1151        fHotCodeReplaceListeners.add(listener);
1152    }
1153    
1154    /**
1155     * Removes the given listener from the collection of hot code replace listeners.
1156     * Once a listener is removed, it will no longer be notified of hot code replace
1157     * attempt successes or failures.
1158     */

1159    public void removeHotCodeReplaceListener(IJavaHotCodeReplaceListener listener) {
1160        fHotCodeReplaceListeners.remove(listener);
1161    }
1162    
1163    /**
1164     * @see ILaunchListener#launchRemoved(ILaunch)
1165     */

1166    public void launchRemoved(ILaunch launch) {
1167        IDebugTarget[] debugTargets= launch.getDebugTargets();
1168        for (int i = 0; i < debugTargets.length; i++) {
1169            IJavaDebugTarget jt = (IJavaDebugTarget)debugTargets[i].getAdapter(IJavaDebugTarget.class);
1170            if (jt != null) {
1171                deregisterTarget((JDIDebugTarget)jt);
1172            }
1173        }
1174    }
1175
1176    /**
1177     * Begin listening for resource changes when a launch is
1178     * registered with a hot swapable target.
1179     *
1180     * @see org.eclipse.debug.core.ILaunchListener#launchAdded(org.eclipse.debug.core.ILaunch)
1181     */

1182    public void launchAdded(ILaunch launch) {
1183        IDebugTarget[] debugTargets= launch.getDebugTargets();
1184        for (int i = 0; i < debugTargets.length; i++) {
1185            IJavaDebugTarget jt = (IJavaDebugTarget)debugTargets[i].getAdapter(IJavaDebugTarget.class);
1186            if (jt != null) {
1187                JDIDebugTarget target = (JDIDebugTarget)jt;
1188                if (target.supportsHotCodeReplace()) {
1189                    addHotSwapTarget(target);
1190                } else if (target.isAvailable()){
1191                    addNonHotSwapTarget(target);
1192                }
1193            }
1194        }
1195        if (!fHotSwapTargets.isEmpty() || !fNoHotSwapTargets.isEmpty()) {
1196            getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_BUILD);
1197        }
1198    }
1199    
1200    /**
1201     * Begin listening for resource changes when a launch is
1202     * registered with a hot swapable target.
1203     *
1204     * @see ILaunchListener#launchChanged(ILaunch)
1205     */

1206    public void launchChanged(ILaunch launch) {
1207        launchAdded(launch);
1208    }
1209    
1210    /* (non-Javadoc)
1211     * @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
1212     */

1213    public void handleDebugEvents(DebugEvent[] events) {
1214        for (int i = 0; i < events.length; i++) {
1215            DebugEvent event = events[i];
1216            if (event.getKind() == DebugEvent.TERMINATE) {
1217                Object JavaDoc source = event.getSource();
1218                if (source instanceof IAdaptable && source instanceof IDebugTarget) {
1219                    IJavaDebugTarget jt = (IJavaDebugTarget)((IAdaptable)source).getAdapter(IJavaDebugTarget.class);
1220                    if (jt != null) {
1221                        deregisterTarget((JDIDebugTarget)jt);
1222                    }
1223                }
1224            }
1225        }
1226    }
1227    
1228    protected void deregisterTarget(JDIDebugTarget target) {
1229        // Remove the target from its hot swap target cache.
1230
if (!fHotSwapTargets.remove(target)) {
1231            fNoHotSwapTargets.remove(target);
1232        }
1233        ILaunch[] launches= DebugPlugin.getDefault().getLaunchManager().getLaunches();
1234        // If there are no more active JDIDebugTargets, stop
1235
// listening to resource changes.
1236
for (int i= 0; i < launches.length; i++) {
1237            IDebugTarget[] targets = launches[i].getDebugTargets();
1238            for (int j = 0; j < targets.length; j++) {
1239                IDebugTarget debugTarget = targets[j];
1240                IJavaDebugTarget jt = (IJavaDebugTarget)debugTarget.getAdapter(IJavaDebugTarget.class);
1241                if (jt != null) {
1242                    if (((JDIDebugTarget)jt).isAvailable()) {
1243                        return;
1244                    }
1245                }
1246            }
1247        }
1248    }
1249    
1250    /**
1251     * Adds the given target to the list of hot-swappable targets.
1252     * Has no effect if the target is already registered.
1253     *
1254     * @param target a target that supports hot swap
1255     */

1256    protected void addHotSwapTarget(JDIDebugTarget target) {
1257        if (!fHotSwapTargets.contains(target)) {
1258            fHotSwapTargets.add(target);
1259        }
1260    }
1261    
1262    /**
1263     * Adds the given target to the list of non hot-swappable targets.
1264     * Has no effect if the target is already registered.
1265     *
1266     * @param target a target that does not support hot swap
1267     */

1268    protected void addNonHotSwapTarget(JDIDebugTarget target) {
1269        if (!fNoHotSwapTargets.contains(target)) {
1270            fNoHotSwapTargets.add(target);
1271        }
1272    }
1273
1274}
1275
1276
Popular Tags