diff -r d86a5e4e1ac5 api.debugger.jpda/apichanges.xml --- a/api.debugger.jpda/apichanges.xml Tue Feb 19 12:00:33 2013 +0100 +++ b/api.debugger.jpda/apichanges.xml Wed Feb 20 14:31:06 2013 +0100 @@ -816,6 +816,24 @@ + + + Breakpoints can deactivated. + + + + + + Two methods are added to JPDADebugger class: areBreakpointsActive() + and setBreakpointsActive(boolean). The set method fires PROP_BREAKPOINTS_ACTIVE + event.
+ These methods are used to activate/deactivate all breakpoints in the + debugger session. +
+ + +
+ diff -r d86a5e4e1ac5 api.debugger.jpda/manifest.mf --- a/api.debugger.jpda/manifest.mf Tue Feb 19 12:00:33 2013 +0100 +++ b/api.debugger.jpda/manifest.mf Wed Feb 20 14:31:06 2013 +0100 @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.api.debugger.jpda/2 OpenIDE-Module-Localizing-Bundle: org/netbeans/api/debugger/jpda/Bundle.properties -OpenIDE-Module-Specification-Version: 2.41 +OpenIDE-Module-Specification-Version: 2.42 OpenIDE-Module-Package-Dependencies: com.sun.jdi[VirtualMachineManager] diff -r d86a5e4e1ac5 api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java --- a/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java Tue Feb 19 12:00:33 2013 +0100 +++ b/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java Wed Feb 20 14:31:06 2013 +0100 @@ -107,6 +107,9 @@ /** Property name constant. * @since 2.25 */ public static final String PROP_CLASSES_FIXED = "classesFixed"; // NOI18N + /** Property name constant. Fired when breakpoints are activated / deactivated. + * @since 2.42 */ + public static final String PROP_BREAKPOINTS_ACTIVE = "breakpointsActive"; // NOI18N /** Suspend property value constant. */ public static final int SUSPEND_ALL = EventRequest.SUSPEND_ALL; @@ -488,6 +491,32 @@ } /** + * Test, if breakpoints are active. + * @return true when breakpoints are active, false + * otherwise. The default implementation returns true, to be overridden + * when needed. + * @since 2.42 + */ + public boolean areBreakpointsActive() { + return true; + } + + /** + * Set all breakpoints to be active / inactive. + * Activation or deactivation of breakpoints should not alter the enabled/disabled + * state of individual breakpoints. + * The default implementation throws UnsupportedOperationException, override + * together with {@link #areBreakpointsActive()} when needed. + * @param active true to make all breakpoints active, + * false to make all breakpoints inactive. + * @throws UnsupportedOperationException when not supported. + * @since 2.42 + */ + public void setBreakpointsActive(boolean active) { + throw new UnsupportedOperationException("This method must be overridden."); + } + + /** * Adds property change listener. * * @param l new listener. diff -r d86a5e4e1ac5 debugger.jpda.projects/src/org/netbeans/modules/debugger/jpda/projects/BreakpointAnnotationProvider.java --- a/debugger.jpda.projects/src/org/netbeans/modules/debugger/jpda/projects/BreakpointAnnotationProvider.java Tue Feb 19 12:00:33 2013 +0100 +++ b/debugger.jpda.projects/src/org/netbeans/modules/debugger/jpda/projects/BreakpointAnnotationProvider.java Wed Feb 20 14:31:06 2013 +0100 @@ -63,14 +63,18 @@ import org.netbeans.api.debugger.Breakpoint.VALIDITY; import org.netbeans.api.debugger.DebuggerEngine; import org.netbeans.api.debugger.DebuggerManager; +import org.netbeans.api.debugger.DebuggerManagerAdapter; import org.netbeans.api.debugger.DebuggerManagerListener; +import org.netbeans.api.debugger.LazyDebuggerManagerListener; import org.netbeans.api.debugger.Session; import org.netbeans.api.debugger.Watch; import org.netbeans.api.debugger.jpda.ClassLoadUnloadBreakpoint; import org.netbeans.api.debugger.jpda.FieldBreakpoint; import org.netbeans.api.debugger.jpda.JPDABreakpoint; +import org.netbeans.api.debugger.jpda.JPDADebugger; import org.netbeans.api.debugger.jpda.LineBreakpoint; import org.netbeans.api.debugger.jpda.MethodBreakpoint; +import org.netbeans.spi.debugger.DebuggerServiceRegistration; import org.netbeans.spi.debugger.jpda.EditorContext; import org.openide.cookies.LineCookie; @@ -94,14 +98,18 @@ * @author Jan Jancura, Martin Entlicher */ @org.openide.util.lookup.ServiceProvider(service=org.openide.text.AnnotationProvider.class) -public class BreakpointAnnotationProvider implements AnnotationProvider, - DebuggerManagerListener { +@DebuggerServiceRegistration(types=LazyDebuggerManagerListener.class) +public class BreakpointAnnotationProvider extends DebuggerManagerAdapter + implements AnnotationProvider/*, + DebuggerManagerListener*/ { private final Map> breakpointToAnnotations = new IdentityHashMap>(); private final Set annotatedFiles = new WeakSet(); private Set dataObjectListeners; private boolean attachManagerListener = true; + private volatile JPDADebugger currentDebugger = null; + private volatile boolean breakpointsActive = true; private RequestProcessor annotationProcessor = new RequestProcessor("Annotation Refresh", 1); private RequestProcessor contextWaitingProcessor = new RequestProcessor("Annotation Refresh Context Waiting", 1); @@ -176,6 +184,11 @@ } @Override + public String[] getProperties() { + return new String[] { DebuggerManager.PROP_BREAKPOINTS, DebuggerManager.PROP_DEBUGGER_ENGINES }; + } + + @Override public void breakpointAdded(Breakpoint breakpoint) { if (isAnnotatable(breakpoint)) { JPDABreakpoint b = (JPDABreakpoint) breakpoint; @@ -207,6 +220,30 @@ if (propertyName == null) { return; } + if (DebuggerManager.PROP_CURRENT_ENGINE.equals(propertyName)) { + JPDADebugger oldDebugger = currentDebugger; + if (oldDebugger != null) { + oldDebugger.removePropertyChangeListener(JPDADebugger.PROP_BREAKPOINTS_ACTIVE, this); + } + DebuggerEngine engine = DebuggerManager.getDebuggerManager().getCurrentEngine(); + boolean active = true; + JPDADebugger debugger = null; + if (engine != null) { + debugger = engine.lookupFirst(null, JPDADebugger.class); + if (debugger != null) { + debugger.addPropertyChangeListener(JPDADebugger.PROP_BREAKPOINTS_ACTIVE, this); + active = debugger.areBreakpointsActive(); + } + } + currentDebugger = debugger; + setBreakpointsActive(active); + } + if (JPDADebugger.PROP_BREAKPOINTS_ACTIVE.equals(propertyName)) { + JPDADebugger debugger = currentDebugger; + if (debugger != null) { + setBreakpointsActive(debugger.areBreakpointsActive()); + } + } if ( (!JPDABreakpoint.PROP_ENABLED.equals (propertyName)) && (!JPDABreakpoint.PROP_VALIDITY.equals (propertyName)) && (!LineBreakpoint.PROP_CONDITION.equals (propertyName)) && @@ -238,6 +275,13 @@ annotationProcessor.post(new AnnotationRefresh(b, true, true)); } + private void setBreakpointsActive(boolean active) { + if (breakpointsActive == active) { + return ; + } + annotationProcessor.post(new AnnotationRefresh(null, true, true)); + } + private final class AnnotationRefresh implements Runnable { private JPDABreakpoint b; @@ -252,17 +296,28 @@ @Override public void run() { synchronized (breakpointToAnnotations) { - if (remove) { - removeAnnotations(b); - if (!add) { - breakpointToAnnotations.remove(b); + if (b != null) { + refreshAnnotation(b); + } else { + List bpts = new ArrayList(breakpointToAnnotations.keySet()); + for (JPDABreakpoint bp : bpts) { + refreshAnnotation(bp); } } - if (add) { - breakpointToAnnotations.put(b, new WeakSet()); - for (FileObject fo : annotatedFiles) { - addAnnotationTo(b, fo); - } + } + } + + private void refreshAnnotation(JPDABreakpoint b) { + if (remove) { + removeAnnotations(b); + if (!add) { + breakpointToAnnotations.remove(b); + } + } + if (add) { + breakpointToAnnotations.put(b, new WeakSet()); + for (FileObject fo : annotatedFiles) { + addAnnotationTo(b, fo); } } } @@ -277,7 +332,8 @@ !((JPDABreakpoint) b).isHidden(); } - private static String getAnnotationType(JPDABreakpoint b, boolean isConditional) { + private static String getAnnotationType(JPDABreakpoint b, boolean isConditional, + boolean active) { boolean isInvalid = b.getValidity() == VALIDITY.INVALID; String annotationType; if (b instanceof LineBreakpoint) { @@ -301,8 +357,10 @@ } else { throw new IllegalStateException(b.toString()); } - if (isInvalid && b.isEnabled ()) { - annotationType += "_broken"; + if (!active) { + annotationType = annotationType + "_stroke"; // NOI18N + } else if (isInvalid && b.isEnabled ()) { + annotationType += "_broken"; // NOI18N } return annotationType; } @@ -481,7 +539,7 @@ throw new IllegalStateException(b.toString()); } boolean isConditional = (condition != null) && condition.trim().length() > 0; - String annotationType = getAnnotationType(b, isConditional); + String annotationType = getAnnotationType(b, isConditional, breakpointsActive); DataObject dataObject; try { dataObject = DataObject.find(fo); @@ -526,7 +584,7 @@ } } - + /* // Not used @Override public Breakpoint[] initBreakpoints() { return new Breakpoint[] {}; } @@ -558,5 +616,5 @@ // Not used @Override public void engineRemoved(DebuggerEngine engine) {} - + */ } diff -r d86a5e4e1ac5 debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/resources/mf-layer.xml --- a/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/resources/mf-layer.xml Tue Feb 19 12:00:33 2013 +0100 +++ b/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/resources/mf-layer.xml Wed Feb 20 14:31:06 2013 +0100 @@ -291,6 +291,23 @@ + + + + + + + + + + + + + + + + diff -r d86a5e4e1ac5 debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java Tue Feb 19 12:00:33 2013 +0100 +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java Wed Feb 20 14:31:06 2013 +0100 @@ -206,6 +206,7 @@ private boolean doContinue = true; // Whether resume() will actually resume private Boolean singleThreadStepResumeDecision = null; private Boolean stepInterruptByBptResumeDecision = null; + private boolean breakpointsActive = true; private InputOutput io; @@ -554,6 +555,22 @@ } } + @Override + public boolean areBreakpointsActive() { + return breakpointsActive; + } + + @Override + public void setBreakpointsActive(boolean active) { + synchronized (this) { + if (breakpointsActive == active) { + return ; + } + breakpointsActive = active; + } + firePropertyChange(PROP_BREAKPOINTS_ACTIVE, !active, active); + } + public Session getSession() { return lookupProvider.lookupFirst(null, Session.class); } diff -r d86a5e4e1ac5 debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/BreakpointImpl.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/BreakpointImpl.java Tue Feb 19 12:00:33 2013 +0100 +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/BreakpointImpl.java Wed Feb 20 14:31:06 2013 +0100 @@ -133,6 +133,7 @@ */ final void set () { breakpoint.addPropertyChangeListener (this); + debugger.addPropertyChangeListener(JPDADebugger.PROP_BREAKPOINTS_ACTIVE, this); if (breakpoint instanceof PropertyChangeListener && isApplicable()) { Session s = debugger.getSession(); DebuggerEngine de = s.getEngineForLanguage ("Java"); @@ -159,7 +160,7 @@ (getDebugger ().getState () == JPDADebugger.STATE_DISCONNECTED) ) return; removeAllEventRequests (); - if (breakpoint.isEnabled () && isEnabled()) { + if (breakpoint.isEnabled () && isEnabled() && debugger.areBreakpointsActive()) { setRequests (); } } @@ -186,7 +187,7 @@ } else if (!Breakpoint.PROP_VALIDITY.equals(propertyName) && !Breakpoint.PROP_GROUP_NAME.equals(propertyName) && !Breakpoint.PROP_GROUP_PROPERTIES.equals(propertyName)) { - if (reader != null) { + if (reader != null && !JPDADebugger.PROP_BREAKPOINTS_ACTIVE.equals(propertyName)) { reader.storeCachedClassName(breakpoint, null); } debugger.getRequestProcessor().post(new Runnable() { @@ -214,6 +215,7 @@ removeAllEventRequests (); } breakpoint.removePropertyChangeListener(this); + debugger.removePropertyChangeListener(JPDADebugger.PROP_BREAKPOINTS_ACTIVE, this); setValidity(Breakpoint.VALIDITY.UNKNOWN, null); if (breakpoint instanceof PropertyChangeListener) { Session s = debugger.getSession(); diff -r d86a5e4e1ac5 debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/BreakpointsDeactivationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/BreakpointsDeactivationTest.java Wed Feb 20 14:31:06 2013 +0100 @@ -0,0 +1,216 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.api.debugger.jpda; + +import junit.framework.Test; +import org.netbeans.api.debugger.DebuggerManager; +import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent; +import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener; +import org.netbeans.junit.NbTestCase; + +/** + * Tests breakpoints deactivation. + * + */ +public class BreakpointsDeactivationTest extends NbTestCase { + + private static final String TEST_APP_PATH = System.getProperty ("test.dir.src") + + "org/netbeans/api/debugger/jpda/testapps/LineBreakpointApp.java"; + + private JPDASupport support; + + + public BreakpointsDeactivationTest (String s) { + super (s); + } + + public static Test suite() { + return JPDASupport.createTestSuite(BreakpointsDeactivationTest.class); + } + + public void testBreakpointsDeactivation () throws Exception { + try { + Utils.BreakPositions bp = Utils.getBreakPositions(TEST_APP_PATH); + LineBreakpoint[] lb = bp.getBreakpoints().toArray(new LineBreakpoint[0]); + { + LineBreakpoint b; + b = lb[4]; + lb[4] = lb[2]; + lb[2] = b; + } + /* + LineBreakpoint lb1 = LineBreakpoint.create (TEST_APP, 32); + LineBreakpoint lb2 = LineBreakpoint.create (TEST_APP, 37); + LineBreakpoint lb3 = LineBreakpoint.create (TEST_APP, 109); + lb3.setPreferredClassName("org.netbeans.api.debugger.jpda.testapps.LineBreakpointApp$Inner"); + LineBreakpoint lb4 = LineBreakpoint.create (TEST_APP, 92); + lb4.setPreferredClassName("org.netbeans.api.debugger.jpda.testapps.LineBreakpointApp$InnerStatic"); + LineBreakpoint lb5 = LineBreakpoint.create (TEST_APP, 41); + */ + DebuggerManager dm = DebuggerManager.getDebuggerManager (); + for (int i = 0; i < lb.length; i++) { + dm.addBreakpoint (lb[i]); + } + + TestBreakpointListener[] tb = new TestBreakpointListener[lb.length]; + for (int i = 0; i < lb.length; i++) { + tb[i] = new TestBreakpointListener (lb[i]); + lb[i].addJPDABreakpointListener (tb[i]); + } + support = JPDASupport.attach ( + "org.netbeans.api.debugger.jpda.testapps.LineBreakpointApp" + ); + JPDADebugger debugger = support.getDebugger(); + assertEquals("Breakpoints should be active initially.", true, debugger.areBreakpointsActive()); + + support.waitState (JPDADebugger.STATE_STOPPED); // stopped on the first breakpoint + + debugger.setBreakpointsActive(false); + assertEquals("Breakpoints should be inactive after deactivation.", false, debugger.areBreakpointsActive()); + + int j = 0; + assertEquals ( + "Debugger stopped at wrong line for breakpoint " + j, + lb[j].getLineNumber (), + debugger.getCurrentCallStackFrame ().getLineNumber (null) + ); + for (int i = j+1; i < tb.length; i++) { + tb[i].checkNotNotified(); + } + if (j < lb.length - 1) { + support.doContinue(); + } + + for (int i = 0; i < tb.length; i++) { + dm.removeBreakpoint (lb[i]); + } + support.waitState (JPDADebugger.STATE_DISCONNECTED); + } finally { + if (support != null) support.doFinish (); + } + } + + // innerclasses ............................................................ + + private class TestBreakpointListener implements JPDABreakpointListener { + + private LineBreakpoint lineBreakpoint; + private int conditionResult; + + private JPDABreakpointEvent event; + private AssertionError failure; + + public TestBreakpointListener (LineBreakpoint lineBreakpoint) { + this (lineBreakpoint, JPDABreakpointEvent.CONDITION_NONE); + } + + public TestBreakpointListener ( + LineBreakpoint lineBreakpoint, + int conditionResult + ) { + this.lineBreakpoint = lineBreakpoint; + this.conditionResult = conditionResult; + } + + public void breakpointReached (JPDABreakpointEvent event) { + try { + checkEvent (event); + } catch (AssertionError e) { + failure = e; + } catch (Throwable e) { + failure = new AssertionError (e); + } + } + + private void checkEvent (JPDABreakpointEvent event) { + this.event = event; + assertEquals ( + "Breakpoint event: Wrong source breakpoint", + lineBreakpoint, + event.getSource () + ); + assertNotNull ( + "Breakpoint event: Context thread is null", + event.getThread () + ); + + int result = event.getConditionResult (); + if ( result == JPDABreakpointEvent.CONDITION_FAILED && + conditionResult != JPDABreakpointEvent.CONDITION_FAILED + ) + failure = new AssertionError (event.getConditionException ()); + else + if (result != conditionResult) + failure = new AssertionError ( + "Unexpected breakpoint condition result: " + result + ); + } + + public void checkResult () { + if (event == null) { + CallStackFrame f = support.getDebugger (). + getCurrentCallStackFrame (); + int ln = -1; + if (f != null) { + ln = f.getLineNumber (null); + } + throw new AssertionError ( + "Breakpoint was not hit (listener was not notified) " + ln + ); + } + if (failure != null) throw failure; + } + + public void checkNotNotified() { + if (event != null) { + JPDAThread t = event.getThread(); + throw new AssertionError ( + "Breakpoint was hit (listener was notified) in thread " + t + ); + } + if (failure != null) throw failure; + } + } +}