diff --git a/api.debugger.jpda/apichanges.xml b/api.debugger.jpda/apichanges.xml --- a/api.debugger.jpda/apichanges.xml +++ b/api.debugger.jpda/apichanges.xml @@ -786,6 +786,21 @@ + + + BreakpointsClassFilter provider introduced. + + + + + + BreakpointsClassFilter introduced to provide a customized set of classes + for breakpoints. + + + + + diff --git a/api.debugger.jpda/manifest.mf b/api.debugger.jpda/manifest.mf --- a/api.debugger.jpda/manifest.mf +++ b/api.debugger.jpda/manifest.mf @@ -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.36 +OpenIDE-Module-Specification-Version: 2.37 OpenIDE-Module-Package-Dependencies: com.sun.jdi[VirtualMachineManager] diff --git a/api.debugger.jpda/src/org/netbeans/modules/debugger/jpda/apiregistry/DebuggerProcessor.java b/api.debugger.jpda/src/org/netbeans/modules/debugger/jpda/apiregistry/DebuggerProcessor.java --- a/api.debugger.jpda/src/org/netbeans/modules/debugger/jpda/apiregistry/DebuggerProcessor.java +++ b/api.debugger.jpda/src/org/netbeans/modules/debugger/jpda/apiregistry/DebuggerProcessor.java @@ -64,6 +64,7 @@ import org.netbeans.api.debugger.jpda.JPDADebugger; import org.netbeans.spi.debugger.ContextProvider; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter; import org.netbeans.spi.debugger.jpda.EditorContext; import org.netbeans.spi.debugger.jpda.Evaluator; import org.netbeans.spi.debugger.jpda.SmartSteppingCallback; @@ -91,7 +92,8 @@ SourcePathProvider.Registration.class.getCanonicalName(), EditorContext.Registration.class.getCanonicalName(), VariablesFilter.Registration.class.getCanonicalName(), - Evaluator.Registration.class.getCanonicalName() + Evaluator.Registration.class.getCanonicalName(), + BreakpointsClassFilter.Registration.class.getCanonicalName() )); } @@ -147,6 +149,13 @@ handleEvaluatorRegistration(e, language); cnt++; } + for (Element e : env.getElementsAnnotatedWith(BreakpointsClassFilter.Registration.class)) { + BreakpointsClassFilter.Registration reg = e.getAnnotation(BreakpointsClassFilter.Registration.class); + + final String path = reg.path(); + handleProviderRegistration(e, BreakpointsClassFilter.class, path); + cnt++; + } return cnt == annotations.size(); } diff --git a/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/BreakpointsClassFilter.java b/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/BreakpointsClassFilter.java new file mode 100644 --- /dev/null +++ b/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/BreakpointsClassFilter.java @@ -0,0 +1,161 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 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]" + * + * 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. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.spi.debugger.jpda; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Map; +import org.netbeans.api.debugger.jpda.JPDABreakpoint; +import org.netbeans.modules.debugger.jpda.apiregistry.DebuggerProcessor; +import org.netbeans.spi.debugger.ContextAwareService; +import org.netbeans.spi.debugger.ContextAwareSupport; +import org.netbeans.spi.debugger.ContextProvider; + +/** + * Filter of breakpoint class names. + * Breakpoints have defined names of classes, which they are to be submitted for. + * But there can be a need to customize the class names for a specific use. + * For instance, add versioned classes for JRebel, etc. + * + * @author Martin Entlicher + * @since 2.37 + */ +public abstract class BreakpointsClassFilter { + + /** + * Provide a modified set of class names. + * + * @param classNames The original set of class names that the breakpoint acts on + * @param breakpoint The associated breakpoint + * @return A modified set of class names, for which the breakpoint is to be submitted. + */ + public abstract ClassNames filterClassNames(ClassNames classNames, JPDABreakpoint breakpoint); + + /** + * The set of class names and excluded class names. + */ + public static class ClassNames { + + private final String[] classNames; + private final String[] excludedClassNames; + + /** + * Create a new set of class names and excluded class names. + * The names can start or end with '*' character. + * @param classNames The (binary) class names + * @param excludedClassNames The excluded (binary) class names + */ + public ClassNames(String[] classNames, String[] excludedClassNames) { + this.classNames = classNames; + this.excludedClassNames = excludedClassNames; + } + + /** + * Get the list of class names + * @return The array of (binary) class names + */ + public String[] getClassNames() { + return classNames; + } + + /** + * Get the list of excluded class names + * @return The array of excluded (binary) class names + */ + public String[] getExcludedClassNames() { + return excludedClassNames; + } + + } + + /** + * Declarative registration of BreakpointsClassFilter implementation. + * By marking the implementation class with this annotation, + * you automatically register that implementation for use by debugger. + * The class must be public and have a public constructor which takes + * no arguments or takes {@link ContextProvider} as an argument. + */ + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE}) + public @interface Registration { + + /** + * An optional path to register this implementation in. + */ + String path() default ""; + + } + + static class ContextAware extends BreakpointsClassFilter implements ContextAwareService { + + private String serviceName; + + private ContextAware(String serviceName) { + this.serviceName = serviceName; + } + + @Override + public BreakpointsClassFilter forContext(ContextProvider context) { + return (BreakpointsClassFilter) ContextAwareSupport.createInstance(serviceName, context); + } + + @Override + public ClassNames filterClassNames(ClassNames classNames, JPDABreakpoint breakpoint) { + return classNames; + } + + /** + * Creates instance of ContextAwareService based on layer.xml + * attribute values + * + * @param attrs attributes loaded from layer.xml + * @return new ContextAwareService instance + */ + static ContextAwareService createService(Map attrs) throws ClassNotFoundException { + String serviceName = (String) attrs.get(DebuggerProcessor.SERVICE_NAME); + return new ContextAware(serviceName); + } + + } +} diff --git a/debugger.jpda/nbproject/project.xml b/debugger.jpda/nbproject/project.xml --- a/debugger.jpda/nbproject/project.xml +++ b/debugger.jpda/nbproject/project.xml @@ -64,6 +64,7 @@ 2 + 2.37 diff --git a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBasedBreakpoint.java b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBasedBreakpoint.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBasedBreakpoint.java +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBasedBreakpoint.java @@ -83,6 +83,7 @@ import org.netbeans.modules.debugger.jpda.jdi.request.ClassUnloadRequestWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper; import org.netbeans.modules.debugger.jpda.util.JPDAUtils; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter; import org.netbeans.spi.debugger.jpda.SourcePathProvider; import org.openide.ErrorManager; import org.openide.filesystems.FileUtil; @@ -101,6 +102,7 @@ private final Object SOURCE_ROOT_LOCK = new Object(); private SourceRootsChangedListener srChListener; private PropertyChangeListener weakSrChListener; + private BreakpointsClassFilter classFilter; private static final Logger logger = Logger.getLogger("org.netbeans.modules.debugger.jpda.breakpoints"); // NOI18N @@ -109,7 +111,7 @@ JPDADebuggerImpl debugger, Session session ) { - super (breakpoint, null, debugger, session); + this (breakpoint, null, debugger, session); } public ClassBasedBreakpoint ( @@ -119,6 +121,11 @@ Session session ) { super (breakpoint, reader, debugger, session); + classFilter = new CompoundClassFilter(session.lookup(null, BreakpointsClassFilter.class)); + } + + protected final BreakpointsClassFilter getClassFilter() { + return classFilter; } protected final void setSourceRoot(String sourceRoot) { @@ -227,7 +234,7 @@ return compareSourceRoots(sourceRoot, urlRoot); } - protected void setClassRequests ( + protected final void setClassRequests ( String[] classFilters, String[] classExclusionFilters, int breakpointType @@ -235,7 +242,7 @@ setClassRequests(classFilters, classExclusionFilters, breakpointType, true); } - protected void setClassRequests ( + protected final void setClassRequests ( String[] classFilters, String[] classExclusionFilters, int breakpointType, @@ -244,39 +251,37 @@ try { if ((breakpointType & ClassLoadUnloadBreakpoint.TYPE_CLASS_LOADED) != 0 ) { - ClassPrepareRequest cpr = EventRequestManagerWrapper. - createClassPrepareRequest (getEventRequestManager()); int i, k = classFilters.length; for (i = 0; i < k; i++) { + ClassPrepareRequest cpr = EventRequestManagerWrapper. + createClassPrepareRequest (getEventRequestManager()); ClassPrepareRequestWrapper.addClassFilter (cpr, classFilters [i]); if (logger.isLoggable(Level.FINE)) logger.fine("Set class load request: " + classFilters [i]); + for (String exclusionFilter : classExclusionFilters) { + ClassPrepareRequestWrapper.addClassExclusionFilter (cpr, exclusionFilter); + if (logger.isLoggable(Level.FINE)) + logger.fine("Set class load exclusion request: " + exclusionFilter); + } + addEventRequest (cpr, ignoreHitCountOnClassLoad); } - k = classExclusionFilters.length; - for (i = 0; i < k; i++) { - ClassPrepareRequestWrapper.addClassExclusionFilter (cpr, classExclusionFilters [i]); - if (logger.isLoggable(Level.FINE)) - logger.fine("Set class load exclusion request: " + classExclusionFilters [i]); - } - addEventRequest (cpr, ignoreHitCountOnClassLoad); } if ((breakpointType & ClassLoadUnloadBreakpoint.TYPE_CLASS_UNLOADED) != 0 ) { - ClassUnloadRequest cur = EventRequestManagerWrapper. - createClassUnloadRequest (getEventRequestManager()); int i, k = classFilters.length; for (i = 0; i < k; i++) { + ClassUnloadRequest cur = EventRequestManagerWrapper. + createClassUnloadRequest (getEventRequestManager()); ClassUnloadRequestWrapper.addClassFilter (cur, classFilters [i]); if (logger.isLoggable(Level.FINE)) logger.fine("Set class unload request: " + classFilters [i]); + for (String exclusionFilter : classExclusionFilters) { + ClassUnloadRequestWrapper.addClassExclusionFilter (cur, exclusionFilter); + if (logger.isLoggable(Level.FINE)) + logger.fine("Set class unload exclusion request: " + exclusionFilter); + } + addEventRequest (cur, false); } - k = classExclusionFilters.length; - for (i = 0; i < k; i++) { - ClassUnloadRequestWrapper.addClassExclusionFilter (cur, classExclusionFilters [i]); - if (logger.isLoggable(Level.FINE)) - logger.fine("Set class unload exclusion request: " + classExclusionFilters [i]); - } - addEventRequest (cur, false); } } catch (VMDisconnectedExceptionWrapper e) { } catch (InternalExceptionWrapper e) { @@ -404,5 +409,23 @@ } } + + private class CompoundClassFilter extends BreakpointsClassFilter { + + private List filters; + + public CompoundClassFilter(List filters) { + this.filters = filters; + } + + @Override + public ClassNames filterClassNames(ClassNames classNames, JPDABreakpoint breakpoint) { + for (BreakpointsClassFilter f : filters) { + classNames = f.filterClassNames(classNames, breakpoint); + } + return classNames; + } + + } } diff --git a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBreakpointImpl.java b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBreakpointImpl.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBreakpointImpl.java +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ClassBreakpointImpl.java @@ -64,6 +64,7 @@ import org.netbeans.modules.debugger.jpda.jdi.request.ClassPrepareRequestWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.ClassUnloadRequestWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter.ClassNames; /** * Implementation of breakpoint on method. @@ -87,9 +88,16 @@ @Override protected void setRequests () { + ClassNames classNames = getClassFilter().filterClassNames( + new ClassNames( + breakpoint.getClassFilters(), + breakpoint.getClassExclusionFilters()), + breakpoint); + String[] names = classNames.getClassNames(); + String[] excludedNames = classNames.getExcludedClassNames(); setClassRequests ( - breakpoint.getClassFilters (), - breakpoint.getClassExclusionFilters (), + names, + excludedNames, breakpoint.getBreakpointType (), false ); diff --git a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ExceptionBreakpointImpl.java b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ExceptionBreakpointImpl.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ExceptionBreakpointImpl.java +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/ExceptionBreakpointImpl.java @@ -65,6 +65,7 @@ import org.netbeans.modules.debugger.jpda.jdi.event.LocatableEventWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.ExceptionRequestWrapper; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter.ClassNames; import org.openide.util.Exceptions; /** @@ -86,12 +87,23 @@ @Override protected void setRequests () { + ClassNames classNames = getClassFilter().filterClassNames( + new ClassNames( + new String[] { + breakpoint.getExceptionClassName() + }, + new String [0]), + breakpoint); + String[] names = classNames.getClassNames(); + String[] excludedNames = classNames.getExcludedClassNames(); setClassRequests ( - new String[] {breakpoint.getExceptionClassName ()}, - new String[0], + names, + excludedNames, ClassLoadUnloadBreakpoint.TYPE_CLASS_LOADED ); - checkLoadedClasses (breakpoint.getExceptionClassName (), null); + for (String cn : names) { + checkLoadedClasses (cn, excludedNames); + } } @Override diff --git a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/FieldBreakpointImpl.java b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/FieldBreakpointImpl.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/FieldBreakpointImpl.java +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/FieldBreakpointImpl.java @@ -84,6 +84,7 @@ import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.WatchpointRequestWrapper; import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter.ClassNames; import org.openide.util.Exceptions; import org.openide.util.NbBundle; @@ -107,6 +108,16 @@ @Override protected void setRequests () { + ClassNames classNames = getClassFilter().filterClassNames( + new ClassNames( + new String[] { + breakpoint.getClassName() + }, + new String [0]), + breakpoint); + String[] names = classNames.getClassNames(); + String[] excludedNames = classNames.getExcludedClassNames(); + boolean access = (breakpoint.getBreakpointType () & FieldBreakpoint.TYPE_ACCESS) != 0; try { @@ -123,11 +134,13 @@ return ; } setClassRequests ( - new String[] {breakpoint.getClassName ()}, - new String[0], + names, + excludedNames, ClassLoadUnloadBreakpoint.TYPE_CLASS_LOADED ); - checkLoadedClasses (breakpoint.getClassName (), null); + for (String cn : names) { + checkLoadedClasses (cn, excludedNames); + } } catch (InternalExceptionWrapper e) { } catch (VMDisconnectedExceptionWrapper e) { } diff --git a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java @@ -110,6 +110,7 @@ import org.netbeans.modules.debugger.jpda.jdi.request.BreakpointRequestWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper; import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter.ClassNames; import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.ErrorManager; import org.openide.cookies.EditorCookie; @@ -267,14 +268,23 @@ return ; } logger.fine("LineBreakpoint "+breakpoint+" - setting request for "+className); + ClassNames classNames = getClassFilter().filterClassNames( + new ClassNames( + new String[] { + className // The class name is correct even for inner classes now + }, + new String [0]), + breakpoint); + String[] names = classNames.getClassNames(); + String[] excludedNames = classNames.getExcludedClassNames(); setClassRequests ( - new String[] { - className // The class name is correct even for inner classes now - }, - new String [0], + names, + excludedNames, ClassLoadUnloadBreakpoint.TYPE_CLASS_LOADED ); - checkLoadedClasses (className, null); + for (String cn : names) { + checkLoadedClasses (cn, excludedNames); + } } private void setInvalid(String reason) { diff --git a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/MethodBreakpointImpl.java b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/MethodBreakpointImpl.java --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/MethodBreakpointImpl.java +++ b/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/MethodBreakpointImpl.java @@ -94,6 +94,7 @@ import org.netbeans.modules.debugger.jpda.jdi.request.MethodEntryRequestWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.MethodExitRequestWrapper; import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter.ClassNames; import org.openide.util.Exceptions; import org.openide.util.NbBundle; @@ -115,13 +116,20 @@ @Override protected void setRequests () { + ClassNames classNames = getClassFilter().filterClassNames( + new ClassNames( + breakpoint.getClassFilters(), + breakpoint.getClassExclusionFilters()), + breakpoint); + String[] names = classNames.getClassNames(); + String[] excludedNames = classNames.getExcludedClassNames(); setClassRequests ( - breakpoint.getClassFilters (), - breakpoint.getClassExclusionFilters (), + names, + excludedNames, ClassLoadUnloadBreakpoint.TYPE_CLASS_LOADED ); - for(String filter : breakpoint.getClassFilters()) { - checkLoadedClasses (filter, breakpoint.getClassExclusionFilters()); + for(String filter : names) { + checkLoadedClasses (filter, excludedNames); } } diff --git a/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/BreakpointsClassFilterTest.java b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/BreakpointsClassFilterTest.java new file mode 100644 --- /dev/null +++ b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/BreakpointsClassFilterTest.java @@ -0,0 +1,237 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 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]" + * + * 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. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.api.debugger.jpda; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +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; +import org.netbeans.spi.debugger.jpda.BreakpointsClassFilter; + +/** + * Test of {@link BreakpointsClassFilter}. + * + * @author Martin Entlicher + */ +public class BreakpointsClassFilterTest extends NbTestCase { + + private static final String TEST_APP_PATH = System.getProperty ("test.dir.src") + + "org/netbeans/api/debugger/jpda/testapps/BreakpointsClassFilterApp.java"; + private static final String FILTER_GROUP_NAME = "filtered"; + private static final String APP_CLASS_NAME = "org.netbeans.api.debugger.jpda.testapps.BreakpointsClassFilterApp"; + private static final String APP2_CLASS_NAME = APP_CLASS_NAME + "2"; + + private JPDASupport support; + + /* + public static Test suite() { + return JPDASupport.createTestSuite(BreakpointsClassFilterTest.class); + } + */ + + public BreakpointsClassFilterTest(String s) { + super(s); + } + + /** + * Two breakpoints of each kind are submitted, + * the first is hit in BreakpointsClassFilterApp class only, + * but the second is hit also in BreakpointsClassFilterApp2 class. + * The latter has {@link #FILTER_GROUP_NAME} set as a group name to + * distinguish the filtered breakpoints. + * Hits in the two application classes are checked by TestBreakpointListener. + * + * @throws Exception + */ + public void testFilteredBreakpoints() throws Exception { + JPDASupport.removeAllBreakpoints (); + List breakpoints = new ArrayList(); + Utils.BreakPositions bp = Utils.getBreakPositions(TEST_APP_PATH); + List lineBreakpoints = bp.getLineBreakpoints(); + lineBreakpoints.get(1).setGroupName(FILTER_GROUP_NAME); + + MethodBreakpoint mb1 = MethodBreakpoint.create(APP_CLASS_NAME, "test"); + mb1.setBreakpointType(MethodBreakpoint.TYPE_METHOD_ENTRY); + + FieldBreakpoint fb1 = FieldBreakpoint.create(APP_CLASS_NAME, "field", FieldBreakpoint.TYPE_ACCESS); + FieldBreakpoint fb2 = FieldBreakpoint.create(APP_CLASS_NAME, "field2", FieldBreakpoint.TYPE_ACCESS); + fb2.setGroupName(FILTER_GROUP_NAME); + + ExceptionBreakpoint eb1 = ExceptionBreakpoint.create(NegativeArraySizeException.class.getName(), ExceptionBreakpoint.TYPE_EXCEPTION_CATCHED_UNCATCHED); + ExceptionBreakpoint eb2 = ExceptionBreakpoint.create(ArithmeticException.class.getName(), ExceptionBreakpoint.TYPE_EXCEPTION_CATCHED_UNCATCHED); + eb2.setGroupName(FILTER_GROUP_NAME); + + MethodBreakpoint mb2 = MethodBreakpoint.create(APP_CLASS_NAME, "test"); + mb2.setBreakpointType(MethodBreakpoint.TYPE_METHOD_EXIT); + mb2.setGroupName(FILTER_GROUP_NAME); + + breakpoints.add(lineBreakpoints.get(0)); + breakpoints.add(mb1); + breakpoints.add(fb1); + breakpoints.add(eb1); + breakpoints.add(lineBreakpoints.get(1)); + breakpoints.add(mb2); + breakpoints.add(fb2); + breakpoints.add(eb2); + + List listeners = new ArrayList(); + for (JPDABreakpoint b : breakpoints) { + TestBreakpointListener tbl = new TestBreakpointListener + (b, FILTER_GROUP_NAME.equals(b.getGroupName()), breakpoints); + b.addJPDABreakpointListener(tbl); + DebuggerManager.getDebuggerManager ().addBreakpoint (b); + listeners.add(tbl); + } + + support = JPDASupport.attach ( + APP_CLASS_NAME + ); + support.waitState(JPDADebugger.STATE_DISCONNECTED); + assertTrue(Arrays.toString(breakpoints.toArray()), breakpoints.isEmpty()); + for (TestBreakpointListener l : listeners) { + assertTrue(l.getMessage(), l.isOK()); + } + } + + private class TestBreakpointListener implements JPDABreakpointListener { + + private final JPDABreakpoint breakpoint; + private final boolean isFiltered; + private final List allBreakpoints; + private Boolean ok = null; + private String message = "Not hit yet."; + private final Set toBeHitIn; + + public TestBreakpointListener(JPDABreakpoint breakpoint, boolean isFiltered, + List allBreakpoints) { + this.breakpoint = breakpoint; + this.isFiltered = isFiltered; + this.allBreakpoints = allBreakpoints; + if (isFiltered) { + toBeHitIn = new HashSet(); + toBeHitIn.add(APP_CLASS_NAME); + toBeHitIn.add(APP2_CLASS_NAME); + } else { + toBeHitIn = null; + } + } + + @Override + public void breakpointReached(JPDABreakpointEvent event) { + //System.err.println("breakpointReached("+event+", isFiltered = "+isFiltered+"), referenceType = "+event.getReferenceType()); + if (!isFiltered) { + boolean removed = allBreakpoints.remove(breakpoint); + if (ok == null && removed) { + ok = Boolean.TRUE; + message = "O.K."; + } else { + if (ok != null) { + message += "Hit again at "+event.getReferenceType()+" "; + ok = Boolean.FALSE; + } else if (!removed) { + message += "BP not in list. "; + ok = Boolean.FALSE; + } + } + } else { // Filtered, expected to hit twice (in APP_CLASS_NAME and APP2_CLASS_NAME) + //System.err.println("breakpointReached("+event+", isFiltered = "+isFiltered+"), referenceType = "+event.getReferenceType()); + String className = event.getReferenceType().name();//event.getThread().getClassName(); + boolean removed = toBeHitIn.remove(className); + if (ok == null && removed && toBeHitIn.isEmpty()) { + allBreakpoints.remove(breakpoint); + ok = Boolean.TRUE; + message = "O.K."; + } else { + if (!removed) { + message += "Hit again at "+className+" "; + ok = Boolean.FALSE; + } + } + } + event.resume(); + } + + public boolean isOK() { + return Boolean.TRUE.equals(ok); + } + + public String getMessage() { + return message + " " + breakpoint+" with group "+breakpoint.getGroupName(); + } + + } + + @BreakpointsClassFilter.Registration(path="netbeans-JPDASession") + public static class TestBreakpointsClassFilter extends BreakpointsClassFilter { + + public TestBreakpointsClassFilter() { + //System.err.println("NEW TestBreakpointsClassFilter()"); + } + + @Override + public ClassNames filterClassNames(ClassNames classNames, JPDABreakpoint breakpoint) { + //System.err.println("filterClassNames("+Arrays.toString(classNames.getClassNames())); + String groupName = breakpoint.getGroupName(); + if (FILTER_GROUP_NAME.equals(groupName)) { + String[] classes = classNames.getClassNames(); + String[] newClasses = new String[classes.length + 1]; + System.arraycopy(classes, 0, newClasses, 0, classes.length); + if (breakpoint instanceof ExceptionBreakpoint) { + newClasses[classes.length] = NullPointerException.class.getName(); + } else { + newClasses[classes.length] = APP2_CLASS_NAME; + } + return new ClassNames(newClasses, classNames.getExcludedClassNames()); + } else { + return classNames; + } + } + + } + +} diff --git a/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/testapps/BreakpointsClassFilterApp.java b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/testapps/BreakpointsClassFilterApp.java new file mode 100644 --- /dev/null +++ b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/testapps/BreakpointsClassFilterApp.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 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]" + * + * 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. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.api.debugger.jpda.testapps; + +import java.util.Random; + +/** + * Test app for breakpoints class filters. + * Some breakpoints stop in this class only, some get filter class names, + * that add BreakpointsClassFilterApp2 as well. + * + * @author Martin Entlicher + */ +public class BreakpointsClassFilterApp { + + private double field = 111.111; + private double field2 = 111.111; + + /** + * @param args the command line arguments + */ + public static void main(final String[] args) { + Thread t = new Thread(BreakpointsClassFilterApp2.class.getName()) { + @Override public void run() { + BreakpointsClassFilterApp2.main(args); + } + }; t.start(); + new BreakpointsClassFilterApp().test(t); + } + + public double test(Thread t) { + double d = 0; // LBREAKPOINT + int n = new Random(Math.round(field)).nextInt(); + n = n - n; + try { + n += new Double[-10].length; + } catch (NegativeArraySizeException nasex) { + } + d = d + field2 + Math.atan(n) + Math.PI + Math.E; // LBREAKPOINT + try { + d += 1/n; + } catch (ArithmeticException aex) { + } + try { + t.join(); + } catch (InterruptedException ex) { + } + return d; + } +} diff --git a/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/testapps/BreakpointsClassFilterApp2.java b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/testapps/BreakpointsClassFilterApp2.java new file mode 100644 --- /dev/null +++ b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/testapps/BreakpointsClassFilterApp2.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 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]" + * + * 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. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.api.debugger.jpda.testapps; + +import java.util.Random; + +/** + * Test app for breakpoints class filters. + * Some breakpoints get this in filter class names, causing pauses in here + * even when submitted for BreakpointsClassFilterApp only. + * + * @author Martin Entlicher + */ +public class BreakpointsClassFilterApp2 { + + private double field = 111.111; + private double field2 = 111.111; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + // TODO code application logic here + // Keep the identical line numbers with BreakpointsClassFilterApp + + + + new BreakpointsClassFilterApp2().test(); + } + + public double test() { + double d = 0; + int n = new Random(Math.round(field)).nextInt(); + n = n - n; + try { + throw new IllegalStateException("TEST"); + } catch (IllegalStateException nasex) { + } + d = d + field2 + Math.atan(n) + Math.PI + Math.E; + try { + Double dbl = null; d += dbl; + } catch (NullPointerException npex) { + } + return d; + } +}