0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.api.debugger;
0043:
0044: import java.beans.*;
0045: import java.util.*;
0046: import java.util.HashMap;
0047:
0048: import org.openide.util.Cancellable;
0049: import org.openide.util.Task;
0050:
0051: import org.netbeans.spi.debugger.ContextProvider;
0052: import org.netbeans.spi.debugger.DelegatingDebuggerEngineProvider;
0053: import org.netbeans.spi.debugger.DelegatingSessionProvider;
0054: import org.netbeans.spi.debugger.DebuggerEngineProvider;
0055: import org.netbeans.spi.debugger.SessionProvider;
0056:
0057: /**
0058: * The root class of Debugger APIs. DebuggerManager manages list of
0059: * {@link org.netbeans.api.debugger.Session}s,
0060: * {@link org.netbeans.api.debugger.Breakpoint}s and
0061: * {@link org.netbeans.api.debugger.Watch}es.
0062: *
0063: *
0064: * <p><br><table border="1" cellpadding="3" cellspacing="0" width="100%">
0065: * <tbody><tr bgcolor="#ccccff">
0066: * <td colspan="2"><font size="+2"><b>Description </b></font></td>
0067: * </tr><tr><td align="left" valign="top" width="1%"><font size="+1">
0068: * <b>Functionality</b></font></td><td>
0069: *
0070: * <b>Start & finish debugging:</b>
0071: * DebuggerManager manages a process of starting a new debugging (
0072: * {@link #startDebugging}). It cooperates with all installed
0073: * {@link org.netbeans.spi.debugger.DebuggerEngineProvider}s to create a new
0074: * {@link org.netbeans.api.debugger.Session} (or Sessions) and a new
0075: * {@link org.netbeans.api.debugger.DebuggerEngine} (or Engines).
0076: * It supports kill all sessions too ({@link #finishAllSessions}).
0077: *
0078: * <br><br>
0079: * <b>Sessions management:</b>
0080: * DebuggerManager keeps list of all
0081: * {@link org.netbeans.api.debugger.Session}s ({@link #getSessions}),
0082: * and manages current session ({@link #getCurrentSession},
0083: * {@link #setCurrentSession}).
0084: *
0085: * <br><br>
0086: * <b>Engine management:</b>
0087: * DebuggerManager provides current engine ({@link #getCurrentEngine}).
0088: * Current engine is derivated from current session. So,
0089: * <i>
0090: * debuggerManager.getCurrentEngine () == debuggerManager.
0091: * getCurrentSession.getCurrentEngine ()
0092: * </i>
0093: * should be always true.
0094: *
0095: * <br><br>
0096: * <b>Breakpoints management:</b>
0097: * DebuggerManager keeps list of all shared breakpoints
0098: * ({@link #getBreakpoints}).
0099: * Breakpoint can be added ({@link #addBreakpoint}) and removed
0100: * ({@link #removeBreakpoint}).
0101: *
0102: * <br><br>
0103: * <b>Watches management:</b>
0104: * DebuggerManager keeps list of all shared watches ({@link #getWatches}).
0105: * Watch can be created & added ({@link #createWatch}).
0106: *
0107: * <br><br>
0108: * <b>Support for listening:</b>
0109: * DebuggerManager propagates all changes to two type of listeners - general
0110: * {@link java.beans.PropertyChangeListener} and specific
0111: * {@link org.netbeans.api.debugger.DebuggerManagerListener}.
0112: *
0113: * <br>
0114: * </td></tr><tr><td align="left" valign="top" width="1%"><font size="+1">
0115: * <b>Clinents / Providers</b></font></td><td>
0116: *
0117: * DebuggerCore module should be the only one provider of this abstract class.
0118: * This class should be called from debugger plug-in modules and from debugger
0119: * UI modules.
0120: *
0121: * <br>
0122: * </td></tr><tr><td align="left" valign="top" width="1%"><font size="+1">
0123: * <b>Lifecycle</b></font></td><td>
0124: *
0125: * The only one instance of DebuggerManager should exist, and it should be
0126: * created in {@link #getDebuggerManager} method.
0127: *
0128: * </td></tr><tr><td align="left" valign="top" width="1%"><font size="+1">
0129: * <b>Evolution</b></font></td><td>
0130: *
0131: * No method should be removed from this class, but some functionality can
0132: * be added.
0133: *
0134: * </td></tr></tbody></table>
0135: *
0136: * @author Jan Jancura
0137: */
0138: public final class DebuggerManager implements ContextProvider {
0139:
0140: // TODO: deprecate all these properties. They are useless, since there are
0141: // dedicated methods in DebuggerManagerListener
0142:
0143: // OR: Remove DebuggerManagerListener and use just the properties.
0144: // - probably not possible because of initBreakpoints() method.
0145:
0146: /** Name of property for the set of breakpoints in the system. */
0147: public static final String PROP_BREAKPOINTS_INIT = "breakpointsInit"; // NOI18N
0148:
0149: /** Name of property for the set of breakpoints in the system. */
0150: public static final String PROP_BREAKPOINTS = "breakpoints"; // NOI18N
0151:
0152: /** Name of property for current debugger engine. */
0153: public static final String PROP_CURRENT_ENGINE = "currentEngine";
0154:
0155: /** Name of property for current debugger session. */
0156: public static final String PROP_CURRENT_SESSION = "currentSession";
0157:
0158: /** Name of property for set of running debugger sessions. */
0159: public static final String PROP_SESSIONS = "sessions";
0160:
0161: /** Name of property for set of running debugger engines. */
0162: public static final String PROP_DEBUGGER_ENGINES = "debuggerEngines";
0163:
0164: /** Name of property for the set of watches in the system. */
0165: public static final String PROP_WATCHES = "watches"; // NOI18N
0166:
0167: /** Name of property for the set of watches in the system. */
0168: public static final String PROP_WATCHES_INIT = "watchesInit"; // NOI18N
0169:
0170: private static DebuggerManager debuggerManager;
0171: private Session currentSession;
0172: private DebuggerEngine currentEngine;
0173: private List sessions = new ArrayList();
0174: private Set engines = new HashSet();
0175: private Vector breakpoints = new Vector();
0176: private boolean breakpointsInitializing = false;
0177: private boolean breakpointsInitialized = false;
0178: private Vector watches = new Vector();
0179: private boolean watchesInitialized = false;
0180: private SessionListener sessionListener = new SessionListener();
0181: private Vector listeners = new Vector();
0182: private HashMap listenersMap = new HashMap();
0183: private ActionsManager actionsManager = null;
0184:
0185: private Lookup lookup = new Lookup.MetaInf(null);
0186:
0187: //private ModuleUnloadListeners moduleUnloadListeners = new ModuleUnloadListeners();
0188:
0189: /**
0190: * Returns default instance of DebuggerManager.
0191: *
0192: * @return default instance of DebuggerManager
0193: */
0194: public static synchronized DebuggerManager getDebuggerManager() {
0195: if (debuggerManager == null)
0196: debuggerManager = new DebuggerManager();
0197: return debuggerManager;
0198: }
0199:
0200: /**
0201: * Creates a new instance of DebuggerManager.
0202: * It's called from a synchronized block, do not call any foreign code from here.
0203: */
0204: private DebuggerManager() {
0205: }
0206:
0207: public synchronized ActionsManager getActionsManager() {
0208: if (actionsManager == null)
0209: actionsManager = new ActionsManager(lookup);
0210: return actionsManager;
0211: }
0212:
0213: // lookup management .............................................
0214:
0215: /**
0216: * Returns list of services of given type from given folder.
0217: *
0218: * @param service a type of service to look for
0219: * @return list of services of given type
0220: */
0221: public <T> List<? extends T> lookup(String folder, Class<T> service) {
0222: return lookup.lookup(folder, service);
0223: }
0224:
0225: /**
0226: * Returns one service of given type from given folder.
0227: *
0228: * @param service a type of service to look for
0229: * @return ne service of given type
0230: */
0231: public <T> T lookupFirst(String folder, Class<T> service) {
0232: return lookup.lookupFirst(folder, service);
0233: }
0234:
0235: /**
0236: * Join two lookups together.
0237: * The result will merge the lookups.
0238: * The result of its {@link #lookup} method will additionally implement {@link Customizer}.
0239: * @param cp1 first lookup
0240: * @param cp2 second lookup
0241: * @return a merger of the two
0242: * @since org.netbeans.api.debugger/1 1.13
0243: */
0244: public static ContextProvider join(ContextProvider cp1,
0245: ContextProvider cp2) {
0246: return new Lookup.Compound(cp1, cp2);
0247: }
0248:
0249: // session / engine management .............................................
0250:
0251: /**
0252: * Start a new debugging for given
0253: * {@link org.netbeans.api.debugger.DebuggerInfo}. DebuggerInfo provides
0254: * information needed to start new debugging. DebuggerManager finds
0255: * all {@link org.netbeans.spi.debugger.SessionProvider}s and
0256: * {@link org.netbeans.spi.debugger.DelegatingSessionProvider}s
0257: * installed for given DebuggerInfo, and creates a new
0258: * {@link Session}(s).
0259: * After that it looks for all
0260: * {@link org.netbeans.spi.debugger.DebuggerEngineProvider}s and
0261: * {@link org.netbeans.spi.debugger.DelegatingDebuggerEngineProvider}s
0262: * installed for Session, and crates a new
0263: * {@link DebuggerEngine}(s).
0264: * <br>
0265: * If the implementation of ACTION_START providers support cancellation (implements {@link Cancellable}),
0266: * this startup sequence can be canceled via Thread.interrupt() while
0267: * startDebugging() method is waiting for the action providers.
0268: *
0269: * @param info debugger startup info
0270: * @return DebuggerEngines started for given info
0271: */
0272: public DebuggerEngine[] startDebugging(DebuggerInfo info) {
0273: //S ystem.out.println("@StartDebugging info: " + info);
0274:
0275: // init sessions
0276: ArrayList sessionProviders = new ArrayList();
0277: ArrayList engines = new ArrayList();
0278: Lookup l = info.getLookup();
0279: Lookup l2 = info.getLookup();
0280: synchronized (l) {
0281: sessionProviders.addAll(l.lookup(null,
0282: SessionProvider.class));
0283: sessionProviders.addAll(l.lookup(null,
0284: DelegatingSessionProvider.class));
0285: }
0286: Session sessionToStart = null;
0287: int i, k = sessionProviders.size();
0288: for (i = 0; i < k; i++) {
0289: Session s = null;
0290: if (sessionProviders.get(i) instanceof DelegatingSessionProvider) {
0291: s = ((DelegatingSessionProvider) sessionProviders
0292: .get(i)).getSession(info);
0293: l = new Lookup.Compound(l, s.privateLookup);
0294: //S ystem.out.println("@ StartDebugging DelegaingSession: " + s);
0295: } else {
0296: SessionProvider sp = (SessionProvider) sessionProviders
0297: .get(i);
0298: s = new Session(sp.getSessionName(), sp
0299: .getLocationName(), sp.getTypeID(), sp
0300: .getServices(), l);
0301: sessionToStart = s;
0302: l = s.getLookup();
0303: l2 = s.getLookup();
0304: addSession(s);
0305: //S ystem.out.println("@ StartDebugging new Session: " + s);
0306: }
0307:
0308: // init DebuggerEngines
0309: ArrayList engineProviders = new ArrayList();
0310: synchronized (l2) {
0311: engineProviders.addAll(l2.lookup(null,
0312: DebuggerEngineProvider.class));
0313: engineProviders.addAll(l2.lookup(null,
0314: DelegatingDebuggerEngineProvider.class));
0315: }
0316: int j, jj = engineProviders.size();
0317: for (j = 0; j < jj; j++) {
0318: DebuggerEngine engine = null;
0319: String[] languages = null;
0320: if (engineProviders.get(j) instanceof DebuggerEngineProvider) {
0321: DebuggerEngineProvider ep = (DebuggerEngineProvider) engineProviders
0322: .get(j);
0323: Object[] services = ep.getServices();
0324: engine = new DebuggerEngine(
0325: ((DebuggerEngineProvider) engineProviders
0326: .get(j)).getEngineTypeID(), s,
0327: services, l);
0328: languages = ep.getLanguages();
0329: ep.setDestructor(engine.new Destructor());
0330: engines.add(engine);
0331: //S ystem.out.println("@ StartDebugging new Engine: " + engine);
0332: } else {
0333: DelegatingDebuggerEngineProvider dep = (DelegatingDebuggerEngineProvider) engineProviders
0334: .get(j);
0335: languages = dep.getLanguages();
0336: engine = dep.getEngine();
0337: dep.setDestructor(engine.new Destructor());
0338: //S ystem.out.println("@ StartDebugging DelegatingEngine: " + engine);
0339: }
0340: int w, ww = languages.length;
0341: for (w = 0; w < ww; w++)
0342: s.addLanguage(languages[w], engine);
0343: }
0344: }
0345:
0346: k = engines.size();
0347: for (i = 0; i < k; i++) {
0348: if (Thread.interrupted()) {
0349: break;
0350: }
0351: Task task = ((DebuggerEngine) engines.get(i))
0352: .getActionsManager().postAction(
0353: ActionsManager.ACTION_START);
0354: if (task instanceof Cancellable) {
0355: try {
0356: task.waitFinished(0);
0357: } catch (InterruptedException iex) {
0358: if (((Cancellable) task).cancel()) {
0359: break;
0360: } else {
0361: task.waitFinished();
0362: }
0363: }
0364: } else {
0365: task.waitFinished();
0366: }
0367: }
0368: if (i < k) { // It was canceled
0369: int n = i + 1;
0370: for (i = 0; i < k; i++) {
0371: ActionsManager am = ((DebuggerEngine) engines.get(i))
0372: .getActionsManager();
0373: if (i < (n - 1))
0374: am.postAction(ActionsManager.ACTION_KILL); // kill the started engines
0375: am.destroy();
0376: }
0377: return new DebuggerEngine[] {};
0378: }
0379:
0380: if (sessionToStart != null)
0381: setCurrentSession(sessionToStart);
0382:
0383: DebuggerEngine[] des = new DebuggerEngine[engines.size()];
0384: return (DebuggerEngine[]) engines.toArray(des);
0385: }
0386:
0387: /**
0388: * Kills all {@link org.netbeans.api.debugger.Session}s and
0389: * {@link org.netbeans.api.debugger.DebuggerEngine}s.
0390: */
0391: public void finishAllSessions() {
0392: Session[] ds = getSessions();
0393:
0394: if (ds.length == 0)
0395: return;
0396:
0397: // finish all non persistent sessions
0398: int i, k = ds.length;
0399: for (i = 0; i < k; i++)
0400: ds[i].getCurrentEngine().getActionsManager().doAction(
0401: ActionsManager.ACTION_KILL);
0402: }
0403:
0404: /**
0405: * Returns current debugger session or <code>null</code>.
0406: *
0407: * @return current debugger session or <code>null</code>
0408: */
0409: public Session getCurrentSession() {
0410: return currentSession;
0411: }
0412:
0413: /**
0414: * Sets current debugger session.
0415: *
0416: * @param session a session to be current
0417: */
0418: public void setCurrentSession(Session session) {
0419: Session oldSession;
0420: Session newSession;
0421: DebuggerEngine oldEngine;
0422: DebuggerEngine newEngine;
0423: synchronized (sessions) {
0424: // 1) check if the session is registerred
0425: if (session != null) {
0426: int i, k = sessions.size();
0427: for (i = 0; i < k; i++)
0428: if (session == sessions.get(i))
0429: break;
0430: if (i == k)
0431: return;
0432: }
0433:
0434: // fire all changes
0435: oldSession = getCurrentSession();
0436: if (session == oldSession)
0437: return;
0438: currentSession = newSession = session;
0439:
0440: oldEngine = currentEngine;
0441: newEngine = null;
0442: if (getCurrentSession() != null)
0443: newEngine = getCurrentSession().getCurrentEngine();
0444: currentEngine = newEngine;
0445: }
0446: if (oldEngine != newEngine) {
0447: firePropertyChange(PROP_CURRENT_ENGINE, oldEngine,
0448: newEngine);
0449: }
0450: firePropertyChange(PROP_CURRENT_SESSION, oldSession, newSession);
0451: }
0452:
0453: /**
0454: * Returns set of running debugger sessions.
0455: *
0456: * @return set of running debugger sessions
0457: */
0458: public Session[] getSessions() {
0459: synchronized (sessions) {
0460: return (Session[]) sessions.toArray(new Session[0]);
0461: }
0462: }
0463:
0464: /**
0465: * Returns set of running debugger engines.
0466: *
0467: * @return set of running debugger engines
0468: */
0469: public DebuggerEngine[] getDebuggerEngines() {
0470: synchronized (engines) {
0471: return (DebuggerEngine[]) engines
0472: .toArray(new DebuggerEngine[engines.size()]);
0473: }
0474: }
0475:
0476: /**
0477: * Returns current debugger engine or <code>null</code>.
0478: *
0479: * @return current debugger engine or <code>null</code>
0480: */
0481: public DebuggerEngine getCurrentEngine() {
0482: return currentEngine;
0483: }
0484:
0485: // breakpoints management ..................................................
0486:
0487: /**
0488: * Adds a new breakpoint.
0489: *
0490: * @param breakpoint a new breakpoint
0491: */
0492: public void addBreakpoint(Breakpoint breakpoint) {
0493: if (initBreakpoints(breakpoint)) {
0494: registerBreakpoint(breakpoint);
0495: breakpoints.addElement(breakpoint);
0496: fireBreakpointCreated(breakpoint, null);
0497: }
0498: }
0499:
0500: private void registerBreakpoint(Breakpoint breakpoint) {
0501: Class c = breakpoint.getClass();
0502: ClassLoader cl = c.getClassLoader();
0503: synchronized (breakpointsByClassLoaders) {
0504: Set<Breakpoint> lb = breakpointsByClassLoaders.get(cl);
0505: if (lb == null) {
0506: lb = new HashSet<Breakpoint>();
0507: breakpointsByClassLoaders.put(cl, lb);
0508: //moduleUnloadListeners.listenOn(cl);
0509: }
0510: lb.add(breakpoint);
0511: }
0512: }
0513:
0514: /**
0515: * Removes breakpoint.
0516: *
0517: * @param breakpoint a breakpoint to be removed
0518: */
0519: public void removeBreakpoint(Breakpoint breakpoint) {
0520: removeBreakpoint(breakpoint, false);
0521: }
0522:
0523: private void removeBreakpoint(Breakpoint breakpoint,
0524: boolean ignoreInitBreakpointsListeners) {
0525: if (!ignoreInitBreakpointsListeners) {
0526: initBreakpoints();
0527: Class c = breakpoint.getClass();
0528: ClassLoader cl = c.getClassLoader();
0529: synchronized (breakpointsByClassLoaders) {
0530: Set<Breakpoint> lb = breakpointsByClassLoaders.get(cl);
0531: if (lb != null) {
0532: lb.remove(breakpoint);
0533: if (lb.isEmpty()) {
0534: breakpointsByClassLoaders.remove(cl);
0535: }
0536: }
0537: }
0538: breakpoints.removeElement(breakpoint);
0539: breakpoint.disposeOut();
0540: } else {
0541: breakpoints.removeElement(breakpoint);
0542: breakpoint.dispose();
0543: }
0544: fireBreakpointRemoved(breakpoint,
0545: ignoreInitBreakpointsListeners, null);
0546: }
0547:
0548: /**
0549: * Gets all registered breakpoints.
0550: *
0551: * @return all breakpoints
0552: */
0553: public Breakpoint[] getBreakpoints() {
0554: initBreakpoints();
0555: return (Breakpoint[]) breakpoints.toArray(new Breakpoint[0]);
0556: }
0557:
0558: private void moduleUnloaded(ClassLoader cl) {
0559: Set<Breakpoint> lb;
0560: synchronized (breakpointsByClassLoaders) {
0561: lb = breakpointsByClassLoaders.remove(cl);
0562: }
0563: if (lb == null)
0564: return;
0565: for (Breakpoint b : lb) {
0566: removeBreakpoint(b, true);
0567: }
0568: }
0569:
0570: // watches management ......................................................
0571:
0572: /**
0573: * Creates a watch with its expression set to an initial value.
0574: * Also allows creation of a hidden watch (not presented to the user),
0575: * for example for internal use in the editor to obtain values of variables
0576: * under the mouse pointer.
0577: *
0578: * @param expr expression to watch for (the format is the responsibility
0579: * of the debugger plug-in implementation, but it is typically
0580: * a variable name).
0581: * @return the new watch
0582: */
0583: public Watch createWatch(String expr) {
0584: initWatches();
0585: Watch w = new Watch(expr);
0586: watches.addElement(w);
0587: fireWatchCreated(w);
0588: return w;
0589: }
0590:
0591: /**
0592: * Gets all shared watches in the system.
0593: *
0594: * @return all watches
0595: */
0596: public Watch[] getWatches() {
0597: initWatches();
0598: return (Watch[]) watches.toArray(new Watch[0]);
0599: }
0600:
0601: /**
0602: * Removes all watches from the system.
0603: */
0604: public void removeAllWatches() {
0605: initWatches();
0606: Vector v = (Vector) watches.clone();
0607: int i, k = v.size();
0608: for (i = k - 1; i >= 0; i--)
0609: ((Watch) v.elementAt(i)).remove();
0610: }
0611:
0612: /**
0613: * Removes watch.
0614: *
0615: * @param w watch to be removed
0616: */
0617: void removeWatch(Watch w) {
0618: initWatches();
0619: watches.removeElement(w);
0620: fireWatchRemoved(w);
0621: }
0622:
0623: // listenersMap ...............................................................
0624:
0625: /**
0626: * Fires property change.
0627: */
0628: private void firePropertyChange(String name, Object o, Object n) {
0629: initDebuggerManagerListeners();
0630: Vector l = (Vector) listeners.clone();
0631: Vector l1;
0632: synchronized (listenersMap) {
0633: l1 = (Vector) listenersMap.get(name);
0634: if (l1 != null)
0635: l1 = (Vector) l1.clone();
0636: }
0637: PropertyChangeEvent ev = new PropertyChangeEvent(this , name, o,
0638: n);
0639: int i, k = l.size();
0640: for (i = 0; i < k; i++)
0641: ((DebuggerManagerListener) l.elementAt(i))
0642: .propertyChange(ev);
0643: if (l1 != null) {
0644: k = l1.size();
0645: for (i = 0; i < k; i++)
0646: ((DebuggerManagerListener) l1.elementAt(i))
0647: .propertyChange(ev);
0648: }
0649: }
0650:
0651: /**
0652: * This listener notificates about changes of breakpoints, watches and threads.
0653: *
0654: * @param l listener object.
0655: */
0656: public void addDebuggerListener(DebuggerManagerListener l) {
0657: listeners.addElement(l);
0658: }
0659:
0660: /**
0661: * Removes debugger listener.
0662: *
0663: * @param l listener object.
0664: */
0665: public void removeDebuggerListener(DebuggerManagerListener l) {
0666: listeners.removeElement(l);
0667: }
0668:
0669: /**
0670: * Add a debuggerManager listener to changes of watches and breakpoints.
0671: *
0672: * @param propertyName a name of property to listen on
0673: * @param l the debuggerManager listener to add
0674: */
0675: public void addDebuggerListener(String propertyName,
0676: DebuggerManagerListener l) {
0677: synchronized (listenersMap) {
0678: Vector listeners = (Vector) listenersMap.get(propertyName);
0679: if (listeners == null) {
0680: listeners = new Vector();
0681: listenersMap.put(propertyName, listeners);
0682: }
0683: listeners.addElement(l);
0684: }
0685: }
0686:
0687: /**
0688: * Remove a debuggerManager listener to changes of watches and breakpoints.
0689: *
0690: * @param propertyName a name of property to listen on
0691: * @param l the debuggerManager listener to remove
0692: */
0693: public void removeDebuggerListener(String propertyName,
0694: DebuggerManagerListener l) {
0695: synchronized (listenersMap) {
0696: Vector listeners = (Vector) listenersMap.get(propertyName);
0697: if (listeners == null)
0698: return;
0699: listeners.removeElement(l);
0700: if (listeners.size() == 0)
0701: listenersMap.remove(propertyName);
0702: }
0703: }
0704:
0705: /**
0706: * Notifies registered listeners about a change.
0707: * Notifies {@link #listeners registered listeners} that a breakpoint
0708: * {@link DebuggerManagerListener#breakpointAdded was added}
0709: * and its properties
0710: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
0711: * were changed.
0712: *
0713: * @param breakpoint a breakpoint that was created
0714: */
0715: private void fireBreakpointCreated(final Breakpoint breakpoint,
0716: final DebuggerManagerListener originatingListener) {
0717: initDebuggerManagerListeners();
0718: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
0719: PROP_BREAKPOINTS, null, null);
0720:
0721: Vector l = (Vector) listeners.clone();
0722: int i, k = l.size();
0723: for (i = 0; i < k; i++) {
0724: DebuggerManagerListener dl = (DebuggerManagerListener) l
0725: .elementAt(i);
0726: if (dl != originatingListener) {
0727: dl.breakpointAdded(breakpoint);
0728: dl.propertyChange(ev);
0729: }
0730: }
0731:
0732: Vector l1;
0733: synchronized (listenersMap) {
0734: l1 = (Vector) listenersMap.get(PROP_BREAKPOINTS);
0735: if (l1 != null) {
0736: l1 = (Vector) l1.clone();
0737: }
0738: }
0739: if (l1 != null) {
0740: k = l1.size();
0741: for (i = 0; i < k; i++) {
0742: DebuggerManagerListener dl = (DebuggerManagerListener) l1
0743: .elementAt(i);
0744: if (dl != originatingListener) {
0745: dl.breakpointAdded(breakpoint);
0746: dl.propertyChange(ev);
0747: }
0748: }
0749: }
0750: }
0751:
0752: /**
0753: * Notifies registered listenersMap about a change.
0754: * Notifies {@link #listeners registered listenersMap} that a breakpoint
0755: * {@link DebuggerManagerListener#breakpointRemoved was removed}
0756: * and its properties
0757: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
0758: * were changed.
0759: *
0760: * @param breakpoint a breakpoint that was removed
0761: */
0762: private void fireBreakpointRemoved(final Breakpoint breakpoint,
0763: boolean ignoreInitBreakpointsListeners,
0764: DebuggerManagerListener ignoredListener) {
0765: initDebuggerManagerListeners();
0766: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
0767: PROP_BREAKPOINTS, null, null);
0768:
0769: Vector l = (Vector) listeners.clone();
0770: int i, k = l.size();
0771: for (i = 0; i < k; i++) {
0772: DebuggerManagerListener ml = (DebuggerManagerListener) l
0773: .elementAt(i);
0774: if (ml == ignoredListener)
0775: continue;
0776: Breakpoint[] bps;
0777: if (ignoreInitBreakpointsListeners
0778: && (bps = ml.initBreakpoints()) != null
0779: && bps.length > 0) {
0780: continue;
0781: }
0782: ml.breakpointRemoved(breakpoint);
0783: ml.propertyChange(ev);
0784: }
0785:
0786: Vector l1;
0787: synchronized (listenersMap) {
0788: l1 = (Vector) listenersMap.get(PROP_BREAKPOINTS);
0789: if (l1 != null) {
0790: l1 = (Vector) l1.clone();
0791: }
0792: }
0793: if (l1 != null) {
0794: k = l1.size();
0795: for (i = 0; i < k; i++) {
0796: DebuggerManagerListener ml = (DebuggerManagerListener) l1
0797: .elementAt(i);
0798: if (ml == ignoredListener)
0799: continue;
0800: Breakpoint[] bps;
0801: if (ignoreInitBreakpointsListeners
0802: && (bps = ml.initBreakpoints()) != null
0803: && bps.length > 0) {
0804: continue;
0805: }
0806: ml.breakpointRemoved(breakpoint);
0807: ml.propertyChange(ev);
0808: }
0809: }
0810: }
0811:
0812: private void initBreakpoints() {
0813: initBreakpoints(null);
0814: }
0815:
0816: private List<Breakpoint> createdBreakpoints;
0817: private Map<ClassLoader, Set<Breakpoint>> breakpointsByClassLoaders = new HashMap<ClassLoader, Set<Breakpoint>>();
0818:
0819: /**
0820: * @param newBreakpoint a breakpoint that is to be added if the breakpoints are not yet initialized.
0821: * @return true if the breakpoints were successfully initialized.
0822: */
0823: private boolean initBreakpoints(Breakpoint newBreakpoint) {
0824: // All is under the lock, including DebuggerManagerListener.initBreakpoints()
0825: // and DebuggerManagerListener.propertyChange(..PROP_BREAKPOINTS_INIT..) calls.
0826: // Clients should return the breakpoints via that listener, not add them
0827: // directly. Therefore this should not lead to deadlock...
0828: Map<Breakpoint, DebuggerManagerListener> originatingListeners;
0829: List<Breakpoint> breakpointsToAdd;
0830: synchronized (breakpoints) {
0831: if (breakpointsInitialized)
0832: return true;
0833: if (breakpointsInitializing) {
0834: if (newBreakpoint != null) {
0835: // Someone is trying to add new breakpoints during initialization process.
0836: // We must permit that doue to historical reasons - see web/jspdebug/src/org/netbeans/modules/web/debug/breakpoints/JspLineBreakpoint.java
0837: createdBreakpoints.add(newBreakpoint);
0838: return false;
0839: }
0840: throw new IllegalStateException(
0841: "Breakpoints not yet initialized and tried to initialize again...");
0842: }
0843: breakpointsInitializing = true;
0844: try {
0845: initDebuggerManagerListeners();
0846:
0847: createdBreakpoints = new ArrayList();
0848: originatingListeners = new HashMap();
0849:
0850: Vector l = (Vector) listeners.clone();
0851: int i, k = l.size();
0852: for (i = 0; i < k; i++) {
0853: DebuggerManagerListener dl = (DebuggerManagerListener) l
0854: .elementAt(i);
0855: Breakpoint[] breakpoints = dl.initBreakpoints();
0856: createdBreakpoints.addAll(Arrays
0857: .asList(breakpoints));
0858: for (int j = 0; j < breakpoints.length; j++) {
0859: originatingListeners.put(breakpoints[j], dl);
0860: }
0861: }
0862:
0863: Vector l1;
0864: synchronized (listenersMap) {
0865: l1 = (Vector) listenersMap
0866: .get(PROP_BREAKPOINTS_INIT);
0867: if (l1 != null) {
0868: l1 = (Vector) l1.clone();
0869: }
0870: }
0871: if (l1 != null) {
0872: k = l1.size();
0873: for (i = 0; i < k; i++) {
0874: DebuggerManagerListener dl = (DebuggerManagerListener) l1
0875: .elementAt(i);
0876: Breakpoint[] breakpoints = dl.initBreakpoints();
0877: createdBreakpoints.addAll(Arrays
0878: .asList(breakpoints));
0879: for (int j = 0; j < breakpoints.length; j++) {
0880: originatingListeners
0881: .put(breakpoints[j], dl);
0882: }
0883: }
0884: }
0885:
0886: breakpoints.addAll(createdBreakpoints);
0887: } finally {
0888: breakpointsInitializing = false;
0889: }
0890: breakpointsInitialized = true;
0891: breakpointsToAdd = createdBreakpoints;
0892: createdBreakpoints = null;
0893: }
0894: for (Breakpoint bp : breakpointsToAdd) {
0895: registerBreakpoint(bp);
0896: fireBreakpointCreated(bp, originatingListeners.get(bp));
0897: }
0898: return true;
0899: }
0900:
0901: private void addBreakpoints(DebuggerManagerListener dl) {
0902: if (!breakpointsInitialized) {
0903: return;
0904: }
0905: //System.err.println("\n addBreakpoints("+dl+")\n");
0906: Map<Breakpoint, DebuggerManagerListener> originatingListeners = new HashMap<Breakpoint, DebuggerManagerListener>();
0907: List<Breakpoint> breakpointsToAdd;
0908: synchronized (breakpoints) {
0909: breakpointsInitialized = false;
0910: breakpointsInitializing = true;
0911: try {
0912: createdBreakpoints = new ArrayList<Breakpoint>();
0913: Breakpoint[] bps = dl.initBreakpoints();
0914: createdBreakpoints.addAll(Arrays.asList(bps));
0915: for (int j = 0; j < bps.length; j++) {
0916: originatingListeners.put(bps[j], dl);
0917: }
0918: //System.err.println("createdBreakpoints = "+createdBreakpoints);
0919: breakpoints.addAll(createdBreakpoints);
0920: } finally {
0921: breakpointsInitializing = false;
0922: breakpointsInitialized = true;
0923: }
0924: breakpointsToAdd = createdBreakpoints;
0925: createdBreakpoints = null;
0926: }
0927: //System.err.println("createdBreakpoints = "+breakpointsToAdd);
0928: for (Breakpoint bp : breakpointsToAdd) {
0929: registerBreakpoint(bp);
0930: fireBreakpointCreated(bp, originatingListeners.get(bp));
0931: }
0932: }
0933:
0934: private void removeBreakpoints(DebuggerManagerListener dl) {
0935: if (!breakpointsInitialized) {
0936: return;
0937: }
0938: Breakpoint[] bps;
0939: try {
0940: java.lang.reflect.Method unloadMethod = dl.getClass()
0941: .getMethod("unloadBreakpoints", new Class[] {});
0942: bps = (Breakpoint[]) unloadMethod.invoke(dl,
0943: new Object[] {});
0944: } catch (Exception exc) {
0945: return;
0946: }
0947: //Breakpoint[] bps = dl.unloadBreakpoints();
0948: //System.err.println("\n removeBreakpoints("+dl+")\n");
0949: breakpoints.removeAll(Arrays.asList(bps));
0950: for (Breakpoint breakpoint : bps) {
0951: Class c = breakpoint.getClass();
0952: ClassLoader cl = c.getClassLoader();
0953: synchronized (breakpointsByClassLoaders) {
0954: Set<Breakpoint> lb = breakpointsByClassLoaders.get(cl);
0955: if (lb != null) {
0956: lb.remove(breakpoint);
0957: if (lb.isEmpty()) {
0958: breakpointsByClassLoaders.remove(cl);
0959: }
0960: }
0961: }
0962: breakpoint.disposeOut();
0963: }
0964: //System.err.println("removedBreakpoints = "+Arrays.asList(bps));
0965: for (Breakpoint bp : bps) {
0966: fireBreakpointRemoved(bp, false, dl);
0967: }
0968: }
0969:
0970: /**
0971: * Notifies registered listeners about a change.
0972: * Notifies {@link #listeners registered listeners} that a watch
0973: * {@link DebuggerManagerListener#watchAdded was added}
0974: * and its properties
0975: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
0976: * were changed.
0977: *
0978: * @param watch a watch that was created
0979: */
0980: private void fireWatchCreated(final Watch watch) {
0981: initDebuggerManagerListeners();
0982: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
0983: PROP_WATCHES, null, null);
0984:
0985: Vector l = (Vector) listeners.clone();
0986: int i, k = l.size();
0987: for (i = 0; i < k; i++) {
0988: ((DebuggerManagerListener) l.elementAt(i))
0989: .watchAdded(watch);
0990: ((DebuggerManagerListener) l.elementAt(i))
0991: .propertyChange(ev);
0992: }
0993:
0994: Vector l1;
0995: synchronized (listenersMap) {
0996: l1 = (Vector) listenersMap.get(PROP_WATCHES);
0997: if (l1 != null) {
0998: l1 = (Vector) l1.clone();
0999: }
1000: }
1001: if (l1 != null) {
1002: k = l1.size();
1003: for (i = 0; i < k; i++) {
1004: ((DebuggerManagerListener) l1.elementAt(i))
1005: .watchAdded(watch);
1006: ((DebuggerManagerListener) l1.elementAt(i))
1007: .propertyChange(ev);
1008: }
1009: }
1010: }
1011:
1012: /**
1013: * Notifies registered listeners about a change.
1014: * Notifies {@link #listeners registered listeners} that a watch
1015: * {@link DebuggerManagerListener#watchRemoved was removed}
1016: * and its properties
1017: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
1018: * were changed.
1019: *
1020: * @param watch a watch that was removed
1021: */
1022: private void fireWatchRemoved(final Watch watch) {
1023: initDebuggerManagerListeners();
1024: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
1025: PROP_WATCHES, null, null);
1026:
1027: Vector l = (Vector) listeners.clone();
1028: int i, k = l.size();
1029: for (i = 0; i < k; i++) {
1030: ((DebuggerManagerListener) l.elementAt(i))
1031: .watchRemoved(watch);
1032: // TODO: fix nonsense double firing
1033: ((DebuggerManagerListener) l.elementAt(i))
1034: .propertyChange(ev);
1035: }
1036:
1037: Vector l1;
1038: synchronized (listenersMap) {
1039: l1 = (Vector) listenersMap.get(PROP_WATCHES);
1040: if (l1 != null) {
1041: l1 = (Vector) l1.clone();
1042: }
1043: }
1044: if (l1 != null) {
1045: k = l1.size();
1046: for (i = 0; i < k; i++) {
1047: ((DebuggerManagerListener) l1.elementAt(i))
1048: .watchRemoved(watch);
1049: // TODO: fix nonsense double firing
1050: ((DebuggerManagerListener) l1.elementAt(i))
1051: .propertyChange(ev);
1052: }
1053: }
1054: }
1055:
1056: private void initWatches() {
1057: synchronized (watches) {
1058: if (watchesInitialized)
1059: return;
1060: watchesInitialized = true;
1061: }
1062: initDebuggerManagerListeners();
1063: // The rest must not be synchronized, since initWatches() does call createWatch()
1064: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
1065: PROP_WATCHES_INIT, null, null);
1066:
1067: Vector l = (Vector) listeners.clone();
1068: int i, k = l.size();
1069: for (i = 0; i < k; i++) {
1070: ((DebuggerManagerListener) l.elementAt(i)).initWatches();
1071: ((DebuggerManagerListener) l.elementAt(i))
1072: .propertyChange(ev);
1073: }
1074:
1075: Vector l1;
1076: synchronized (listenersMap) {
1077: l1 = (Vector) listenersMap.get(PROP_WATCHES_INIT);
1078: if (l1 != null) {
1079: l1 = (Vector) l1.clone();
1080: }
1081: }
1082: if (l1 != null) {
1083: k = l1.size();
1084: for (i = 0; i < k; i++) {
1085: ((DebuggerManagerListener) l1.elementAt(i))
1086: .initWatches();
1087: ((DebuggerManagerListener) l1.elementAt(i))
1088: .propertyChange(ev);
1089: }
1090: }
1091: }
1092:
1093: /**
1094: * Notifies registered listeners about a change.
1095: * Notifies {@link #listeners registered listeners} that a session
1096: * {@link DebuggerManagerListener#sessionAdded was added}
1097: * and its properties
1098: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
1099: * were changed.
1100: *
1101: * @param session a session that was created
1102: */
1103: private void fireSessionAdded(final Session session,
1104: final Session[] old, final Session[] ne) {
1105: initDebuggerManagerListeners();
1106: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
1107: PROP_SESSIONS, old, ne);
1108:
1109: Vector l = (Vector) listeners.clone();
1110: int i, k = l.size();
1111: for (i = 0; i < k; i++) {
1112: ((DebuggerManagerListener) l.elementAt(i))
1113: .sessionAdded(session);
1114: ((DebuggerManagerListener) l.elementAt(i))
1115: .propertyChange(ev);
1116: }
1117:
1118: Vector l1;
1119: synchronized (listenersMap) {
1120: l1 = (Vector) listenersMap.get(PROP_SESSIONS);
1121: if (l1 != null) {
1122: l1 = (Vector) l1.clone();
1123: }
1124: }
1125: if (l1 != null) {
1126: k = l1.size();
1127: for (i = 0; i < k; i++) {
1128: ((DebuggerManagerListener) l1.elementAt(i))
1129: .sessionAdded(session);
1130: ((DebuggerManagerListener) l1.elementAt(i))
1131: .propertyChange(ev);
1132: }
1133: }
1134: }
1135:
1136: /**
1137: * Notifies registered listeners about a change.
1138: * Notifies {@link #listeners registered listeners} that a session
1139: * {@link DebuggerManagerListener#sessionRemoved was removed}
1140: * and its properties
1141: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
1142: * were changed.
1143: *
1144: * @param session a session that was removed
1145: */
1146: private void fireSessionRemoved(final Session session,
1147: final Session[] old, final Session[] ne) {
1148: initDebuggerManagerListeners();
1149: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
1150: PROP_SESSIONS, old, ne);
1151:
1152: Vector l = (Vector) listeners.clone();
1153: int i, k = l.size();
1154: for (i = 0; i < k; i++) {
1155: ((DebuggerManagerListener) l.elementAt(i))
1156: .sessionRemoved(session);
1157: ((DebuggerManagerListener) l.elementAt(i))
1158: .propertyChange(ev);
1159: }
1160:
1161: Vector l1;
1162: synchronized (listenersMap) {
1163: l1 = (Vector) listenersMap.get(PROP_SESSIONS);
1164: if (l1 != null) {
1165: l1 = (Vector) l1.clone();
1166: }
1167: }
1168: if (l1 != null) {
1169: k = l1.size();
1170: for (i = 0; i < k; i++) {
1171: ((DebuggerManagerListener) l1.elementAt(i))
1172: .sessionRemoved(session);
1173: ((DebuggerManagerListener) l1.elementAt(i))
1174: .propertyChange(ev);
1175: }
1176: }
1177: }
1178:
1179: /**
1180: * Notifies registered listeners about a change.
1181: * Notifies {@link #listeners registered listeners} that a engine
1182: * {@link DebuggerManagerListener#engineAdded was added}
1183: * and its properties
1184: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
1185: * were changed.
1186: *
1187: * @param engine a engine that was created
1188: */
1189: private void fireEngineAdded(final DebuggerEngine engine,
1190: final DebuggerEngine[] old, final DebuggerEngine[] ne) {
1191: initDebuggerManagerListeners();
1192: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
1193: PROP_DEBUGGER_ENGINES, old, ne);
1194:
1195: Vector l = (Vector) listeners.clone();
1196: int i, k = l.size();
1197: for (i = 0; i < k; i++) {
1198: ((DebuggerManagerListener) l.elementAt(i))
1199: .engineAdded(engine);
1200: ((DebuggerManagerListener) l.elementAt(i))
1201: .propertyChange(ev);
1202: }
1203:
1204: Vector l1;
1205: synchronized (listenersMap) {
1206: l1 = (Vector) listenersMap.get(PROP_DEBUGGER_ENGINES);
1207: if (l1 != null) {
1208: l1 = (Vector) l1.clone();
1209: }
1210: }
1211: if (l1 != null) {
1212: k = l1.size();
1213: for (i = 0; i < k; i++) {
1214: ((DebuggerManagerListener) l1.elementAt(i))
1215: .engineAdded(engine);
1216: ((DebuggerManagerListener) l1.elementAt(i))
1217: .propertyChange(ev);
1218: }
1219: }
1220: }
1221:
1222: /**
1223: * Notifies registered listeners about a change.
1224: * Notifies {@link #listeners registered listeners} that a engine
1225: * {@link DebuggerManagerListener#engineRemoved was removed}
1226: * and its properties
1227: * {@link PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)}
1228: * were changed.
1229: *
1230: * @param engine a engine that was removed
1231: */
1232: private void fireEngineRemoved(final DebuggerEngine engine,
1233: final DebuggerEngine[] old, final DebuggerEngine[] ne) {
1234: initDebuggerManagerListeners();
1235: PropertyChangeEvent ev = new PropertyChangeEvent(this ,
1236: PROP_DEBUGGER_ENGINES, old, ne);
1237:
1238: Vector l = (Vector) listeners.clone();
1239: int i, k = l.size();
1240: for (i = 0; i < k; i++) {
1241: ((DebuggerManagerListener) l.elementAt(i))
1242: .engineRemoved(engine);
1243: ((DebuggerManagerListener) l.elementAt(i))
1244: .propertyChange(ev);
1245: }
1246:
1247: Vector l1;
1248: synchronized (listenersMap) {
1249: l1 = (Vector) listenersMap.get(PROP_DEBUGGER_ENGINES);
1250: if (l1 != null) {
1251: l1 = (Vector) l1.clone();
1252: }
1253: }
1254: if (l1 != null) {
1255: k = l1.size();
1256: for (i = 0; i < k; i++) {
1257: ((DebuggerManagerListener) l1.elementAt(i))
1258: .engineRemoved(engine);
1259: ((DebuggerManagerListener) l1.elementAt(i))
1260: .propertyChange(ev);
1261: }
1262: }
1263: }
1264:
1265: // helper methods ....................................................
1266:
1267: private Set<LazyDebuggerManagerListener> loadedListeners;
1268: private List<? extends LazyDebuggerManagerListener> listenersLookupList;
1269:
1270: private void initDebuggerManagerListeners() {
1271: synchronized (listenersMap) {
1272: if (loadedListeners == null) {
1273: loadedListeners = new HashSet<LazyDebuggerManagerListener>();
1274: listenersLookupList = lookup.lookup(null,
1275: LazyDebuggerManagerListener.class);
1276: refreshDebuggerManagerListeners(listenersLookupList);
1277: ((Customizer) listenersLookupList)
1278: .addPropertyChangeListener(new PropertyChangeListener() {
1279:
1280: public void propertyChange(
1281: PropertyChangeEvent evt) {
1282: refreshDebuggerManagerListeners((List<? extends LazyDebuggerManagerListener>) evt
1283: .getSource());
1284: }
1285: });
1286: }
1287:
1288: }
1289: }
1290:
1291: private void refreshDebuggerManagerListeners(
1292: List<? extends LazyDebuggerManagerListener> listenersLookupList) {
1293: //System.err.println("\n refreshDebuggerManagerListeners()");
1294: //It's neccessary to pay attention on the order in which the listeners and breakpoints are registered!
1295: //Annotation listeners must be unregistered AFTER breakpoints are removed
1296: //and registered back BEFORE breakpoints are loaded
1297: Set<LazyDebuggerManagerListener> addedInitBreakpointsListeners = new HashSet<LazyDebuggerManagerListener>();
1298: Set<ClassLoader> uninstalledModules = new HashSet<ClassLoader>();
1299: synchronized (listenersLookupList) {
1300: int i, k = listenersLookupList.size();
1301: //System.err.println("size() = "+k+", content = "+listenersLookupList+"\n");
1302: for (i = 0; i < k; i++) {
1303: LazyDebuggerManagerListener l = listenersLookupList
1304: .get(i);
1305: if (loadedListeners.add(l)) {
1306: String[] props = l.getProperties();
1307: if ((props == null) || (props.length == 0)) {
1308: addDebuggerListener(l);
1309: } else {
1310: int j, jj = props.length;
1311: for (j = 0; j < jj; j++) {
1312: addDebuggerListener(props[j], l);
1313: }
1314: }
1315: //System.err.println("ADDED listener: "+l);
1316: addedInitBreakpointsListeners.add(l);
1317: }
1318: }
1319: Set<LazyDebuggerManagerListener> listenersToRemove = new HashSet<LazyDebuggerManagerListener>(
1320: loadedListeners);
1321: listenersToRemove.removeAll(listenersLookupList);
1322: for (LazyDebuggerManagerListener l : listenersToRemove) {
1323: if (loadedListeners.contains(l)) {
1324: removeBreakpoints(l);
1325: }
1326: }
1327: for (LazyDebuggerManagerListener l : listenersToRemove) {
1328: if (loadedListeners.remove(l)) {
1329: moduleUnloaded(l.getClass().getClassLoader());
1330: String[] props = l.getProperties();
1331: if ((props == null) || (props.length == 0)) {
1332: removeDebuggerListener(l);
1333: } else {
1334: int j, jj = props.length;
1335: for (j = 0; j < jj; j++) {
1336: removeDebuggerListener(props[j], l);
1337: }
1338: }
1339: //System.err.println("REMOVED listener: "+l);
1340: }
1341: }
1342: }
1343: for (LazyDebuggerManagerListener l : addedInitBreakpointsListeners) {
1344: addBreakpoints(l);
1345: }
1346: }
1347:
1348: private void addSession(Session session) {
1349: Session[] oldSessions;
1350: Session[] newSessions;
1351: synchronized (sessions) {
1352: oldSessions = getSessions();
1353: int i, k = oldSessions.length;
1354: for (i = 0; i < k; i++)
1355: if (session == oldSessions[i])
1356: return;
1357:
1358: newSessions = new Session[oldSessions.length + 1];
1359: System.arraycopy(oldSessions, 0, newSessions, 0,
1360: oldSessions.length);
1361: newSessions[oldSessions.length] = session;
1362: this .sessions.add(session);
1363:
1364: session.addPropertyChangeListener(sessionListener);
1365: }
1366: fireSessionAdded(session, oldSessions, newSessions);
1367: }
1368:
1369: private void removeSession(Session session) {
1370: Session[] oldSessions;
1371: Session[] newSessions;
1372: Session nCurrentSesson = null;
1373: synchronized (sessions) {
1374: oldSessions = getSessions();
1375: // find index of given debugger and new instance of currentDebugger
1376: int i, k = oldSessions.length;
1377: for (i = 0; i < k; i++) {
1378: if (oldSessions[i] == session) {
1379: break;
1380: } else if (nCurrentSesson == null) {
1381: nCurrentSesson = oldSessions[i];
1382: }
1383: }
1384: if (i == k)
1385: return; // this debugger is not registered
1386:
1387: // set new current debugger session
1388: if (session == getCurrentSession()) {
1389: if ((nCurrentSesson == null) && (k > 1))
1390: nCurrentSesson = oldSessions[1];
1391: } else {
1392: nCurrentSesson = getCurrentSession();
1393: }
1394:
1395: newSessions = new Session[oldSessions.length - 1];
1396: System.arraycopy(oldSessions, 0, newSessions, 0, i);
1397: if ((oldSessions.length - i) > 1)
1398: System.arraycopy(oldSessions, i + 1, newSessions, i,
1399: oldSessions.length - i - 1);
1400: sessions.remove(i);
1401:
1402: session.removePropertyChangeListener(sessionListener);
1403: // The current engine is set in setCurrentSession().
1404: }
1405: setCurrentSession(nCurrentSesson);
1406: fireSessionRemoved(session, oldSessions, newSessions);
1407: }
1408:
1409: void addEngine(DebuggerEngine engine) {
1410: DebuggerEngine[] old;
1411: DebuggerEngine[] ne;
1412: synchronized (engines) {
1413: if (engines.contains(engine))
1414: return;
1415: old = (DebuggerEngine[]) engines
1416: .toArray(new DebuggerEngine[engines.size()]);
1417: engines.add(engine);
1418: ne = (DebuggerEngine[]) engines
1419: .toArray(new DebuggerEngine[engines.size()]);
1420: }
1421: fireEngineAdded(engine, old, ne);
1422: }
1423:
1424: void removeEngine(DebuggerEngine engine) {
1425: DebuggerEngine[] old;
1426: DebuggerEngine[] ne;
1427: synchronized (engines) {
1428: if (!engines.contains(engine))
1429: return;
1430: old = (DebuggerEngine[]) engines
1431: .toArray(new DebuggerEngine[engines.size()]);
1432: engines.remove(engine);
1433: ne = (DebuggerEngine[]) engines
1434: .toArray(new DebuggerEngine[engines.size()]);
1435: }
1436: fireEngineRemoved(engine, old, ne);
1437: }
1438:
1439: // innerclasses ............................................................
1440:
1441: /**
1442: * Listens on all engines and sessions for:
1443: * current thread changes
1444: * start / finish of engines
1445: * last action
1446: * current engine
1447: */
1448: private class SessionListener implements PropertyChangeListener {
1449: public void propertyChange(PropertyChangeEvent e) {
1450: if (e.getSource() instanceof Session) {
1451: if ((!e.getPropertyName().equals(
1452: Session.PROP_CURRENT_LANGUAGE))
1453: && (!e.getPropertyName().equals(
1454: Session.PROP_SUPPORTED_LANGUAGES)))
1455: return;
1456: // update the current engine
1457: DebuggerEngine oldEngine;
1458: DebuggerEngine newEngine;
1459: synchronized (sessions) {
1460: oldEngine = currentEngine;
1461: newEngine = null;
1462: if (getCurrentSession() != null)
1463: newEngine = getCurrentSession()
1464: .getCurrentEngine();
1465: currentEngine = newEngine;
1466: }
1467: if (newEngine != oldEngine) {
1468: firePropertyChange(PROP_CURRENT_ENGINE, oldEngine,
1469: newEngine);
1470: }
1471: Session s = (Session) e.getSource();
1472: if (s.getSupportedLanguages().length == 0)
1473: removeSession(s);
1474: }
1475: }
1476: }
1477:
1478: /*
1479: private class ModuleUnloadListeners {
1480:
1481: private Map<ClassLoader, ModuleChangeListener> moduleChangeListeners
1482: = new HashMap<ClassLoader, ModuleChangeListener>();
1483:
1484: public void listenOn(ClassLoader cl) {
1485: /*
1486: org.openide.util.Lookup.Result<ModuleInfo> moduleLookupResult =
1487: org.openide.util.Lookup.getDefault ().lookup(
1488: new org.openide.util.Lookup.Template<ModuleInfo>(ModuleInfo.class));
1489: synchronized(moduleChangeListeners) {
1490: if (!moduleChangeListeners.containsKey(cl)) {
1491: for (ModuleInfo mi : moduleLookupResult.allInstances()) {
1492: if (mi.isEnabled() && mi.getClassLoader() == cl) {
1493: ModuleChangeListener l = new ModuleChangeListener(cl);
1494: mi.addPropertyChangeListener(WeakListeners.propertyChange(l, mi));
1495: moduleChangeListeners.put(cl, l);
1496: }
1497: }
1498: }
1499: }
1500: *//*
1501: }
1502:
1503: private final class ModuleChangeListener implements PropertyChangeListener {
1504:
1505: private ClassLoader cl;
1506:
1507: public ModuleChangeListener(ClassLoader cl) {
1508: this.cl = cl;
1509: }
1510:
1511: public void propertyChange(PropertyChangeEvent evt) {
1512: ModuleInfo mi = (ModuleInfo) evt.getSource();
1513: if (!mi.isEnabled()) {
1514: synchronized (moduleChangeListeners) {
1515: moduleChangeListeners.remove(cl);
1516: }
1517: moduleUnloaded(cl);
1518: }
1519: }
1520:
1521: }
1522: }
1523: */
1524: }
|