From 5c96b7a2e035d406029f340fb9f87cacb8978ff6 Mon Sep 17 00:00:00 2001 From: sjiang Date: Tue, 9 Sep 2008 14:17:29 +0200 Subject: 6736611: [Evt Srv] EventSubscriber.unsubscribe removes other listeners Reviewed-by: emcmanus --- .../javax/management/event/EventSubscriber.java | 82 +++++++------- .../management/eventService/SubUnsubTest.java | 125 +++++++++++++++++++++ 2 files changed, 164 insertions(+), 43 deletions(-) create mode 100644 test/javax/management/eventService/SubUnsubTest.java diff --git a/src/share/classes/javax/management/event/EventSubscriber.java b/src/share/classes/javax/management/event/EventSubscriber.java index 242634555..9948810bf 100644 --- a/src/share/classes/javax/management/event/EventSubscriber.java +++ b/src/share/classes/javax/management/event/EventSubscriber.java @@ -149,10 +149,10 @@ public class EventSubscriber implements EventConsumer { if (listener == null) throw new IllegalArgumentException("Null listener"); - final ListenerInfo li = new ListenerInfo(listener, filter, handback); - List list; + final MyListenerInfo li = new MyListenerInfo(listener, filter, handback); + List list; - Map> map; + Map> map; Set names; if (name.isPattern()) { map = patternSubscriptionMap; @@ -165,7 +165,7 @@ public class EventSubscriber implements EventConsumer { synchronized (map) { list = map.get(name); if (list == null) { - list = new ArrayList(); + list = new ArrayList(); map.put(name, list); } list.add(li); @@ -186,7 +186,6 @@ public class EventSubscriber implements EventConsumer { public void unsubscribe(ObjectName name, NotificationListener listener) throws ListenerNotFoundException, IOException { - if (logger.traceOn()) logger.trace("unsubscribe", "" + name); @@ -196,7 +195,7 @@ public class EventSubscriber implements EventConsumer { if (listener == null) throw new ListenerNotFoundException(); - Map> map; + Map> map; Set names; if (name.isPattern()) { @@ -207,22 +206,39 @@ public class EventSubscriber implements EventConsumer { names = Collections.singleton(name); } - final ListenerInfo li = new ListenerInfo(listener, null, null); - List list; + List toRemove = new ArrayList(); synchronized (map) { - list = map.get(name); - if (list == null || !list.remove(li)) + List list = map.get(name); + if (list == null) { + throw new ListenerNotFoundException(); + } + + for (MyListenerInfo info : list) { + if (info.listener == listener) { + toRemove.add(info); + } + } + + if (toRemove.isEmpty()) { throw new ListenerNotFoundException(); + } + + for (MyListenerInfo info : toRemove) { + list.remove(info); + } if (list.isEmpty()) map.remove(name); } for (ObjectName mbeanName : names) { - try { - mbeanServer.removeNotificationListener(mbeanName, li.listener); - } catch (Exception e) { - logger.fine("unsubscribe", "removeNotificationListener", e); + for (MyListenerInfo i : toRemove) { + try { + mbeanServer.removeNotificationListener(mbeanName, + i.listener, i.filter, i.handback); + } catch (Exception e) { + logger.fine("unsubscribe", "removeNotificationListener", e); + } } } } @@ -256,12 +272,12 @@ public class EventSubscriber implements EventConsumer { return; } - final List listeners = new ArrayList(); + final List listeners = new ArrayList(); // If there are subscribers for the exact name that has just arrived // then add their listeners to the list. synchronized (exactSubscriptionMap) { - List exactListeners = exactSubscriptionMap.get(name); + List exactListeners = exactSubscriptionMap.get(name); if (exactListeners != null) listeners.addAll(exactListeners); } @@ -277,7 +293,7 @@ public class EventSubscriber implements EventConsumer { } // Add all the listeners just found to the new MBean. - for (ListenerInfo li : listeners) { + for (MyListenerInfo li : listeners) { try { mbeanServer.addNotificationListener( name, @@ -292,12 +308,12 @@ public class EventSubscriber implements EventConsumer { } }; - private static class ListenerInfo { + private static class MyListenerInfo { public final NotificationListener listener; public final NotificationFilter filter; public final Object handback; - public ListenerInfo(NotificationListener listener, + public MyListenerInfo(NotificationListener listener, NotificationFilter filter, Object handback) { @@ -308,26 +324,6 @@ public class EventSubscriber implements EventConsumer { this.filter = filter; this.handback = handback; } - - /* Two ListenerInfo instances are equal if they have the same - * NotificationListener. This means that we can use List.remove - * to implement the two-argument removeNotificationListener. - */ - @Override - public boolean equals(Object o) { - if (o == this) - return true; - - if (!(o instanceof ListenerInfo)) - return false; - - return listener.equals(((ListenerInfo)o).listener); - } - - @Override - public int hashCode() { - return listener.hashCode(); - } } // --------------------------------- @@ -338,10 +334,10 @@ public class EventSubscriber implements EventConsumer { // --------------------------------- private final MBeanServer mbeanServer; - private final Map> exactSubscriptionMap = - new HashMap>(); - private final Map> patternSubscriptionMap = - new HashMap>(); + private final Map> exactSubscriptionMap = + new HashMap>(); + private final Map> patternSubscriptionMap = + new HashMap>(); diff --git a/test/javax/management/eventService/SubUnsubTest.java b/test/javax/management/eventService/SubUnsubTest.java new file mode 100644 index 000000000..f88898823 --- /dev/null +++ b/test/javax/management/eventService/SubUnsubTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test SubUnsubTest + * @bug 6736611 + * @summary Test not to remove other listeners when calling unsubscribe + * @author Shanliang JIANG + * @run clean SubUnsubTest + * @run build SubUnsubTest + * @run main SubUnsubTest + */ + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventSubscriber; +import javax.management.event.EventClient; +public class SubUnsubTest { + private static class CountListener implements NotificationListener { + volatile int count; + + public void handleNotification(Notification n, Object h) { + count++; + } + } + + public static interface SenderMBean {} + + public static class Sender extends NotificationBroadcasterSupport + implements SenderMBean { + void send() { + Notification n = new Notification("type", this, 1L); + sendNotification(n); + } + } + + public static void main(String[] args) throws Exception { + System.out.println("Testing EventSubscriber-unsubscribe method."); + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + ObjectName name1 = new ObjectName("d:type=Sender,id=1"); + ObjectName name2 = new ObjectName("d:type=Sender,id=2"); + ObjectName pattern = new ObjectName("d:type=Sender,*"); + Sender sender1 = new Sender(); + Sender sender2 = new Sender(); + mbs.registerMBean(sender1, name1); + mbs.registerMBean(sender2, name2); + + EventSubscriber sub = EventSubscriber.getEventSubscriber(mbs); + + System.out.println("Single subscribe covering both MBeans"); + CountListener listener = new CountListener(); + + System.out.println("Subscribing and adding listeners ..."); + sub.subscribe(pattern, listener, null, null); + sub.subscribe(name2, listener, null, null); + mbs.addNotificationListener(name2, listener, null, null); + + sender1.send(); + sender2.send(); + if (listener.count != 4) { + throw new RuntimeException("Do not receive all notifications: "+ + "Expect 4, got "+listener.count); + } + + System.out.println("Unsubscribe the listener with the pattern."); + sub.unsubscribe(pattern, listener); + listener.count = 0; + sender1.send(); + sender2.send(); + if (listener.count != 2) { + throw new RuntimeException("The method unsubscribe removes wrong listeners."); + } + + System.out.println("Unsubscribe the listener with the ObjectName."); + sub.unsubscribe(name2, listener); + listener.count = 0; + sender1.send(); + sender2.send(); + if (listener.count != 1) { + throw new RuntimeException("The method unsubscribe removes wrong listeners."); + } + + System.out.println("Subscribe twice to same MBean with same listener " + + "but different handback."); + sub.subscribe(name1, listener, null, new Object()); + sub.subscribe(name1, listener, null, new Object()); + listener.count = 0; + + sub.unsubscribe(name1, listener); + sender1.send(); + if (listener.count > 0) { + throw new RuntimeException("EventSubscriber: the method unsubscribe" + + " does not remove a listener which was subscribed 2 times."); + } + + System.out.println("Bye bye!"); + return; + } +} \ No newline at end of file -- cgit v1.2.3 From 5edd06340e0673aee925c1197b08c826d95dbbb8 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Tue, 9 Sep 2008 14:57:30 +0200 Subject: 6746196: Some JMX classes do not compile with Eclipse compiler Reviewed-by: dfuchs Contributed-by: roman.kennke@aicas.com --- .../com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java | 10 +++++----- .../classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java b/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java index 3509c40f3..b86b2e001 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java @@ -1172,10 +1172,10 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { final Class propertyNamesClass = ConstructorProperties.class; Class targetClass = getTargetClass(); - Constructor[] constrs = targetClass.getConstructors(); + Constructor[] constrs = targetClass.getConstructors(); // Applicable if and only if there are any annotated constructors - List annotatedConstrList = newList(); + List> annotatedConstrList = newList(); for (Constructor constr : constrs) { if (Modifier.isPublic(constr.getModifiers()) && constr.getAnnotation(propertyNamesClass) != null) @@ -1206,7 +1206,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { // Also remember the set of properties in that constructor // so we can test unambiguity. Set getterIndexSets = newSet(); - for (Constructor constr : annotatedConstrList) { + for (Constructor constr : annotatedConstrList) { String[] propertyNames = constr.getAnnotation(propertyNamesClass).value(); @@ -1363,10 +1363,10 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { } private static class Constr { - final Constructor constructor; + final Constructor constructor; final int[] paramIndexes; final BitSet presentParams; - Constr(Constructor constructor, int[] paramIndexes, + Constr(Constructor constructor, int[] paramIndexes, BitSet presentParams) { this.constructor = constructor; this.paramIndexes = paramIndexes; diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java b/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java index 725292bca..99aaa8546 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java @@ -623,7 +623,7 @@ abstract class MBeanIntrospector { } private static MBeanConstructorInfo[] findConstructors(Class c) { - Constructor[] cons = c.getConstructors(); + Constructor[] cons = c.getConstructors(); MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; for (int i = 0; i < cons.length; i++) { String descr = "Public constructor of the MBean"; -- cgit v1.2.3 From d7f282fb1c13646ae9b78f0d1677d496e959527a Mon Sep 17 00:00:00 2001 From: dfuchs Date: Tue, 9 Sep 2008 17:01:45 +0200 Subject: 6745832: jmx namespaces: Some refactoring/commenting would improve code readability. Reviewed-by: emcmanus --- .../interceptor/DefaultMBeanServerInterceptor.java | 4 +- .../sun/jmx/interceptor/DispatchInterceptor.java | 94 +++++---- .../jmx/interceptor/DomainDispatchInterceptor.java | 82 +++++--- .../interceptor/NamespaceDispatchInterceptor.java | 40 ++-- .../classes/com/sun/jmx/mbeanserver/Util.java | 15 +- .../com/sun/jmx/namespace/DomainInterceptor.java | 83 ++++---- .../com/sun/jmx/namespace/HandlerInterceptor.java | 64 +++--- .../com/sun/jmx/namespace/JMXNamespaceUtils.java | 21 +- .../sun/jmx/namespace/NamespaceInterceptor.java | 4 - .../sun/jmx/namespace/RoutingConnectionProxy.java | 55 +++--- .../namespace/RoutingMBeanServerConnection.java | 218 ++++++++++++--------- .../com/sun/jmx/namespace/RoutingProxy.java | 148 ++++++++++++-- .../com/sun/jmx/namespace/RoutingServerProxy.java | 52 ++--- .../javax/management/namespace/JMXDomain.java | 12 +- .../javax/management/namespace/JMXNamespace.java | 38 ++-- .../javax/management/namespace/JMXNamespaces.java | 6 +- .../management/namespace/JMXRemoteNamespace.java | 49 ++--- .../namespace/MBeanServerConnectionWrapper.java | 1 - test/javax/management/namespace/Wombat.java | 7 +- 19 files changed, 579 insertions(+), 414 deletions(-) diff --git a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index 7d95a77fd..7faeb8727 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -2021,7 +2021,7 @@ public class DefaultMBeanServerInterceptor private void addJMXNamespace(JMXNamespace namespace, final ObjectName logicalName, final Queue postQueue) { - dispatcher.addNamespace(logicalName, namespace, postQueue); + dispatcher.addInterceptorFor(logicalName, namespace, postQueue); } /** @@ -2035,7 +2035,7 @@ public class DefaultMBeanServerInterceptor private void removeJMXNamespace(JMXNamespace namespace, final ObjectName logicalName, final Queue postQueue) { - dispatcher.removeNamespace(logicalName, namespace, postQueue); + dispatcher.removeInterceptorFor(logicalName, namespace, postQueue); } /** diff --git a/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java index 9e8625d16..4a79567fe 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java @@ -194,7 +194,7 @@ public abstract class DispatchInterceptor // found in the handlerMap. Note: there doesn't need to be an interceptor // for that key in the Map. // - public abstract String getHandlerKey(ObjectName name); + abstract String getHandlerKey(ObjectName name); // Returns an interceptor for that name, or null if there's no interceptor // for that name. @@ -277,7 +277,7 @@ public abstract class DispatchInterceptor // of JMXNamespace (or a subclass of it) is registered as an MBean. // This method is usually invoked from within the repository lock, // hence the necessity of the postRegisterQueue. - public void addNamespace(ObjectName name, N jmxNamespace, + public void addInterceptorFor(ObjectName name, N jmxNamespace, Queue postRegisterQueue) { final String key = getHandlerKey(name); validateHandlerNameFor(key,name); @@ -298,7 +298,7 @@ public abstract class DispatchInterceptor // of JMXNamespace (or a subclass of it) is deregistered. // This method is usually invoked from within the repository lock, // hence the necessity of the postDeregisterQueue. - public void removeNamespace(ObjectName name, N jmxNamespace, + public void removeInterceptorFor(ObjectName name, N jmxNamespace, Queue postDeregisterQueue) { final String key = getHandlerKey(name); final T ns; @@ -330,7 +330,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name) + public final ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException { @@ -338,7 +338,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name, + public final ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, @@ -347,7 +347,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name, + public final ObjectInstance createMBean(String className, ObjectName name, Object params[], String signature[]) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, @@ -357,7 +357,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name, + public final ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object params[], String signature[]) throws ReflectionException, InstanceAlreadyExistsException, @@ -368,42 +368,43 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance registerMBean(Object object, ObjectName name) + public final ObjectInstance registerMBean(Object object, ObjectName name) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { return getInterceptorForCreate(name).registerMBean(object,name); } // From MBeanServer - public void unregisterMBean(ObjectName name) + public final void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { getInterceptorForInstance(name).unregisterMBean(name); } // From MBeanServer - public ObjectInstance getObjectInstance(ObjectName name) + public final ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException { return getInterceptorForInstance(name).getObjectInstance(name); } // From MBeanServer - public Set queryMBeans(ObjectName name, QueryExp query) { - final QueryInterceptor mbs = + public final Set queryMBeans(ObjectName name, + QueryExp query) { + final QueryInterceptor queryInvoker = getInterceptorForQuery(name); - if (mbs == null) return Collections.emptySet(); - else return mbs.queryMBeans(name,query); + if (queryInvoker == null) return Collections.emptySet(); + else return queryInvoker.queryMBeans(name,query); } // From MBeanServer - public Set queryNames(ObjectName name, QueryExp query) { - final QueryInterceptor mbs = + public final Set queryNames(ObjectName name, QueryExp query) { + final QueryInterceptor queryInvoker = getInterceptorForQuery(name); - if (mbs == null) return Collections.emptySet(); - else return mbs.queryNames(name,query); + if (queryInvoker == null) return Collections.emptySet(); + else return queryInvoker.queryNames(name,query); } // From MBeanServer - public boolean isRegistered(ObjectName name) { + public final boolean isRegistered(ObjectName name) { final MBeanServer mbs = getInterceptorOrNullFor(name); if (mbs == null) return false; else return mbs.isRegistered(name); @@ -415,20 +416,21 @@ public abstract class DispatchInterceptor } // From MBeanServer - public Object getAttribute(ObjectName name, String attribute) + public final Object getAttribute(ObjectName name, String attribute) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { return getInterceptorForInstance(name).getAttribute(name,attribute); } // From MBeanServer - public AttributeList getAttributes(ObjectName name, String[] attributes) + public final AttributeList getAttributes(ObjectName name, + String[] attributes) throws InstanceNotFoundException, ReflectionException { return getInterceptorForInstance(name).getAttributes(name,attributes); } // From MBeanServer - public void setAttribute(ObjectName name, Attribute attribute) + public final void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { @@ -436,14 +438,14 @@ public abstract class DispatchInterceptor } // From MBeanServer - public AttributeList setAttributes(ObjectName name, + public final AttributeList setAttributes(ObjectName name, AttributeList attributes) throws InstanceNotFoundException, ReflectionException { return getInterceptorForInstance(name).setAttributes(name,attributes); } // From MBeanServer - public Object invoke(ObjectName name, String operationName, + public final Object invoke(ObjectName name, String operationName, Object params[], String signature[]) throws InstanceNotFoundException, MBeanException, ReflectionException { @@ -463,63 +465,69 @@ public abstract class DispatchInterceptor public abstract String[] getDomains(); // From MBeanServer - public void addNotificationListener(ObjectName name, + public final void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException { - getInterceptorForInstance(name).addNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + addNotificationListener(name,listener,filter, handback); } // From MBeanServer - public void addNotificationListener(ObjectName name, + public final void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException { - getInterceptorForInstance(name).addNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + addNotificationListener(name,listener,filter, handback); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, ObjectName listener) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener); + getInterceptorForInstance(name). + removeNotificationListener(name,listener); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + removeNotificationListener(name,listener,filter, handback); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, NotificationListener listener) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener); + getInterceptorForInstance(name). + removeNotificationListener(name,listener); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + removeNotificationListener(name,listener,filter, handback); } // From MBeanServer - public MBeanInfo getMBeanInfo(ObjectName name) + public final MBeanInfo getMBeanInfo(ObjectName name) throws InstanceNotFoundException, IntrospectionException, ReflectionException { return getInterceptorForInstance(name).getMBeanInfo(name); @@ -527,21 +535,23 @@ public abstract class DispatchInterceptor // From MBeanServer - public boolean isInstanceOf(ObjectName name, String className) + public final boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { return getInterceptorForInstance(name).isInstanceOf(name,className); } // From MBeanServer - public ClassLoader getClassLoaderFor(ObjectName mbeanName) + public final ClassLoader getClassLoaderFor(ObjectName mbeanName) throws InstanceNotFoundException { - return getInterceptorForInstance(mbeanName).getClassLoaderFor(mbeanName); + return getInterceptorForInstance(mbeanName). + getClassLoaderFor(mbeanName); } // From MBeanServer - public ClassLoader getClassLoader(ObjectName loaderName) + public final ClassLoader getClassLoader(ObjectName loaderName) throws InstanceNotFoundException { - return getInterceptorForInstance(loaderName).getClassLoader(loaderName); + return getInterceptorForInstance(loaderName). + getClassLoader(loaderName); } } diff --git a/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java index cb1489ee5..9b9b1d6b1 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java @@ -75,7 +75,7 @@ class DomainDispatchInterceptor private final DomainDispatchInterceptor parent; AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) { - super(dispatcher.localNamespace); + super(dispatcher.nextInterceptor); parent = dispatcher; } @@ -91,9 +91,8 @@ class DomainDispatchInterceptor // Add all matching MBeans from local namespace. final Set res = Util.cloneSet(local); - final boolean all = (pattern == null || - pattern.getDomain().equals("*")); if (pattern == null) pattern = ObjectName.WILDCARD; + final boolean all = pattern.getDomain().equals("*"); final String domain = pattern.getDomain(); @@ -142,7 +141,7 @@ class DomainDispatchInterceptor } } - private final DefaultMBeanServerInterceptor localNamespace; + private final DefaultMBeanServerInterceptor nextInterceptor; private final String mbeanServerName; private final MBeanServerDelegate delegate; @@ -165,7 +164,7 @@ class DomainDispatchInterceptor MBeanInstantiator instantiator, Repository repository, NamespaceDispatchInterceptor namespaces) { - localNamespace = new DefaultMBeanServerInterceptor(outer, + nextInterceptor = new DefaultMBeanServerInterceptor(outer, delegate, instantiator,repository,namespaces); mbeanServerName = Util.getMBeanServerSecurityName(delegate); this.delegate = delegate; @@ -182,7 +181,7 @@ class DomainDispatchInterceptor @Override void validateHandlerNameFor(String key, ObjectName name) { super.validateHandlerNameFor(key,name); - final String[] domains = localNamespace.getDomains(); + final String[] domains = nextInterceptor.getDomains(); for (int i=0;i postRegisterQueue) { if (handler instanceof JMXDomain) - localNamespace.addNamespace(name, + nextInterceptor.addInterceptorFor(name, (JMXDomain)handler,postRegisterQueue); - else super.addNamespace(name,handler,postRegisterQueue); + else super.addInterceptorFor(name,handler,postRegisterQueue); } @Override - public void removeNamespace(ObjectName name, JMXNamespace handler, + public void removeInterceptorFor(ObjectName name, JMXNamespace handler, Queue postDeregisterQueue) { if (handler instanceof JMXDomain) - localNamespace.removeNamespace(name,(JMXDomain)handler, + nextInterceptor.removeInterceptorFor(name,(JMXDomain)handler, postDeregisterQueue); - else super.removeNamespace(name,handler,postDeregisterQueue); + else super.removeInterceptorFor(name,handler,postDeregisterQueue); } diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/src/share/classes/com/sun/jmx/mbeanserver/Util.java index fcbdd9041..93c5c97f6 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -57,7 +57,8 @@ import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; public class Util { private final static int NAMESPACE_SEPARATOR_LENGTH = NAMESPACE_SEPARATOR.length(); - public final static String ILLEGAL_MBEANSERVER_NAME_CHARS=";:*?"; + public final static char[] ILLEGAL_MBEANSERVER_NAME_CHARS=";:*?". + toCharArray(); static Map newMap() { @@ -621,7 +622,7 @@ public class Util { * is {@code null}. * @throws IllegalArgumentException if mbeanServerName contains illegal * characters, or is empty, or is {@code "-"}. - * Illegal characters are {@value #ILLEGAL_MBEANSERVER_NAME_CHARS}. + * Illegal characters are {@link #ILLEGAL_MBEANSERVER_NAME_CHARS}. */ public static String checkServerName(String mbeanServerName) { if ("".equals(mbeanServerName)) @@ -632,7 +633,7 @@ public class Util { "\"-\" is not a valid MBean server name"); if (isMBeanServerNameUndefined(mbeanServerName)) return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME; - for (char c : ILLEGAL_MBEANSERVER_NAME_CHARS.toCharArray()) { + for (char c : ILLEGAL_MBEANSERVER_NAME_CHARS) { if (mbeanServerName.indexOf(c) >= 0) throw new IllegalArgumentException( "invalid character in MBeanServer name: "+c); @@ -662,15 +663,15 @@ public class Util { } // Log the exception and its causes without logging the stack trace. - // Use with care - it is usally preferable to log the whole stack trace! + // Use with care - it is usually preferable to log the whole stack trace! // We don't want to log the whole stack trace here: logshort() is // called in those cases where the exception might not be abnormal. private static void logshort(String msg, Throwable t) { if (JmxProperties.MISC_LOGGER.isLoggable(Level.FINE)) { StringBuilder toprint = new StringBuilder(msg); - toprint.append("\nCaused By: ").append(String.valueOf(t)); - while ((t=t.getCause())!=null) - toprint.append("\nCaused By: ").append(String.valueOf(t)); + do { + toprint.append("\nCaused By: ").append(String.valueOf(t)); + } while ((t=t.getCause())!=null); JmxProperties.MISC_LOGGER.fine(toprint.toString()); } } diff --git a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java index d7619814d..84644d831 100644 --- a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java @@ -85,7 +85,7 @@ public class DomainInterceptor extends HandlerInterceptor { final ObjectName pattern; public PatternNotificationFilter(ObjectName pattern) { - this.pattern = pattern; + this.pattern = pattern==null?ObjectName.WILDCARD:pattern; } public boolean isNotificationEnabled(Notification notification) { @@ -93,7 +93,7 @@ public class DomainInterceptor extends HandlerInterceptor { return false; final MBeanServerNotification mbsn = (MBeanServerNotification) notification; - if (pattern == null || pattern.apply(mbsn.getMBeanName())) + if (pattern.apply(mbsn.getMBeanName())) return true; return false; } @@ -110,6 +110,7 @@ public class DomainInterceptor extends HandlerInterceptor { super(handler); this.domainName = domainName; this.serverName = serverName; + ALL = Util.newObjectName(domainName+":*"); } @Override @@ -118,27 +119,27 @@ public class DomainInterceptor extends HandlerInterceptor { ", domain="+this.domainName+")"; } - public void connectDelegate(final MBeanServerDelegate delegate) + final void connectDelegate(final MBeanServerDelegate delegate) throws InstanceNotFoundException { final NotificationFilter filter = new PatternNotificationFilter(getPatternFor(null)); synchronized (this) { - if (mbsListener == null) + if (mbsListener == null) { mbsListener = new NotificationListener() { - - public void handleNotification(Notification notification, - Object handback) { - if (filter.isNotificationEnabled(notification)) - delegate.sendNotification(notification); - } - }; + public void handleNotification(Notification notification, + Object handback) { + if (filter.isNotificationEnabled(notification)) + delegate.sendNotification(notification); + } + }; + } } - getNamespace(). + getHandlerInterceptorMBean(). addMBeanServerNotificationListener(mbsListener, filter); } - public void disconnectDelegate() + final void disconnectDelegate() throws InstanceNotFoundException, ListenerNotFoundException { final NotificationListener l; synchronized (this) { @@ -146,10 +147,10 @@ public class DomainInterceptor extends HandlerInterceptor { if (l == null) return; mbsListener = null; } - getNamespace().removeMBeanServerNotificationListener(l); + getHandlerInterceptorMBean().removeMBeanServerNotificationListener(l); } - public void addPostRegisterTask(Queue queue, + public final void addPostRegisterTask(Queue queue, final MBeanServerDelegate delegate) { if (queue == null) throw new IllegalArgumentException("task queue must not be null"); @@ -158,14 +159,15 @@ public class DomainInterceptor extends HandlerInterceptor { try { connectDelegate(delegate); } catch (Exception x) { - throw new UnsupportedOperationException("notification forwarding",x); + throw new UnsupportedOperationException( + "notification forwarding",x); } } }; queue.add(task1); } - public void addPostDeregisterTask(Queue queue, + public final void addPostDeregisterTask(Queue queue, final MBeanServerDelegate delegate) { if (queue == null) throw new IllegalArgumentException("task queue must not be null"); @@ -174,17 +176,18 @@ public class DomainInterceptor extends HandlerInterceptor { try { disconnectDelegate(); } catch (Exception x) { - throw new UnsupportedOperationException("notification forwarding",x); + throw new UnsupportedOperationException( + "notification forwarding",x); } } }; queue.add(task1); } - /** - * Throws IllegalArgumentException if targetName.getDomain() is not - * in the domain handled. - **/ + // No name conversion for JMXDomains... + // Throws IllegalArgumentException if targetName.getDomain() is not + // in the domain handled. + // @Override protected ObjectName toSource(ObjectName targetName) { if (targetName == null) return null; @@ -198,6 +201,7 @@ public class DomainInterceptor extends HandlerInterceptor { return targetName; } + // No name conversion for JMXDomains... @Override protected ObjectName toTarget(ObjectName sourceName) { return sourceName; @@ -255,16 +259,16 @@ public class DomainInterceptor extends HandlerInterceptor { if (LOG.isLoggable(Level.FINE)) LOG.fine("Unexpected exception raised in queryNames: "+x); LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x); + return Collections.emptySet(); } - // We reach here only when an exception was raised. - // - final Set empty = Collections.emptySet(); - return empty; } + // Compute a new pattern which is a sub pattern of 'name' but only selects + // the MBeans in domain 'domainName' + // When we reach here, it has been verified that 'name' matches our domain + // name (done by DomainDispatchInterceptor) private ObjectName getPatternFor(final ObjectName name) { try { - if (ALL == null) ALL = ObjectName.getInstance(domainName + ":*"); if (name == null) return ALL; if (name.getDomain().equals(domainName)) return name; return name.withDomain(domainName); @@ -284,11 +288,8 @@ public class DomainInterceptor extends HandlerInterceptor { if (LOG.isLoggable(Level.FINE)) LOG.fine("Unexpected exception raised in queryNames: "+x); LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x); + return Collections.emptySet(); } - // We reach here only when an exception was raised. - // - final Set empty = Collections.emptySet(); - return empty; } @Override @@ -306,7 +307,7 @@ public class DomainInterceptor extends HandlerInterceptor { // in the domain. @Override public Integer getMBeanCount() { - return getNamespace().getMBeanCount(); + return getHandlerInterceptorMBean().getMBeanCount(); } private boolean checkOn() { @@ -320,8 +321,8 @@ public class DomainInterceptor extends HandlerInterceptor { @Override void check(ObjectName routingName, String member, String action) { if (!checkOn()) return; - final String act = (action==null)?"-":action.intern(); - if(act == "queryMBeans" || act == "queryNames") { // ES: OK + final String act = (action==null)?"-":action; + if("queryMBeans".equals(act) || "queryNames".equals(act)) { // This is tricky. check with 3 parameters is called // by queryNames/queryMBeans before performing the query. // At this point we must check with no class name. @@ -355,16 +356,8 @@ public class DomainInterceptor extends HandlerInterceptor { if (!checkOn()) return; final MBeanPermission perm; - // action is most probably already an intern string. - // string literals are intern strings. - // we create a new intern string for 'action' - just to be on - // the safe side... - // We intern it in order to be able to use == rather than equals - // below, because if we don't, and if action is not one of the - // 4 literals below, we would have to do a full string comparison. - // - final String act = (action==null)?"-":action.intern(); - if (act == "getDomains") { // ES: OK + final String act = (action==null)?"-":action; + if ("getDomains".equals(act)) { // ES: OK perm = new MBeanPermission(serverName,"-",member, routingName,act); } else { @@ -381,7 +374,7 @@ public class DomainInterceptor extends HandlerInterceptor { String getClassName(ObjectName routingName) { if (routingName == null || routingName.isPattern()) return "-"; try { - return getNamespace().getSourceServer(). + return getHandlerInterceptorMBean().getSourceServer(). getObjectInstance(routingName).getClassName(); } catch (InstanceNotFoundException ex) { LOG.finest("Can't get class name for "+routingName+ diff --git a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java index eb48ef849..7c2f39348 100644 --- a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java @@ -63,8 +63,8 @@ import javax.management.namespace.JMXNamespace; /** * This interceptor wraps a JMXNamespace, and performs * {@code ObjectName} rewriting. {@code HandlerInterceptor} are - * usually created and managed by a {@link NamespaceDispatcher} or - * {@link DomainDispatcher}. + * created and managed by a {@link NamespaceDispatchInterceptor} or a + * {@link DomainDispatchInterceptor}. *

* This API is a Sun internal API and is subject to changes without notice. *

@@ -90,6 +90,12 @@ public abstract class HandlerInterceptor this.handler = handler; } + // + // The {@code source} connection is a connection to the MBeanServer + // that contains the actual MBeans. + // In the case of cascading, that would be a connection to the sub + // agent. Practically, this is JMXNamespace.getSourceServer(); + // @Override protected MBeanServer source() { return handler.getSourceServer(); @@ -105,7 +111,9 @@ public abstract class HandlerInterceptor return source(); } - T getNamespace() { + // The namespace or domain handler - this either a JMXNamespace or a + // a JMXDomain + T getHandlerInterceptorMBean() { return handler; } @@ -122,7 +130,7 @@ public abstract class HandlerInterceptor Util.newRuntimeIOException(x)); } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException { @@ -172,7 +180,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, ObjectName listener) throws InstanceNotFoundException, ListenerNotFoundException { @@ -183,7 +191,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public String getDefaultDomain() { try { @@ -193,7 +201,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public String[] getDomains() { try { @@ -203,7 +211,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Integer getMBeanCount() { try { @@ -213,7 +221,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, AttributeNotFoundException, @@ -226,7 +234,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Set queryNames(ObjectName name, QueryExp query) { try { @@ -236,7 +244,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Set queryMBeans(ObjectName name, QueryExp query) { try { @@ -246,7 +254,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { @@ -257,7 +265,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, @@ -270,7 +278,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) @@ -284,7 +292,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Object getAttribute(ObjectName name, String attribute) throws MBeanException, AttributeNotFoundException, @@ -296,7 +304,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) @@ -309,7 +317,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, @@ -323,7 +331,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, NotificationListener listener) @@ -336,7 +344,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, @@ -349,7 +357,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) @@ -362,7 +370,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public boolean isRegistered(ObjectName name) { try { @@ -372,7 +380,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { @@ -383,7 +391,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public MBeanInfo getMBeanInfo(ObjectName name) throws InstanceNotFoundException, IntrospectionException, @@ -395,7 +403,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException { @@ -406,7 +414,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name, Object[] params, String[] signature) @@ -421,7 +429,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object[] params, String[] signature) @@ -437,7 +445,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public AttributeList setAttributes(ObjectName name,AttributeList attributes) throws InstanceNotFoundException, ReflectionException { @@ -448,7 +456,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) diff --git a/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java b/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java index 1fccfcbc7..49f875144 100644 --- a/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java +++ b/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java @@ -29,7 +29,6 @@ import com.sun.jmx.defaults.JmxProperties; import java.io.IOException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import java.util.logging.Level; @@ -40,6 +39,8 @@ import javax.management.MBeanServerConnection; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.event.EventClient; +import javax.management.event.EventClientDelegateMBean; +import javax.management.namespace.JMXNamespace; import javax.management.namespace.JMXNamespaces; import javax.management.remote.JMXAddressable; import javax.management.remote.JMXConnector; @@ -66,26 +67,10 @@ public final class JMXNamespaceUtils { return new WeakHashMap(); } - /** Creates a new instance of JMXNamespaces */ + /** There are no instances of this class */ private JMXNamespaceUtils() { } - /** - * Returns an unmodifiable option map in which the given keys have been - * filtered out. - * @param keys keys to filter out from the map. - * @return An unmodifiable option map in which the given keys have been - * filtered out. - */ - public static Map filterMap(Map map, K... keys) { - final Map filtered; - filtered=new HashMap(map); - for (K key : keys) { - filtered.remove(key); - } - return unmodifiableMap(filtered); - } - // returns un unmodifiable view of a map. public static Map unmodifiableMap(Map aMap) { if (aMap == null || aMap.isEmpty()) diff --git a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java index da85fc6d4..11afeb2af 100644 --- a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java @@ -54,10 +54,6 @@ import javax.management.namespace.JMXNamespacePermission; */ public class NamespaceInterceptor extends HandlerInterceptor { - /** - * A logger for this class. - **/ - private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; private static final Logger PROBE_LOG = Logger.getLogger( JmxProperties.NAMESPACE_LOGGER+".probe"); diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java index 787343eec..443c80f2a 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java @@ -45,6 +45,9 @@ import javax.management.namespace.JMXNamespaces; *

* @since 1.7 */ +// See class hierarchy and detailled explanations in RoutingProxy in this +// package. +// public class RoutingConnectionProxy extends RoutingProxy { @@ -93,40 +96,28 @@ public class RoutingConnectionProxy targetNs+"\", "+forwardsContext+")"; } + static final RoutingProxyFactory + + FACTORY = new RoutingProxyFactory + () { + + public RoutingConnectionProxy newInstance(MBeanServerConnection source, + String sourcePath, String targetPath, + boolean forwardsContext) { + return new RoutingConnectionProxy(source,sourcePath, + targetPath,forwardsContext); + } + + public RoutingConnectionProxy newInstance( + MBeanServerConnection source, String sourcePath) { + return new RoutingConnectionProxy(source,sourcePath); + } + }; + public static MBeanServerConnection cd(MBeanServerConnection source, String sourcePath) { - if (source == null) throw new IllegalArgumentException("null"); - if (source.getClass().equals(RoutingConnectionProxy.class)) { - // cast is OK here, but findbugs complains unless we use class.cast - final RoutingConnectionProxy other = - RoutingConnectionProxy.class.cast(source); - final String target = other.getTargetNamespace(); - - // Avoid multiple layers of serialization. - // - // We construct a new proxy from the original source instead of - // stacking a new proxy on top of the old one. - // - that is we replace - // cd ( cd ( x, dir1), dir2); - // by - // cd (x, dir1//dir2); - // - // We can do this only when the source class is exactly - // NamespaceConnectionProxy. - // - if (target == null || target.equals("")) { - final String path = - JMXNamespaces.concat(other.getSourceNamespace(), - sourcePath); - return new RoutingConnectionProxy(other.source(),path,"", - other.forwardsContext); - } - // Note: we could do possibly something here - but it would involve - // removing part of targetDir, and possibly adding - // something to sourcePath. - // Too complex to bother! => simply default to stacking... - } - return new RoutingConnectionProxy(source,sourcePath); + return RoutingProxy.cd(RoutingConnectionProxy.class, FACTORY, + source, sourcePath); } } diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java index 904e5848e..70df9b504 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java @@ -83,18 +83,32 @@ public abstract class RoutingMBeanServerConnection//}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - String[] checkAttributes(ObjectName routingName, - String[] attributes, String action) { - check(routingName,null,action); - return attributes; - } - - /** - * This method is a hook to implement permission checking in subclasses. - * By default, this method does nothing and simply returns - * {@code attribute}. - * - * @param routingName The name of the MBean in the enclosing context. - * This is of the form {@code //}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - AttributeList checkAttributes(ObjectName routingName, - AttributeList attributes, String action) { - check(routingName,null,action); - return attributes; - } - // from MBeanServerConnection public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException, IOException { @@ -195,37 +171,6 @@ public abstract class RoutingMBeanServerConnection//}. - * @param member The {@link - * javax.management.namespace.JMXNamespacePermission#getMember member} - * name. - * @param action The {@link - * javax.management.namespace.JMXNamespacePermission#getActions action} - * name. - */ - void check(ObjectName routingName, - String member, String action) { - } - - void checkPattern(ObjectName routingPattern, - String member, String action) { - // pattern is checked only at posteriori by checkQuery. - // checking it a priori usually doesn't work, because ObjectName.apply - // does not work between two patterns. - check(null,null,action); - } - - void checkCreate(ObjectName routingName, String className, - String action) { - } - // from MBeanServerConnection public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) @@ -446,24 +391,6 @@ public abstract class RoutingMBeanServerConnection//}. - * @param action one of "queryNames" or "queryMBeans" - * @return true if {@code sourceName} can be returned. - */ - boolean checkQuery(ObjectName routingName, String action) { - return true; - } // Return names in the target's context. ObjectInstance processOutputInstance(ObjectInstance source) { @@ -643,6 +570,111 @@ public abstract class RoutingMBeanServerConnection//}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + */ + String[] checkAttributes(ObjectName routingName, + String[] attributes, String action) { + check(routingName,null,action); + return attributes; + } + + /** + * This method is a hook to implement permission checking in subclasses. + * By default, this method does nothing and simply returns + * {@code attribute}. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + */ + AttributeList checkAttributes(ObjectName routingName, + AttributeList attributes, String action) { + check(routingName,null,action); + return attributes; + } + + /** + * This method is a hook to implement permission checking in subclasses. + * By default, this method does nothing. + * A subclass may override this method and throw a {@link + * SecurityException} if the permission is denied. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param member The {@link + * javax.management.namespace.JMXNamespacePermission#getMember member} + * name. + * @param action The {@link + * javax.management.namespace.JMXNamespacePermission#getActions action} + * name. + */ + void check(ObjectName routingName, + String member, String action) { + } + + // called in createMBean and registerMBean + void checkCreate(ObjectName routingName, String className, + String action) { + } + + // A priori check for queryNames/queryMBeans/ + void checkPattern(ObjectName routingPattern, + String member, String action) { + // pattern is checked only at posteriori by checkQuery. + // checking it a priori usually doesn't work, because ObjectName.apply + // does not work between two patterns. + // We only check that we have the permission requested for 'action'. + check(null,null,action); + } + + + /** + * This is a hook to implement permission checking in subclasses. + * + * Checks that the caller has sufficient permission for returning + * information about {@code sourceName} in {@code action}. + * + * By default always return true. Subclass may override this method + * and return false if the caller doesn't have sufficient permissions. + * + * @param routingName The name of the MBean to include or exclude from + * the query, expressed in the enclosing context. + * This is of the form {@code //}. + * @param action one of "queryNames" or "queryMBeans" + * @return true if {@code sourceName} can be returned. + */ + boolean checkQuery(ObjectName routingName, String action) { + return true; + } + /** * This method is a hook to implement permission checking in subclasses. * Checks that the caller as the necessary permissions to view the @@ -658,14 +690,4 @@ public abstract class RoutingMBeanServerConnection{@link RoutingConnectionProxy}: to cd in an MBeanServerConnection.

- *

{@link RoutingServerProxy}: to cd in an MBeanServer.

+ *

{@link RoutingConnectionProxy}: to narrow down into an + * MBeanServerConnection.

+ *

{@link RoutingServerProxy}: to narrow down into an MBeanServer.

*

* This API is a Sun internal API and is subject to changes without notice. *

* @since 1.7 */ +// +// RoutingProxies are client side objects which are used to narrow down +// into a namespace. They are used to perform ObjectName translation, +// adding the namespace to the routing ObjectName before sending it over +// to the source connection, and removing that prefix from results of +// queries, createMBean, registerMBean, and getObjectInstance. +// This translation is the opposite to that which is performed by +// NamespaceInterceptors. +// +// There is however a special case where routing proxies are used on the +// 'server' side to remove a namespace - rather than to add it: +// This the case of ClientContext. +// When an ObjectName like "jmx.context//c1=v1,c2=v2//D:k=v" reaches the +// jmx.context namespace, a routing proxy is used to remove the prefix +// c1=v1,c2=v2// from the routing objectname. +// +// For a RoutingProxy used in a narrowDownToNamespace operation, we have: +// targetNs="" // targetNS is the namespace 'to remove' +// sourceNS= // namespace 'to add' +// +// For a RoutingProxy used in a ClientContext operation, we have: +// targetNs= // context must be removed from object name +// sourceNs="" // nothing to add... +// +// RoutingProxies can also be used on the client side to implement +// "withClientContext" operations. In that case, the boolean parameter +// 'forwards context' is set to true, targetNs is "", and sourceNS may +// also be "". When forwardsContext is true, the RoutingProxy dynamically +// creates an ObjectNameRouter for each operation - in order to dynamically add +// the context attached to the thread to the routing ObjectName. This is +// performed in the getObjectNameRouter() method. +// +// Finally, in order to avoid too many layers of wrapping, +// RoutingConnectionProxy and RoutingServerProxy can be created through a +// factory method that can concatenate namespace pathes in order to +// return a single RoutingProxy - rather than wrapping a RoutingProxy inside +// another RoutingProxy. See RoutingConnectionProxy.cd and +// RoutingServerProxy.cd +// +// The class hierarchy is as follows: +// +// RoutingMBeanServerConnection +// [abstract class for all routing interceptors, +// such as RoutingProxies and HandlerInterceptors] +// / \ +// / \ +// RoutingProxy HandlerInterceptor +// [base class for [base class for server side +// client-side objects used objects, created by +// in narrowDownTo] DispatchInterceptors] +// / \ | \ +// RoutingConnectionProxy \ | NamespaceInterceptor +// [wraps MBeanServerConnection \ | [used to remove +// objects] \ | namespace prefix and +// RoutingServerProxy | wrap JMXNamespace] +// [wraps MBeanServer | +// Objects] | +// DomainInterceptor +// [used to wrap JMXDomain] +// +// RoutingProxies also differ from HandlerInterceptors in that they transform +// calls to MBeanServerConnection operations that do not have any parameters +// into a call to the underlying JMXNamespace MBean. +// So for instance a call to: +// JMXNamespaces.narrowDownToNamespace(conn,"foo").getDomains() +// is transformed into +// conn.getAttribute("foo//type=JMXNamespace","Domains"); +// public abstract class RoutingProxy extends RoutingMBeanServerConnection { @@ -179,17 +245,11 @@ public abstract class RoutingProxy throw x; } catch (MBeanException ex) { throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(), + ex, ex.getTargetException()); - } catch (AttributeNotFoundException ex) { + } catch (Exception ex) { throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(),ex); - } catch (InstanceNotFoundException ex) { - throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(),ex); - } catch (ReflectionException ex) { - throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(),ex); + ex,ex); } } @@ -279,4 +339,62 @@ public abstract class RoutingProxy (" mounted on targetNs="+targetNs)); } + // Creates an instance of a subclass 'R' of RoutingProxy + // RoutingServerProxy and RoutingConnectionProxy have their own factory + // instance. + static interface RoutingProxyFactory> { + R newInstance(T source, + String sourcePath, String targetPath, + boolean forwardsContext); + R newInstance(T source, + String sourcePath); + } + + // Performs a narrowDownToNamespace operation. + // This method will attempt to merge two RoutingProxies in a single + // one if they are of the same class. + // + // This method is never called directly - it should be called only by + // subclasses of RoutingProxy. + // + // As for now it is called by: + // RoutingServerProxy.cd and RoutingConnectionProxy.cd. + // + static > + R cd(Class routingProxyClass, + RoutingProxyFactory factory, + T source, String sourcePath) { + if (source == null) throw new IllegalArgumentException("null"); + if (source.getClass().equals(routingProxyClass)) { + // cast is OK here, but findbugs complains unless we use class.cast + final R other = routingProxyClass.cast(source); + final String target = other.getTargetNamespace(); + + // Avoid multiple layers of serialization. + // + // We construct a new proxy from the original source instead of + // stacking a new proxy on top of the old one. + // - that is we replace + // cd ( cd ( x, dir1), dir2); + // by + // cd (x, dir1//dir2); + // + // We can do this only when the source class is exactly + // RoutingServerProxy. + // + if (target == null || target.equals("")) { + final String path = + JMXNamespaces.concat(other.getSourceNamespace(), + sourcePath); + return factory.newInstance(other.source(),path,"", + other.forwardsContext); + } + // Note: we could do possibly something here - but it would involve + // removing part of targetDir, and possibly adding + // something to sourcePath. + // Too complex to bother! => simply default to stacking... + } + return factory.newInstance(source,sourcePath); + } } diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java index 94aa139dd..f58e39816 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java @@ -69,6 +69,9 @@ import javax.management.namespace.JMXNamespaces; * * @since 1.7 */ +// See class hierarchy and detailled explanations in RoutingProxy in this +// package. +// public class RoutingServerProxy extends RoutingProxy implements MBeanServer { @@ -564,39 +567,24 @@ public class RoutingServerProxy } } + static final RoutingProxyFactory + FACTORY = new RoutingProxyFactory() { + + public RoutingServerProxy newInstance(MBeanServer source, + String sourcePath, String targetPath, + boolean forwardsContext) { + return new RoutingServerProxy(source,sourcePath, + targetPath,forwardsContext); + } + + public RoutingServerProxy newInstance( + MBeanServer source, String sourcePath) { + return new RoutingServerProxy(source,sourcePath); + } + }; public static MBeanServer cd(MBeanServer source, String sourcePath) { - if (source == null) throw new IllegalArgumentException("null"); - if (source.getClass().equals(RoutingServerProxy.class)) { - // cast is OK here, but findbugs complains unless we use class.cast - final RoutingServerProxy other = - RoutingServerProxy.class.cast(source); - final String target = other.getTargetNamespace(); - - // Avoid multiple layers of serialization. - // - // We construct a new proxy from the original source instead of - // stacking a new proxy on top of the old one. - // - that is we replace - // cd ( cd ( x, dir1), dir2); - // by - // cd (x, dir1//dir2); - // - // We can do this only when the source class is exactly - // NamespaceServerProxy. - // - if (target == null || target.equals("")) { - final String path = - JMXNamespaces.concat(other.getSourceNamespace(), - sourcePath); - return new RoutingServerProxy(other.source(),path,"", - other.forwardsContext); - } - // Note: we could do possibly something here - but it would involve - // removing part of targetDir, and possibly adding - // something to sourcePath. - // Too complex to bother! => simply default to stacking... - } - return new RoutingServerProxy(source,sourcePath); + return RoutingProxy.cd(RoutingServerProxy.class, FACTORY, + source, sourcePath); } } diff --git a/src/share/classes/javax/management/namespace/JMXDomain.java b/src/share/classes/javax/management/namespace/JMXDomain.java index a54fde7f3..bff3c1370 100644 --- a/src/share/classes/javax/management/namespace/JMXDomain.java +++ b/src/share/classes/javax/management/namespace/JMXDomain.java @@ -308,17 +308,17 @@ public class JMXDomain extends JMXNamespace { * It is however only available for subclasses in this package. **/ @Override - ObjectName validateHandlerName(ObjectName supliedName) { - if (supliedName == null) + ObjectName validateHandlerName(ObjectName suppliedName) { + if (suppliedName == null) throw new IllegalArgumentException("Must supply a valid name"); final String dirName = JMXNamespaces. - normalizeNamespaceName(supliedName.getDomain()); + normalizeNamespaceName(suppliedName.getDomain()); final ObjectName handlerName = getDomainObjectName(dirName); - if (!supliedName.equals(handlerName)) + if (!suppliedName.equals(handlerName)) throw new IllegalArgumentException("invalid name space name: "+ - supliedName); + suppliedName); - return supliedName; + return suppliedName; } /** diff --git a/src/share/classes/javax/management/namespace/JMXNamespace.java b/src/share/classes/javax/management/namespace/JMXNamespace.java index 39cb11b02..23f3004eb 100644 --- a/src/share/classes/javax/management/namespace/JMXNamespace.java +++ b/src/share/classes/javax/management/namespace/JMXNamespace.java @@ -482,8 +482,8 @@ public class JMXNamespace /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} - * interface in order to get a handle to the MBean server in which it is - * registered. It also check the validity of its own ObjectName. + * interface in order to get a reference to the MBean server in which it is + * registered. It also checks the validity of its own ObjectName. *

* This method is called by the MBean server. * Application classes should never call this method directly. @@ -502,11 +502,14 @@ public class JMXNamespace */ public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { - if (objectName != null && ! objectName.equals(name)) - throw new IllegalStateException( + // need to synchronize to protect against multiple registration. + synchronized(this) { + if (objectName != null && ! objectName.equals(name)) + throw new IllegalStateException( "Already registered under another name: " + objectName); - objectName = validateHandlerName(name); - mbeanServer = server; + objectName = validateHandlerName(name); + mbeanServer = server; + } return name; } @@ -517,23 +520,23 @@ public class JMXNamespace * reuse JMXNamespace in order to implement sessions... * It is however only available for subclasses in this package. **/ - ObjectName validateHandlerName(ObjectName supliedName) { - if (supliedName == null) + ObjectName validateHandlerName(ObjectName suppliedName) { + if (suppliedName == null) throw new IllegalArgumentException("Must supply a valid name"); final String dirName = JMXNamespaces. - normalizeNamespaceName(supliedName.getDomain()); + normalizeNamespaceName(suppliedName.getDomain()); final ObjectName handlerName = JMXNamespaces.getNamespaceObjectName(dirName); - if (!supliedName.equals(handlerName)) + if (!suppliedName.equals(handlerName)) throw new IllegalArgumentException("invalid name space name: "+ - supliedName); - return supliedName; + suppliedName); + return suppliedName; } /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} - * interface in order to get a handle to the MBean server in which it is + * interface in order to get a reference to the MBean server in which it is * registered. *

* This method is called by the MBean server. Application classes should @@ -549,7 +552,7 @@ public class JMXNamespace /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} - * interface in order to get a handle to the MBean server in which it is + * interface in order to get a reference to the MBean server in which it is * registered. *

* This method is called by the MBean server. Application classes should @@ -573,8 +576,11 @@ public class JMXNamespace * @see MBeanRegistration#postDeregister MBeanRegistration */ public void postDeregister() { - mbeanServer = null; - objectName = null; + // need to synchronize to protect against multiple registration. + synchronized(this) { + mbeanServer = null; + objectName = null; + } } diff --git a/src/share/classes/javax/management/namespace/JMXNamespaces.java b/src/share/classes/javax/management/namespace/JMXNamespaces.java index b3eafd28a..429a9d466 100644 --- a/src/share/classes/javax/management/namespace/JMXNamespaces.java +++ b/src/share/classes/javax/management/namespace/JMXNamespaces.java @@ -266,11 +266,15 @@ public class JMXNamespaces { ObjectNameRouter.normalizeNamespacePath(namespace,false, true,false); try { + // We could use Util.newObjectName here - but throwing an + // IllegalArgumentException that contains just the supplied + // namespace instead of the whole ObjectName seems preferable. return ObjectName.getInstance(sourcePath+ NAMESPACE_SEPARATOR+":"+ JMXNamespace.TYPE_ASSIGNMENT); } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(namespace,x); + throw new IllegalArgumentException("Invalid namespace: " + + namespace,x); } } diff --git a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java index 1639fd2fc..1e877e0ce 100644 --- a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java +++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.security.AccessControlException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; @@ -125,10 +126,8 @@ public class JMXRemoteNamespace // the underlying JMXConnector. It is used in particular to maintain the // "connected" state in this MBean. // - private static class ConnectionListener implements NotificationListener { - private final JMXRemoteNamespace handler; - private ConnectionListener(JMXRemoteNamespace handler) { - this.handler = handler; + private class ConnectionListener implements NotificationListener { + private ConnectionListener() { } public void handleNotification(Notification notification, Object handback) { @@ -136,7 +135,11 @@ public class JMXRemoteNamespace return; final JMXConnectionNotification cn = (JMXConnectionNotification)notification; - handler.checkState(this,cn,(JMXConnector)handback); + final String type = cn.getType(); + if (JMXConnectionNotification.CLOSED.equals(type) + || JMXConnectionNotification.FAILED.equals(type)) { + checkState(this,cn,(JMXConnector)handback); + } } } @@ -188,7 +191,7 @@ public class JMXRemoteNamespace "Connected", "Emitted when the Connected state of this object changes"); - private static long seqNumber=0; + private static AtomicLong seqNumber = new AtomicLong(0); private final NotificationBroadcasterSupport broadcaster; private final ConnectionListener listener; @@ -237,7 +240,7 @@ public class JMXRemoteNamespace this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap); // handles (dis)connection events - this.listener = new ConnectionListener(this); + this.listener = new ConnectionListener(); // XXX TODO: remove the probe, or simplify it. this.probed = false; @@ -313,8 +316,8 @@ public class JMXRemoteNamespace broadcaster.removeNotificationListener(listener, filter, handback); } - private static synchronized long getNextSeqNumber() { - return seqNumber++; + private static long getNextSeqNumber() { + return seqNumber.getAndIncrement(); } @@ -362,14 +365,18 @@ public class JMXRemoteNamespace // lock while evaluating the true value of the connected state, // while anyone might also call close() or connect() from a // different thread. - // // The method switchConnection() (called from here too) also has the - // same kind of complex logic. + // same kind of complex logic: // // We use the JMXConnector has a handback to the notification listener // (emittingConnector) in order to be able to determine whether the // notification concerns the current connector in use, or an older - // one. + // one. The 'emittingConnector' is the connector from which the + // notification originated. This could be an 'old' connector - as + // closed() and connect() could already have been called before the + // notification arrived. So what we do is to compare the + // 'emittingConnector' with the current connector, to see if the + // notification actually comes from the curent connector. // boolean remove = false; @@ -486,14 +493,12 @@ public class JMXRemoteNamespace } } - private void closeall(JMXConnector... a) { - for (JMXConnector c : a) { - try { - if (c != null) c.close(); - } catch (Exception x) { - // OK: we're gonna throw the original exception later. - LOG.finest("Ignoring exception when closing connector: "+x); - } + private void close(JMXConnector c) { + try { + if (c != null) c.close(); + } catch (Exception x) { + // OK: we're gonna throw the original exception later. + LOG.finest("Ignoring exception when closing connector: "+x); } } @@ -640,10 +645,10 @@ public class JMXRemoteNamespace msc = aconn.getMBeanServerConnection(); aconn.addConnectionNotificationListener(listener,null,aconn); } catch (IOException io) { - closeall(aconn); + close(aconn); throw io; } catch (RuntimeException x) { - closeall(aconn); + close(aconn); throw x; } diff --git a/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java b/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java index f74785ffd..7eeda961e 100644 --- a/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java +++ b/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java @@ -28,7 +28,6 @@ package javax.management.namespace; import com.sun.jmx.mbeanserver.Util; import java.io.IOException; import java.io.ObjectInputStream; -import java.security.AccessController; import java.util.Set; import javax.management.Attribute; diff --git a/test/javax/management/namespace/Wombat.java b/test/javax/management/namespace/Wombat.java index 03dbd020e..bef648c0f 100644 --- a/test/javax/management/namespace/Wombat.java +++ b/test/javax/management/namespace/Wombat.java @@ -68,7 +68,12 @@ public class Wombat extends StandardMBean } public Wombat() throws NotCompliantMBeanException { - super(WombatMBean.class); + this(WombatMBean.class); + } + + public Wombat(Class clazz) + throws NotCompliantMBeanException { + super(clazz); final Random r = new Random(); seed = ((r.nextLong() % MAX_SEED) + MAX_SEED)%MAX_SEED; period = 200 + (((r.nextLong()%80)+80)%80)*10; -- cgit v1.2.3 From 2e6e2a657c63a7da4735136b407b1d291b2f1057 Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 9 Sep 2008 15:20:07 -0700 Subject: 6728229: (str) StringBuilder.append(CharSequence) does not throw IndexOutOfBoundsException Summary: Major spec rework Reviewed-by: alanb --- .../classes/java/lang/AbstractStringBuilder.java | 444 +++++++++++---------- src/share/classes/java/lang/StringBuffer.java | 57 +-- src/share/classes/java/lang/StringBuilder.java | 56 +-- 3 files changed, 244 insertions(+), 313 deletions(-) diff --git a/src/share/classes/java/lang/AbstractStringBuilder.java b/src/share/classes/java/lang/AbstractStringBuilder.java index 4ba130b49..3ee4c21d0 100644 --- a/src/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/share/classes/java/lang/AbstractStringBuilder.java @@ -42,7 +42,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ - char value[]; + char[] value; /** * The count is the number of characters used. @@ -333,8 +333,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * dst.length * */ - public void getChars(int srcBegin, int srcEnd, char dst[], - int dstBegin) + public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { if (srcBegin < 0) throw new StringIndexOutOfBoundsException(srcBegin); @@ -366,14 +365,14 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the Object - * argument. + * Appends the string representation of the {@code Object} argument. *

- * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(Object)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param obj an Object. + * @param obj an {@code Object}. * @return a reference to this object. */ public AbstractStringBuilder append(Object obj) { @@ -383,17 +382,17 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Appends the specified string to this character sequence. *

- * The characters of the String argument are appended, in + * The characters of the {@code String} argument are appended, in * order, increasing the length of this sequence by the length of the - * argument. If str is null, then the four - * characters "null" are appended. + * argument. If {@code str} is {@code null}, then the four + * characters {@code "null"} are appended. *

* Let n be the length of this character sequence just prior to - * execution of the append method. Then the character at + * execution of the {@code append} method. Then the character at * index k in the new character sequence is equal to the character * at index k in the old character sequence, if k is less * than n; otherwise, it is equal to the character at index - * k-n in the argument str. + * k-n in the argument {@code str}. * * @param str a string. * @return a reference to this object. @@ -435,33 +434,33 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends a subsequence of the specified CharSequence to this + * Appends a subsequence of the specified {@code CharSequence} to this * sequence. *

- * Characters of the argument s, starting at - * index start, are appended, in order, to the contents of - * this sequence up to the (exclusive) index end. The length - * of this sequence is increased by the value of end - start. + * Characters of the argument {@code s}, starting at + * index {@code start}, are appended, in order, to the contents of + * this sequence up to the (exclusive) index {@code end}. The length + * of this sequence is increased by the value of {@code end - start}. *

* Let n be the length of this character sequence just prior to - * execution of the append method. Then the character at + * execution of the {@code append} method. Then the character at * index k in this character sequence becomes equal to the * character at index k in this sequence, if k is less than * n; otherwise, it is equal to the character at index - * k+start-n in the argument s. + * k+start-n in the argument {@code s}. *

- * If s is null, then this method appends + * If {@code s} is {@code null}, then this method appends * characters as if the s parameter was a sequence containing the four - * characters "null". + * characters {@code "null"}. * * @param s the sequence to append. * @param start the starting index of the subsequence to be appended. * @param end the end index of the subsequence to be appended. * @return a reference to this object. * @throws IndexOutOfBoundsException if - * start or end are negative, or - * start is greater than end or - * end is greater than s.length() + * {@code start} is negative, or + * {@code start} is greater than {@code end} or + * {@code end} is greater than {@code s.length()} */ public AbstractStringBuilder append(CharSequence s, int start, int end) { if (s == null) @@ -483,22 +482,22 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the char array + * Appends the string representation of the {@code char} array * argument to this sequence. *

* The characters of the array argument are appended, in order, to * the contents of this sequence. The length of this sequence * increases by the length of the argument. *

- * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char[])} and the - * characters of that string were then {@link #append(String) appended} - * to this character sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(char[])}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * * @param str the characters to be appended. * @return a reference to this object. */ - public AbstractStringBuilder append(char str[]) { + public AbstractStringBuilder append(char[] str) { int newCount = count + str.length; if (newCount > value.length) expandCapacity(newCount); @@ -509,22 +508,25 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Appends the string representation of a subarray of the - * char array argument to this sequence. + * {@code char} array argument to this sequence. *

- * Characters of the char array str, starting at - * index offset, are appended, in order, to the contents + * Characters of the {@code char} array {@code str}, starting at + * index {@code offset}, are appended, in order, to the contents * of this sequence. The length of this sequence increases - * by the value of len. + * by the value of {@code len}. *

- * The overall effect is exactly as if the arguments were converted to - * a string by the method {@link String#valueOf(char[],int,int)} and the - * characters of that string were then {@link #append(String) appended} - * to this character sequence. + * The overall effect is exactly as if the arguments were converted + * to a string by the method {@link String#valueOf(char[],int,int)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * * @param str the characters to be appended. - * @param offset the index of the first char to append. - * @param len the number of chars to append. + * @param offset the index of the first {@code char} to append. + * @param len the number of {@code char}s to append. * @return a reference to this object. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code len < 0} + * or {@code offset+len > str.length} */ public AbstractStringBuilder append(char str[], int offset, int len) { int newCount = count + len; @@ -536,14 +538,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the boolean + * Appends the string representation of the {@code boolean} * argument to the sequence. *

- * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(boolean)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param b a boolean. + * @param b a {@code boolean}. * @return a reference to this object. */ public AbstractStringBuilder append(boolean b) { @@ -569,18 +572,18 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the char + * Appends the string representation of the {@code char} * argument to this sequence. *

* The argument is appended to the contents of this sequence. - * The length of this sequence increases by 1. + * The length of this sequence increases by {@code 1}. *

- * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char)} and the character - * in that string were then {@link #append(String) appended} to this - * character sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(char)}, + * and the character in that string were then + * {@link #append(String) appended} to this character sequence. * - * @param c a char. + * @param c a {@code char}. * @return a reference to this object. */ public AbstractStringBuilder append(char c) { @@ -592,14 +595,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the int + * Appends the string representation of the {@code int} * argument to this sequence. *

- * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(int)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param i an int. + * @param i an {@code int}. * @return a reference to this object. */ public AbstractStringBuilder append(int i) { @@ -618,14 +622,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the long + * Appends the string representation of the {@code long} * argument to this sequence. *

- * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(long)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param l a long. + * @param l a {@code long}. * @return a reference to this object. */ public AbstractStringBuilder append(long l) { @@ -644,14 +649,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the float + * Appends the string representation of the {@code float} * argument to this sequence. *

- * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this string sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(float)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param f a float. + * @param f a {@code float}. * @return a reference to this object. */ public AbstractStringBuilder append(float f) { @@ -660,14 +666,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the double + * Appends the string representation of the {@code double} * argument to this sequence. *

- * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(double)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param d a double. + * @param d a {@code double}. * @return a reference to this object. */ public AbstractStringBuilder append(double d) { @@ -677,17 +684,17 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Removes the characters in a substring of this sequence. - * The substring begins at the specified start and extends to - * the character at index end - 1 or to the end of the + * The substring begins at the specified {@code start} and extends to + * the character at index {@code end - 1} or to the end of the * sequence if no such character exists. If - * start is equal to end, no changes are made. + * {@code start} is equal to {@code end}, no changes are made. * * @param start The beginning index, inclusive. * @param end The ending index, exclusive. * @return This object. - * @throws StringIndexOutOfBoundsException if start - * is negative, greater than length(), or - * greater than end. + * @throws StringIndexOutOfBoundsException if {@code start} + * is negative, greater than {@code length()}, or + * greater than {@code end}. */ public AbstractStringBuilder delete(int start, int end) { if (start < 0) @@ -705,7 +712,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the codePoint + * Appends the string representation of the {@code codePoint} * argument to this sequence. * *

The argument is appended to the contents of this sequence. @@ -713,15 +720,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * {@link Character#charCount(int) Character.charCount(codePoint)}. * *

The overall effect is exactly as if the argument were - * converted to a char array by the method {@link - * Character#toChars(int)} and the character in that array were - * then {@link #append(char[]) appended} to this character + * converted to a {@code char} array by the method + * {@link Character#toChars(int)} and the character in that array + * were then {@link #append(char[]) appended} to this character * sequence. * * @param codePoint a Unicode code point * @return a reference to this object. * @exception IllegalArgumentException if the specified - * codePoint isn't a valid Unicode code point + * {@code codePoint} isn't a valid Unicode code point */ public AbstractStringBuilder appendCodePoint(int codePoint) { if (!Character.isValidCodePoint(codePoint)) { @@ -879,27 +886,27 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of a subarray of the str + * Inserts the string representation of a subarray of the {@code str} * array argument into this sequence. The subarray begins at the - * specified offset and extends len chars. + * specified {@code offset} and extends {@code len} {@code char}s. * The characters of the subarray are inserted into this sequence at - * the position indicated by index. The length of this - * sequence increases by len chars. + * the position indicated by {@code index}. The length of this + * sequence increases by {@code len} {@code char}s. * * @param index position at which to insert subarray. - * @param str A char array. - * @param offset the index of the first char in subarray to + * @param str A {@code char} array. + * @param offset the index of the first {@code char} in subarray to * be inserted. - * @param len the number of chars in the subarray to + * @param len the number of {@code char}s in the subarray to * be inserted. * @return This object - * @throws StringIndexOutOfBoundsException if index - * is negative or greater than length(), or - * offset or len are negative, or - * (offset+len) is greater than - * str.length. + * @throws StringIndexOutOfBoundsException if {@code index} + * is negative or greater than {@code length()}, or + * {@code offset} or {@code len} are negative, or + * {@code (offset+len)} is greater than + * {@code str.length}. */ - public AbstractStringBuilder insert(int index, char str[], int offset, + public AbstractStringBuilder insert(int index, char[] str, int offset, int len) { if ((index < 0) || (index > length())) @@ -918,20 +925,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the Object + * Inserts the string representation of the {@code Object} * argument into this character sequence. *

- * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(Object)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param obj an Object. + * @param obj an {@code Object}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -942,28 +950,28 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Inserts the string into this character sequence. *

- * The characters of the String argument are inserted, in + * The characters of the {@code String} argument are inserted, in * order, into this sequence at the indicated offset, moving up any * characters originally above that position and increasing the length * of this sequence by the length of the argument. If - * str is null, then the four characters - * "null" are inserted into this sequence. + * {@code str} is {@code null}, then the four characters + * {@code "null"} are inserted into this sequence. *

* The character at index k in the new character sequence is * equal to: *

    *
  • the character at index k in the old character sequence, if - * k is less than offset - *
  • the character at index k-offset in the - * argument str, if k is not less than - * offset but is less than offset+str.length() - *
  • the character at index k-str.length() in the + * k is less than {@code offset} + *
  • the character at index k{@code -offset} in the + * argument {@code str}, if k is not less than + * {@code offset} but is less than {@code offset+str.length()} + *
  • the character at index k{@code -str.length()} in the * old character sequence, if k is not less than - * offset+str.length() + * {@code offset+str.length()} *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. * @param str a string. @@ -986,27 +994,30 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the char array + * Inserts the string representation of the {@code char} array * argument into this sequence. *

* The characters of the array argument are inserted into the * contents of this sequence at the position indicated by - * offset. The length of this sequence increases by + * {@code offset}. The length of this sequence increases by * the length of the argument. *

- * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char[])} and the - * characters of that string were then - * {@link #insert(int,String) inserted} into this - * character sequence at the position indicated by - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(char[])}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. + *

+ * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. * @param str a character array. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ - public AbstractStringBuilder insert(int offset, char str[]) { + public AbstractStringBuilder insert(int offset, char[] str) { if ((offset < 0) || (offset > length())) throw new StringIndexOutOfBoundsException(offset); int len = str.length; @@ -1020,18 +1031,20 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the specified CharSequence into this sequence. + * Inserts the specified {@code CharSequence} into this sequence. *

- * The characters of the CharSequence argument are inserted, + * The characters of the {@code CharSequence} argument are inserted, * in order, into this sequence at the indicated offset, moving up * any characters originally above that position and increasing the length * of this sequence by the length of the argument s. *

* The result of this method is exactly the same as if it were an - * invocation of this object's insert(dstOffset, s, 0, s.length()) method. + * invocation of this object's + * {@link #insert(int,CharSequence,int,int) insert}(dstOffset, s, 0, s.length()) + * method. * - *

If s is null, then the four characters - * "null" are inserted into this sequence. + *

If {@code s} is {@code null}, then the four characters + * {@code "null"} are inserted into this sequence. * * @param dstOffset the offset. * @param s the sequence to be inserted @@ -1047,51 +1060,51 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts a subsequence of the specified CharSequence into + * Inserts a subsequence of the specified {@code CharSequence} into * this sequence. *

- * The subsequence of the argument s specified by - * start and end are inserted, + * The subsequence of the argument {@code s} specified by + * {@code start} and {@code end} are inserted, * in order, into this sequence at the specified destination offset, moving * up any characters originally above that position. The length of this - * sequence is increased by end - start. + * sequence is increased by {@code end - start}. *

* The character at index k in this sequence becomes equal to: *

    *
  • the character at index k in this sequence, if - * k is less than dstOffset - *
  • the character at index k+start-dstOffset in - * the argument s, if k is greater than or equal to - * dstOffset but is less than dstOffset+end-start - *
  • the character at index k-(end-start) in this + * k is less than {@code dstOffset} + *
  • the character at index k{@code +start-dstOffset} in + * the argument {@code s}, if k is greater than or equal to + * {@code dstOffset} but is less than {@code dstOffset+end-start} + *
  • the character at index k{@code -(end-start)} in this * sequence, if k is greater than or equal to - * dstOffset+end-start + * {@code dstOffset+end-start} *

- * The dstOffset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code dstOffset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. *

The start argument must be nonnegative, and not greater than - * end. + * {@code end}. *

The end argument must be greater than or equal to - * start, and less than or equal to the length of s. + * {@code start}, and less than or equal to the length of s. * - *

If s is null, then this method inserts + *

If {@code s} is {@code null}, then this method inserts * characters as if the s parameter was a sequence containing the four - * characters "null". + * characters {@code "null"}. * * @param dstOffset the offset in this sequence. * @param s the sequence to be inserted. * @param start the starting index of the subsequence to be inserted. * @param end the end index of the subsequence to be inserted. * @return a reference to this object. - * @throws IndexOutOfBoundsException if dstOffset - * is negative or greater than this.length(), or - * start or end are negative, or - * start is greater than end or - * end is greater than s.length() + * @throws IndexOutOfBoundsException if {@code dstOffset} + * is negative or greater than {@code this.length()}, or + * {@code start} or {@code end} are negative, or + * {@code start} is greater than {@code end} or + * {@code end} is greater than {@code s.length()} */ public AbstractStringBuilder insert(int dstOffset, CharSequence s, - int start, int end) { + int start, int end) { if (s == null) s = "null"; if ((dstOffset < 0) || (dstOffset > this.length())) @@ -1115,20 +1128,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the boolean + * Inserts the string representation of the {@code boolean} * argument into this sequence. *

- * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(boolean)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param b a boolean. + * @param b a {@code boolean}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1137,25 +1151,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the char + * Inserts the string representation of the {@code char} * argument into this sequence. *

- * The second argument is inserted into the contents of this sequence - * at the position indicated by offset. The length - * of this sequence increases by one. - *

- * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char)} and the character - * in that string were then {@link #insert(int, String) inserted} into - * this character sequence at the position indicated by - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(char)}, + * and the character in that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param c a char. + * @param c a {@code char}. * @return a reference to this object. * @throws IndexOutOfBoundsException if the offset is invalid. */ @@ -1170,20 +1180,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the second int + * Inserts the string representation of the second {@code int} * argument into this sequence. *

- * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(int)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param i an int. + * @param i an {@code int}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1192,20 +1203,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the long + * Inserts the string representation of the {@code long} * argument into this sequence. *

- * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the position - * indicated by offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(long)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param l a long. + * @param l a {@code long}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1214,20 +1226,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the float + * Inserts the string representation of the {@code float} * argument into this sequence. *

- * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(float)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param f a float. + * @param f a {@code float}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1236,20 +1249,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the double + * Inserts the string representation of the {@code double} * argument into this sequence. *

- * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(double)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

- * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param d a double. + * @param d a {@code double}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ diff --git a/src/share/classes/java/lang/StringBuffer.java b/src/share/classes/java/lang/StringBuffer.java index e0ecbef0e..f44bb2bae 100644 --- a/src/share/classes/java/lang/StringBuffer.java +++ b/src/share/classes/java/lang/StringBuffer.java @@ -212,7 +212,7 @@ package java.lang; * @throws NullPointerException {@inheritDoc} * @throws IndexOutOfBoundsException {@inheritDoc} */ - public synchronized void getChars(int srcBegin, int srcEnd, char dst[], + public synchronized void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { super.getChars(srcBegin, srcEnd, dst, dstBegin); @@ -228,10 +228,6 @@ package java.lang; value[index] = ch; } - /** - * @see java.lang.String#valueOf(java.lang.Object) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(Object obj) { super.append(String.valueOf(obj)); return this; @@ -314,20 +310,19 @@ package java.lang; return this; } - public synchronized StringBuffer append(char str[]) { + public synchronized StringBuffer append(char[] str) { super.append(str); return this; } - public synchronized StringBuffer append(char str[], int offset, int len) { + /** + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public synchronized StringBuffer append(char[] str, int offset, int len) { super.append(str, offset, len); return this; } - /** - * @see java.lang.String#valueOf(boolean) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(boolean b) { super.append(b); return this; @@ -338,10 +333,6 @@ package java.lang; return this; } - /** - * @see java.lang.String#valueOf(int) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(int i) { super.append(i); return this; @@ -355,28 +346,16 @@ package java.lang; return this; } - /** - * @see java.lang.String#valueOf(long) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(long lng) { super.append(lng); return this; } - /** - * @see java.lang.String#valueOf(float) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(float f) { super.append(f); return this; } - /** - * @see java.lang.String#valueOf(double) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(double d) { super.append(d); return this; @@ -437,7 +416,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ - public synchronized StringBuffer insert(int index, char str[], int offset, + public synchronized StringBuffer insert(int index, char[] str, int offset, int len) { super.insert(index, str, offset, len); @@ -446,9 +425,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(java.lang.Object) - * @see #insert(int, java.lang.String) - * @see #length() */ public synchronized StringBuffer insert(int offset, Object obj) { super.insert(offset, String.valueOf(obj)); @@ -457,7 +433,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public synchronized StringBuffer insert(int offset, String str) { super.insert(offset, str); @@ -467,7 +442,7 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ - public synchronized StringBuffer insert(int offset, char str[]) { + public synchronized StringBuffer insert(int offset, char[] str) { super.insert(offset, str); return this; } @@ -498,9 +473,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(boolean) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, boolean b) { return insert(offset, String.valueOf(b)); @@ -508,7 +480,6 @@ package java.lang; /** * @throws IndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public synchronized StringBuffer insert(int offset, char c) { super.insert(offset, c); @@ -517,9 +488,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(int) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, int i) { return insert(offset, String.valueOf(i)); @@ -527,9 +495,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(long) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, long l) { return insert(offset, String.valueOf(l)); @@ -537,9 +502,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(float) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, float f) { return insert(offset, String.valueOf(f)); @@ -547,9 +509,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(double) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, double d) { return insert(offset, String.valueOf(d)); diff --git a/src/share/classes/java/lang/StringBuilder.java b/src/share/classes/java/lang/StringBuilder.java index 99f19768d..e7eea47be 100644 --- a/src/share/classes/java/lang/StringBuilder.java +++ b/src/share/classes/java/lang/StringBuilder.java @@ -124,10 +124,6 @@ public final class StringBuilder append(seq); } - /** - * @see java.lang.String#valueOf(java.lang.Object) - * @see #append(java.lang.String) - */ public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } @@ -175,7 +171,6 @@ public final class StringBuilder } /** - * @throws IndexOutOfBoundsException {@inheritDoc} */ public StringBuilder append(CharSequence s) { if (s == null) @@ -197,20 +192,19 @@ public final class StringBuilder return this; } - public StringBuilder append(char str[]) { + public StringBuilder append(char[] str) { super.append(str); return this; } - public StringBuilder append(char str[], int offset, int len) { + /** + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public StringBuilder append(char[] str, int offset, int len) { super.append(str, offset, len); return this; } - /** - * @see java.lang.String#valueOf(boolean) - * @see #append(java.lang.String) - */ public StringBuilder append(boolean b) { super.append(b); return this; @@ -221,37 +215,21 @@ public final class StringBuilder return this; } - /** - * @see java.lang.String#valueOf(int) - * @see #append(java.lang.String) - */ public StringBuilder append(int i) { super.append(i); return this; } - /** - * @see java.lang.String#valueOf(long) - * @see #append(java.lang.String) - */ public StringBuilder append(long lng) { super.append(lng); return this; } - /** - * @see java.lang.String#valueOf(float) - * @see #append(java.lang.String) - */ public StringBuilder append(float f) { super.append(f); return this; } - /** - * @see java.lang.String#valueOf(double) - * @see #append(java.lang.String) - */ public StringBuilder append(double d) { super.append(d); return this; @@ -292,7 +270,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ - public StringBuilder insert(int index, char str[], int offset, + public StringBuilder insert(int index, char[] str, int offset, int len) { super.insert(index, str, offset, len); @@ -301,9 +279,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(java.lang.Object) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, Object obj) { return insert(offset, String.valueOf(obj)); @@ -311,7 +286,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public StringBuilder insert(int offset, String str) { super.insert(offset, str); @@ -321,7 +295,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ - public StringBuilder insert(int offset, char str[]) { + public StringBuilder insert(int offset, char[] str) { super.insert(offset, str); return this; } @@ -349,9 +323,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(boolean) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, boolean b) { super.insert(offset, b); @@ -360,7 +331,6 @@ public final class StringBuilder /** * @throws IndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public StringBuilder insert(int offset, char c) { super.insert(offset, c); @@ -369,9 +339,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(int) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, int i) { return insert(offset, String.valueOf(i)); @@ -379,9 +346,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(long) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, long l) { return insert(offset, String.valueOf(l)); @@ -389,9 +353,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(float) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, float f) { return insert(offset, String.valueOf(f)); @@ -399,9 +360,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(double) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, double d) { return insert(offset, String.valueOf(d)); -- cgit v1.2.3 From 2244265d33ba3a74a5958fbd3d44b1636bf0651e Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 9 Sep 2008 15:20:07 -0700 Subject: 6733145: (bf) CharBuffer.subSequence can be updated to take advantage of covariant returns Summary: Change return type to CharBuffer Reviewed-by: alanb --- src/share/classes/java/nio/ByteBufferAs-X-Buffer.java | 2 +- src/share/classes/java/nio/Direct-X-Buffer.java | 2 +- src/share/classes/java/nio/Heap-X-Buffer.java | 2 +- src/share/classes/java/nio/StringCharBuffer.java | 2 +- src/share/classes/java/nio/X-Buffer.java | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java b/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java index 5761fd867..1b959ab6e 100644 --- a/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java +++ b/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java @@ -186,7 +186,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private // --- Methods to support CharSequence --- - public CharSequence subSequence(int start, int end) { + public CharBuffer subSequence(int start, int end) { int pos = position(); int lim = limit(); assert (pos <= lim); diff --git a/src/share/classes/java/nio/Direct-X-Buffer.java b/src/share/classes/java/nio/Direct-X-Buffer.java index d1cfb6b5c..44915f207 100644 --- a/src/share/classes/java/nio/Direct-X-Buffer.java +++ b/src/share/classes/java/nio/Direct-X-Buffer.java @@ -402,7 +402,7 @@ class Direct$Type$Buffer$RW$$BO$ // --- Methods to support CharSequence --- - public CharSequence subSequence(int start, int end) { + public CharBuffer subSequence(int start, int end) { int pos = position(); int lim = limit(); assert (pos <= lim); diff --git a/src/share/classes/java/nio/Heap-X-Buffer.java b/src/share/classes/java/nio/Heap-X-Buffer.java index 0c19ca554..ed59c73a8 100644 --- a/src/share/classes/java/nio/Heap-X-Buffer.java +++ b/src/share/classes/java/nio/Heap-X-Buffer.java @@ -566,7 +566,7 @@ class Heap$Type$Buffer$RW$ // --- Methods to support CharSequence --- - public CharSequence subSequence(int start, int end) { + public CharBuffer subSequence(int start, int end) { if ((start < 0) || (end > length()) || (start > end)) diff --git a/src/share/classes/java/nio/StringCharBuffer.java b/src/share/classes/java/nio/StringCharBuffer.java index 3f49ae1eb..648b1986f 100644 --- a/src/share/classes/java/nio/StringCharBuffer.java +++ b/src/share/classes/java/nio/StringCharBuffer.java @@ -99,7 +99,7 @@ class StringCharBuffer // package-private return str.toString().substring(start + offset, end + offset); } - public final CharSequence subSequence(int start, int end) { + public final CharBuffer subSequence(int start, int end) { try { int pos = position(); return new StringCharBuffer(str, -1, diff --git a/src/share/classes/java/nio/X-Buffer.java b/src/share/classes/java/nio/X-Buffer.java index 99a7468ff..67cf6db3d 100644 --- a/src/share/classes/java/nio/X-Buffer.java +++ b/src/share/classes/java/nio/X-Buffer.java @@ -1239,13 +1239,13 @@ public abstract class $Type$Buffer * smaller than start and no larger than * remaining() * - * @return The new character sequence + * @return The new character buffer * * @throws IndexOutOfBoundsException * If the preconditions on start and end * do not hold */ - public abstract CharSequence subSequence(int start, int end); + public abstract CharBuffer subSequence(int start, int end); // --- Methods to support Appendable --- -- cgit v1.2.3 From 44714f079ee897c2c80ca0a6248b608f650afb66 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Wed, 10 Sep 2008 13:36:47 +0200 Subject: 6734813: Provide a way to construct an ObjectName without checked exceptions 6746649: ObjectName constructors and methods declare unchecked exceptions in throws clauses Reviewed-by: dfuchs --- .../interceptor/DefaultMBeanServerInterceptor.java | 5 +- .../com/sun/jmx/mbeanserver/Repository.java | 2 +- .../classes/com/sun/jmx/mbeanserver/Util.java | 8 - .../com/sun/jmx/namespace/DomainInterceptor.java | 4 +- .../java/lang/management/PlatformComponent.java | 2 +- src/share/classes/java/util/logging/Logging.java | 2 +- .../javax/management/MBeanServerDelegate.java | 2 +- src/share/classes/javax/management/ObjectName.java | 161 ++++++++++++++++--- .../javax/management/QueryNotificationFilter.java | 2 +- .../management/event/EventClientDelegateMBean.java | 2 +- .../management/namespace/MBeanServerSupport.java | 14 +- src/share/classes/sun/management/Util.java | 6 +- test/javax/management/ObjectName/ValueOfTest.java | 175 +++++++++++++++++++++ 13 files changed, 327 insertions(+), 58 deletions(-) create mode 100644 test/javax/management/ObjectName/ValueOfTest.java diff --git a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index 7faeb8727..7da3406b9 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -613,8 +613,7 @@ public class DefaultMBeanServerInterceptor List result = new ArrayList(domains.length); for (int i = 0; i < domains.length; i++) { try { - ObjectName dom = - Util.newObjectName(domains[i] + ":x=x"); + ObjectName dom = ObjectName.valueOf(domains[i] + ":x=x"); checkMBeanPermission(mbeanServerName, (String) null, null, dom, "getDomains"); result.add(domains[i]); } catch (SecurityException e) { @@ -1170,7 +1169,7 @@ public class DefaultMBeanServerInterceptor if one is supplied where it shouldn't be). */ final String completeName = domain + name; - return Util.newObjectName(completeName); + return ObjectName.valueOf(completeName); } public String getDefaultDomain() { diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java index 5c3339833..03f3e5277 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java @@ -396,7 +396,7 @@ public class Repository { // Set domain to default if domain is empty and not already set if (dom.length() == 0) - name = Util.newObjectName(domain + name.toString()); + name = ObjectName.valueOf(domain + name.toString()); // Do we have default domain ? if (dom == domain) { // ES: OK (dom & domain are interned) diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/src/share/classes/com/sun/jmx/mbeanserver/Util.java index 93c5c97f6..6307adbf8 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -110,14 +110,6 @@ public class Util { return new ArrayList(c); } - public static ObjectName newObjectName(String s) { - try { - return new ObjectName(s); - } catch (MalformedObjectNameException e) { - throw new IllegalArgumentException(e); - } - } - /* This method can be used by code that is deliberately violating the * allowed checked casts. Rather than marking the whole method containing * the code with @SuppressWarnings, you can use a call to this method for diff --git a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java index 84644d831..7b88087a8 100644 --- a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java @@ -110,7 +110,7 @@ public class DomainInterceptor extends HandlerInterceptor { super(handler); this.domainName = domainName; this.serverName = serverName; - ALL = Util.newObjectName(domainName+":*"); + ALL = ObjectName.valueOf(domainName+":*"); } @Override @@ -437,7 +437,7 @@ public class DomainInterceptor extends HandlerInterceptor { int count=0; for (int i=0;i set = mbs.queryNames(on, null); for (PlatformComponent pc : subComponents) { set.addAll(pc.getObjectNames(mbs)); diff --git a/src/share/classes/java/util/logging/Logging.java b/src/share/classes/java/util/logging/Logging.java index 72468699e..d99782407 100644 --- a/src/share/classes/java/util/logging/Logging.java +++ b/src/share/classes/java/util/logging/Logging.java @@ -118,6 +118,6 @@ class Logging implements LoggingMXBean { } public ObjectName getObjectName() { - return com.sun.jmx.mbeanserver.Util.newObjectName(LogManager.LOGGING_MXBEAN_NAME); + return ObjectName.valueOf(LogManager.LOGGING_MXBEAN_NAME); } } diff --git a/src/share/classes/javax/management/MBeanServerDelegate.java b/src/share/classes/javax/management/MBeanServerDelegate.java index ea4799d3f..00b82a92f 100644 --- a/src/share/classes/javax/management/MBeanServerDelegate.java +++ b/src/share/classes/javax/management/MBeanServerDelegate.java @@ -304,7 +304,7 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean, * @since 1.6 */ public static final ObjectName DELEGATE_NAME = - Util.newObjectName("JMImplementation:type=MBeanServerDelegate"); + ObjectName.valueOf("JMImplementation:type=MBeanServerDelegate"); /* Return a timestamp that is monotonically increasing even if System.currentTimeMillis() isn't (for example, if you call this diff --git a/src/share/classes/javax/management/ObjectName.java b/src/share/classes/javax/management/ObjectName.java index 20bcfce72..8185edc27 100644 --- a/src/share/classes/javax/management/ObjectName.java +++ b/src/share/classes/javax/management/ObjectName.java @@ -413,7 +413,7 @@ public class ObjectName implements Comparable, QueryExp { } private void copyToOtherDomain(String domain, ObjectName aname) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // The domain cannot be null if (domain == null) @@ -467,7 +467,7 @@ public class ObjectName implements Comparable, QueryExp { * is null. */ private void construct(String name) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // The name cannot be null if (name == null) @@ -729,7 +729,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException One of the parameters is null. */ private void construct(String domain, Map props) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // The domain cannot be null if (domain == null) @@ -1071,7 +1071,7 @@ public class ObjectName implements Comparable, QueryExp { * Check if the supplied key is a valid key. */ private static void checkKey(String key) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { if (key == null) throw new NullPointerException("Invalid key (null)"); @@ -1359,9 +1359,10 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException The name parameter * is null. * + * @see #valueOf(String) */ public static ObjectName getInstance(String name) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { return new ObjectName(name); } @@ -1386,10 +1387,11 @@ public class ObjectName implements Comparable, QueryExp { * follow the rules for quoting. * @exception NullPointerException One of the parameters is null. * + * @see #valueOf(String, String, String) */ public static ObjectName getInstance(String domain, String key, String value) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { return new ObjectName(domain, key, value); } @@ -1417,10 +1419,11 @@ public class ObjectName implements Comparable, QueryExp { * quoting. * @exception NullPointerException One of the parameters is null. * + * @see #valueOf(String, Hashtable) */ public static ObjectName getInstance(String domain, Hashtable table) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { return new ObjectName(domain, table); } @@ -1453,11 +1456,120 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException The name is null. * */ - public static ObjectName getInstance(ObjectName name) - throws NullPointerException { + public static ObjectName getInstance(ObjectName name) { if (name.getClass().equals(ObjectName.class)) return name; - return Util.newObjectName(name.getSerializedNameString()); + return valueOf(name.getSerializedNameString()); + } + + /** + *

Return an instance of ObjectName that can be used anywhere + * an object obtained with {@link #ObjectName(String) new + * ObjectName(name)} can be used. The returned object may be of + * a subclass of ObjectName. Calling this method twice with the + * same parameters may return the same object or two equal but + * not identical objects.

+ * + *

This method is equivalent to {@link #getInstance(String)} except that + * it does not throw any checked exceptions.

+ * + * @param name A string representation of the object name. + * + * @return an ObjectName corresponding to the given String. + * + * @exception IllegalArgumentException The string passed as a + * parameter does not have the right format. The {@linkplain + * Throwable#getCause() cause} of this exception will be a + * {@link MalformedObjectNameException}. + * @exception NullPointerException The name parameter + * is null. + * + * @since 1.7 + */ + public static ObjectName valueOf(String name) { + try { + return getInstance(name); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException(e.getMessage(), e); + // Just plain IllegalArgumentException(e) produces an exception + // message "javax.management.MalformedObjectNameException: ..." + // which is distracting. + } + } + + /** + *

Return an instance of ObjectName that can be used anywhere + * an object obtained with {@link #ObjectName(String, String, + * String) new ObjectName(domain, key, value)} can be used. The + * returned object may be of a subclass of ObjectName. Calling + * this method twice with the same parameters may return the same + * object or two equal but not identical objects.

+ * + *

This method is equivalent to {@link #getInstance(String, String, + * String)} except that it does not throw any checked exceptions.

+ * + * @param domain The domain part of the object name. + * @param key The attribute in the key property of the object name. + * @param value The value in the key property of the object name. + * + * @return an ObjectName corresponding to the given domain, + * key, and value. + * + * @exception IllegalArgumentException The + * domain, key, or value + * contains an illegal character, or value does not + * follow the rules for quoting. The {@linkplain + * Throwable#getCause() cause} of this exception will be a + * {@link MalformedObjectNameException}. + * @exception NullPointerException One of the parameters is null. + * + * @since 1.7 + */ + public static ObjectName valueOf(String domain, String key, String value) { + try { + return getInstance(domain, key, value); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + /** + *

Return an instance of ObjectName that can be used anywhere + * an object obtained with {@link #ObjectName(String, Hashtable) + * new ObjectName(domain, table)} can be used. The returned + * object may be of a subclass of ObjectName. Calling this method + * twice with the same parameters may return the same object or + * two equal but not identical objects.

+ * + *

This method is equivalent to {@link #getInstance(String, Hashtable)} + * except that it does not throw any checked exceptions.

+ * + * @param domain The domain part of the object name. + * @param table A hash table containing one or more key + * properties. The key of each entry in the table is the key of a + * key property in the object name. The associated value in the + * table is the associated value in the object name. + * + * @return an ObjectName corresponding to the given domain and + * key mappings. + * + * @exception IllegalArgumentException The domain + * contains an illegal character, or one of the keys or values in + * table contains an illegal character, or one of the + * values in table does not follow the rules for + * quoting. The {@linkplain Throwable#getCause() cause} of this exception + * will be a {@link MalformedObjectNameException}. + * @exception NullPointerException One of the parameters is null. + * + * @since 1.7 + */ + public static ObjectName valueOf(String domain, + Hashtable table) { + try { + return new ObjectName(domain, table); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } } /** @@ -1477,7 +1589,7 @@ public class ObjectName implements Comparable, QueryExp { * @since 1.7 **/ public final ObjectName withDomain(String newDomain) - throws NullPointerException, MalformedObjectNameException { + throws MalformedObjectNameException { return new ObjectName(newDomain, this); } @@ -1490,9 +1602,11 @@ public class ObjectName implements Comparable, QueryExp { * parameter does not have the right format. * @exception NullPointerException The name parameter * is null. + * + * @see #valueOf(String) */ public ObjectName(String name) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { construct(name); } @@ -1508,9 +1622,11 @@ public class ObjectName implements Comparable, QueryExp { * contains an illegal character, or value does not * follow the rules for quoting. * @exception NullPointerException One of the parameters is null. + * + * @see #valueOf(String, String, String) */ public ObjectName(String domain, String key, String value) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // If key or value are null a NullPointerException // will be thrown by the put method in Hashtable. // @@ -1533,9 +1649,11 @@ public class ObjectName implements Comparable, QueryExp { * values in table does not follow the rules for * quoting. * @exception NullPointerException One of the parameters is null. + * + * @see #valueOf(String, Hashtable) */ public ObjectName(String domain, Hashtable table) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { construct(domain, table); /* The exception for when a key or value in the table is not a String is now ClassCastException rather than @@ -1629,8 +1747,7 @@ public class ObjectName implements Comparable, QueryExp { * * @since 1.6 */ - public boolean isPropertyValuePattern(String property) - throws NullPointerException, IllegalArgumentException { + public boolean isPropertyValuePattern(String property) { if (property == null) throw new NullPointerException("key property can't be null"); for (int i = 0; i < _ca_array.length; i++) { @@ -1691,7 +1808,7 @@ public class ObjectName implements Comparable, QueryExp { * * @exception NullPointerException If property is null. */ - public String getKeyProperty(String property) throws NullPointerException { + public String getKeyProperty(String property) { return _getKeyPropertyList().get(property); } @@ -1950,8 +2067,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException if s is null. * */ - public static String quote(String s) - throws NullPointerException { + public static String quote(String s) { final StringBuilder buf = new StringBuilder("\""); final int len = s.length(); for (int i = 0; i < len; i++) { @@ -1995,8 +2111,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException if q is null. * */ - public static String unquote(String q) - throws IllegalArgumentException, NullPointerException { + public static String unquote(String q) { final StringBuilder buf = new StringBuilder(); final int len = q.length(); if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"') @@ -2041,7 +2156,7 @@ public class ObjectName implements Comparable, QueryExp { * * @since 1.6 */ - public static final ObjectName WILDCARD = Util.newObjectName("*:*"); + public static final ObjectName WILDCARD = valueOf("*:*"); // Category : Utilities <=================================== @@ -2064,7 +2179,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException if name is null. * */ - public boolean apply(ObjectName name) throws NullPointerException { + public boolean apply(ObjectName name) { if (name == null) throw new NullPointerException(); diff --git a/src/share/classes/javax/management/QueryNotificationFilter.java b/src/share/classes/javax/management/QueryNotificationFilter.java index 42451088f..7d1990fa2 100644 --- a/src/share/classes/javax/management/QueryNotificationFilter.java +++ b/src/share/classes/javax/management/QueryNotificationFilter.java @@ -170,7 +170,7 @@ public class QueryNotificationFilter implements NotificationFilter { private static final long serialVersionUID = -8408613922660635231L; private static final ObjectName DEFAULT_NAME = - Util.newObjectName(":type=Notification"); + ObjectName.valueOf(":type=Notification"); private static final QueryExp trueQuery; static { ValueExp zero = Query.value(0); diff --git a/src/share/classes/javax/management/event/EventClientDelegateMBean.java b/src/share/classes/javax/management/event/EventClientDelegateMBean.java index ba57cce9c..7c0b3107c 100644 --- a/src/share/classes/javax/management/event/EventClientDelegateMBean.java +++ b/src/share/classes/javax/management/event/EventClientDelegateMBean.java @@ -96,7 +96,7 @@ public interface EventClientDelegateMBean { * {@value #OBJECT_NAME_STRING}. */ public final static ObjectName OBJECT_NAME = - Util.newObjectName(OBJECT_NAME_STRING); + ObjectName.valueOf(OBJECT_NAME_STRING); /** * A unique listener identifier specified for an EventClient. diff --git a/src/share/classes/javax/management/namespace/MBeanServerSupport.java b/src/share/classes/javax/management/namespace/MBeanServerSupport.java index 2f0e19838..903be3c30 100644 --- a/src/share/classes/javax/management/namespace/MBeanServerSupport.java +++ b/src/share/classes/javax/management/namespace/MBeanServerSupport.java @@ -193,14 +193,6 @@ import javax.management.loading.ClassLoaderRepository; * } * * public class PropsMBS extends MBeanServerSupport { - * private static ObjectName newObjectName(String name) { - * try { - * return new ObjectName(name); - * } catch (MalformedObjectNameException e) { - * throw new AssertionError(e); - * } - * } - * * public static class PropertyImpl implements PropertyMBean { * private final String name; * @@ -219,7 +211,7 @@ import javax.management.loading.ClassLoaderRepository; * throws InstanceNotFoundException { * * // Check that the name is a legal one for a Property MBean - * ObjectName namePattern = newObjectName( + * ObjectName namePattern = ObjectName.valueOf( * "com.example:type=Property,name=\"*\""); * if (!namePattern.apply(name)) * throw new InstanceNotFoundException(name); @@ -239,7 +231,7 @@ import javax.management.loading.ClassLoaderRepository; * {@code Set names = new TreeSet();} * Properties props = System.getProperties(); * for (String propName : props.stringPropertyNames()) { - * ObjectName objectName = newObjectName( + * ObjectName objectName = ObjectName.valueOf( * "com.example:type=Property,name=" + * ObjectName.quote(propName)); * names.add(objectName); @@ -278,7 +270,7 @@ import javax.management.loading.ClassLoaderRepository; * } * * public void propertyChanged(String name, String newValue) { - * ObjectName objectName = newObjectName( + * ObjectName objectName = ObjectName.valueOf( * "com.example:type=Property,name=" + ObjectName.quote(name)); * Notification n = new Notification( * "com.example.property.changed", objectName, 0L, diff --git a/src/share/classes/sun/management/Util.java b/src/share/classes/sun/management/Util.java index 3c0997531..1da808300 100644 --- a/src/share/classes/sun/management/Util.java +++ b/src/share/classes/sun/management/Util.java @@ -43,12 +43,8 @@ class Util { return (String[]) list.toArray(EMPTY_STRING_ARRAY); } - static ObjectName newObjectName(String name) { - return com.sun.jmx.mbeanserver.Util.newObjectName(name); - } - public static ObjectName newObjectName(String domainAndType, String name) { - return newObjectName(domainAndType + ",name=" + name); + return ObjectName.valueOf(domainAndType + ",name=" + name); } private static ManagementPermission monitorPermission = diff --git a/test/javax/management/ObjectName/ValueOfTest.java b/test/javax/management/ObjectName/ValueOfTest.java new file mode 100644 index 000000000..4a68a3d4c --- /dev/null +++ b/test/javax/management/ObjectName/ValueOfTest.java @@ -0,0 +1,175 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6734813 + * @summary Test the ObjectName.valueOf methods + * @author Eamonn McManus + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +public class ValueOfTest { + public static void main(String[] args) throws Exception { + // Calls that should work + testPositive("d:foo=bar,baz=buh"); + testPositive("foo", "bar", "baz"); + Hashtable h = new Hashtable(); + h.put("foo", "bar"); + h.put("baz", "buh"); + testPositive("domain", h); + + // Calls that should not work + testNegative("d"); + testNegative("d:"); + testNegative("d::foo=bar"); + testNegative("d:", "foo", "bar"); + testNegative("d", "foo=", "bar"); + testNegative("d:", h); + testNegative("d", new Hashtable()); + } + + private static void testPositive(Object... args) throws Exception { + Method valueOf = valueOfMethod(args); + Method getInstance = getInstanceMethod(args); + Constructor constructor = constructor(args); + + Object valueOfValue = valueOf.invoke(null, args); + Object getInstanceValue = getInstance.invoke(null, args); + Object constructorValue = constructor.newInstance(args); + + String argString = + Arrays.toString(args).replace('[', '(').replace(']', ')'); + + if (!valueOfValue.equals(getInstanceValue)) { + throw new Exception( + "valueOf" + argString + " differs from getInstance" + + argString); + } + + if (!valueOfValue.equals(constructorValue)) { + throw new Exception( + "valueOf" + argString + " differs from new ObjectName " + + argString); + } + + System.out.println("OK: valueOf" + argString); + } + + private static void testNegative(Object... args) throws Exception { + Method valueOf = valueOfMethod(args); + Method getInstance = getInstanceMethod(args); + + String argString = + Arrays.toString(args).replace('[', '(').replace(']', ')'); + + final Throwable valueOfException; + try { + valueOf.invoke(null, args); + throw new Exception("valueOf" + argString + " did not fail but should"); + } catch (InvocationTargetException e) { + valueOfException = e.getCause(); + } + if (!(valueOfException instanceof IllegalArgumentException)) { + throw new Exception( + "valueOf" + argString + " threw " + + valueOfException.getClass().getName() + " instead of " + + "IllegalArgumentException", valueOfException); + } + + final Throwable valueOfCause = valueOfException.getCause(); + if (!(valueOfCause instanceof MalformedObjectNameException)) { + throw new Exception( + "valueOf" + argString + " threw exception with wrong " + + "type of cause", valueOfCause); + } + + if (!valueOfException.getMessage().equals(valueOfCause.getMessage())) { + // The IllegalArgumentException should have the same message as + // the MalformedObjectNameException it wraps. + // This isn't specified but is desirable. + throw new Exception( + "valueOf" + argString + ": message in wrapping " + + "IllegalArgumentException (" + valueOfException.getMessage() + + ") differs from message in wrapped " + + "MalformedObjectNameException (" + valueOfCause.getMessage() + + ")"); + } + + final Throwable getInstanceException; + try { + getInstance.invoke(null, args); + throw new Exception("getInstance" + argString + " did not fail but should"); + } catch (InvocationTargetException e) { + getInstanceException = e.getCause(); + } + if (!(getInstanceException instanceof MalformedObjectNameException)) { + throw new Exception( + "getInstance" + argString + " threw wrong exception", + getInstanceException); + } + + if (!valueOfException.getMessage().equals(getInstanceException.getMessage())) { + // Again this is not specified. + throw new Exception( + "Exception message from valueOf" + argString + " (" + + valueOfException.getMessage() + ") differs from message " + + "from getInstance" + argString + " (" + + getInstanceException.getMessage() + ")"); + } + + System.out.println("OK (correct exception): valueOf" + argString); + } + + private static Method valueOfMethod(Object[] args) throws Exception { + return method("valueOf", args); + } + + private static Method getInstanceMethod(Object[] args) throws Exception { + return method("getInstance", args); + } + + private static Method method(String name, Object[] args) throws Exception { + Class[] argTypes = argTypes(args); + return ObjectName.class.getMethod(name, argTypes); + } + + private static Constructor constructor(Object[] args) throws Exception { + Class[] argTypes = argTypes(args); + return ObjectName.class.getConstructor(argTypes); + } + + private static Class[] argTypes(Object[] args) { + Class[] argTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) + argTypes[i] = args[i].getClass(); + return argTypes; + } +} -- cgit v1.2.3 From 9e121110d16501a20f18ed055ff1ac0ee20383c3 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Wed, 10 Sep 2008 14:56:57 +0200 Subject: 6746759: Fix for 6734813 introduced build break Reviewed-by: dfuchs --- src/share/classes/sun/management/ClassLoadingImpl.java | 2 +- src/share/classes/sun/management/CompilationImpl.java | 2 +- src/share/classes/sun/management/HotSpotDiagnostic.java | 2 +- src/share/classes/sun/management/HotspotInternal.java | 2 +- src/share/classes/sun/management/ManagementFactoryHelper.java | 4 ++-- src/share/classes/sun/management/MemoryImpl.java | 2 +- src/share/classes/sun/management/OperatingSystemImpl.java | 2 +- src/share/classes/sun/management/RuntimeImpl.java | 2 +- src/share/classes/sun/management/ThreadImpl.java | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/share/classes/sun/management/ClassLoadingImpl.java b/src/share/classes/sun/management/ClassLoadingImpl.java index 2f4b6ed07..9eda58380 100644 --- a/src/share/classes/sun/management/ClassLoadingImpl.java +++ b/src/share/classes/sun/management/ClassLoadingImpl.java @@ -71,6 +71,6 @@ class ClassLoadingImpl implements ClassLoadingMXBean { native static void setVerboseClass(boolean value); public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.CLASS_LOADING_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/CompilationImpl.java b/src/share/classes/sun/management/CompilationImpl.java index d69d9cc64..9c13e67eb 100644 --- a/src/share/classes/sun/management/CompilationImpl.java +++ b/src/share/classes/sun/management/CompilationImpl.java @@ -70,7 +70,7 @@ class CompilationImpl implements CompilationMXBean { } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.COMPILATION_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.COMPILATION_MXBEAN_NAME); } diff --git a/src/share/classes/sun/management/HotSpotDiagnostic.java b/src/share/classes/sun/management/HotSpotDiagnostic.java index f42188ff1..d33337fcf 100644 --- a/src/share/classes/sun/management/HotSpotDiagnostic.java +++ b/src/share/classes/sun/management/HotSpotDiagnostic.java @@ -117,6 +117,6 @@ public class HotSpotDiagnostic implements HotSpotDiagnosticMXBean { } public ObjectName getObjectName() { - return Util.newObjectName("com.sun.management:type=HotSpotDiagnostic"); + return ObjectName.valueOf("com.sun.management:type=HotSpotDiagnostic"); } } diff --git a/src/share/classes/sun/management/HotspotInternal.java b/src/share/classes/sun/management/HotspotInternal.java index 7006699a4..88f9a8268 100644 --- a/src/share/classes/sun/management/HotspotInternal.java +++ b/src/share/classes/sun/management/HotspotInternal.java @@ -41,7 +41,7 @@ public class HotspotInternal private final static String HOTSPOT_INTERNAL_MBEAN_NAME = "sun.management:type=HotspotInternal"; - private static ObjectName objName = Util.newObjectName(HOTSPOT_INTERNAL_MBEAN_NAME); + private static ObjectName objName = ObjectName.valueOf(HOTSPOT_INTERNAL_MBEAN_NAME); private MBeanServer server = null; /** diff --git a/src/share/classes/sun/management/ManagementFactoryHelper.java b/src/share/classes/sun/management/ManagementFactoryHelper.java index 9bb4785d7..c07acfdbc 100644 --- a/src/share/classes/sun/management/ManagementFactoryHelper.java +++ b/src/share/classes/sun/management/ManagementFactoryHelper.java @@ -220,7 +220,7 @@ public class ManagementFactoryHelper { */ private static void addMBean(MBeanServer mbs, Object mbean, String mbeanName) { try { - final ObjectName objName = Util.newObjectName(mbeanName); + final ObjectName objName = ObjectName.valueOf(mbeanName); // inner class requires these fields to be final final MBeanServer mbs0 = mbs; @@ -280,7 +280,7 @@ public class ManagementFactoryHelper { private static void unregisterMBean(MBeanServer mbs, String mbeanName) { try { - final ObjectName objName = Util.newObjectName(mbeanName); + final ObjectName objName = ObjectName.valueOf(mbeanName); // inner class requires these fields to be final final MBeanServer mbs0 = mbs; diff --git a/src/share/classes/sun/management/MemoryImpl.java b/src/share/classes/sun/management/MemoryImpl.java index 29da467ce..aa56186ae 100644 --- a/src/share/classes/sun/management/MemoryImpl.java +++ b/src/share/classes/sun/management/MemoryImpl.java @@ -177,7 +177,7 @@ class MemoryImpl extends NotificationEmitterSupport } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.MEMORY_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.MEMORY_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/OperatingSystemImpl.java b/src/share/classes/sun/management/OperatingSystemImpl.java index cfe729688..9ab8b5695 100644 --- a/src/share/classes/sun/management/OperatingSystemImpl.java +++ b/src/share/classes/sun/management/OperatingSystemImpl.java @@ -74,7 +74,7 @@ public class OperatingSystemImpl implements OperatingSystemMXBean { } } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/RuntimeImpl.java b/src/share/classes/sun/management/RuntimeImpl.java index e58040e83..55bcbdc85 100644 --- a/src/share/classes/sun/management/RuntimeImpl.java +++ b/src/share/classes/sun/management/RuntimeImpl.java @@ -149,7 +149,7 @@ class RuntimeImpl implements RuntimeMXBean { } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.RUNTIME_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/ThreadImpl.java b/src/share/classes/sun/management/ThreadImpl.java index d12258b9e..565966e9d 100644 --- a/src/share/classes/sun/management/ThreadImpl.java +++ b/src/share/classes/sun/management/ThreadImpl.java @@ -415,7 +415,7 @@ class ThreadImpl implements ThreadMXBean { private static native void resetContentionTimes0(long tid); public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.THREAD_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.THREAD_MXBEAN_NAME); } } -- cgit v1.2.3 From 0a7c25b652c7794023624da781f979c40b993104 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Wed, 10 Sep 2008 16:27:13 +0200 Subject: 6746754: jmx namespace: test for leading separator missing 6669137: RFE: InstanceNotFoundException should have a constructor that takes an ObjectName 6746796: jmx namespaces: Several tests are missing an @bug or @run keyword Summary: Note on 6669137: first implementation of 6669137 was actually pushed with 5072476 - here we only have a small update and a test case. Also re-fixes 6737133: Compilation failure of test/javax/management/eventService/LeaseManagerDeadlockTest.java which had failed. Reviewed-by: emcmanus, yjoan --- .../com/sun/jmx/namespace/RoutingProxy.java | 17 +- .../management/InstanceNotFoundException.java | 2 +- .../MBeanServer/InstanceNotFoundExceptionTest.java | 76 +++++++ .../MBeanServerFactory/NamedMBeanServerTest.java | 1 + .../eventService/LeaseManagerDeadlockTest.java | 1 + .../management/namespace/DomainCreationTest.java | 1 + .../namespace/EventWithNamespaceControlTest.java | 1 + .../namespace/EventWithNamespaceTest.java | 2 +- .../management/namespace/ExportNamespaceTest.java | 1 + test/javax/management/namespace/JMXDomainTest.java | 1 + .../namespace/JMXNamespaceSecurityTest.java | 1 + .../management/namespace/JMXNamespaceTest.java | 1 + .../management/namespace/JMXNamespaceViewTest.java | 1 + .../management/namespace/JMXNamespacesTest.java | 1 + .../namespace/JMXRemoteNamespaceTest.java | 1 + .../javax/management/namespace/LazyDomainTest.java | 1 + .../namespace/LeadingSeparatorsTest.java | 227 +++++++++++++++++++++ .../namespace/NamespaceCreationTest.java | 1 + .../namespace/NamespaceNotificationsTest.java | 1 + .../management/namespace/NullObjectNameTest.java | 1 + .../javax/management/namespace/QueryNamesTest.java | 1 + .../namespace/RemoveNotificationListenerTest.java | 1 + .../namespace/RoutingServerProxyTest.java | 1 + .../namespace/SerialParamProcessorTest.java | 1 + .../management/namespace/SourceNamespaceTest.java | 1 + .../namespace/VirtualMBeanNotifTest.java | 1 + .../management/namespace/VirtualMBeanTest.java | 2 +- .../namespace/VirtualNamespaceQueryTest.java | 1 + .../management/namespace/VirtualPropsTest.java | 2 +- 29 files changed, 344 insertions(+), 6 deletions(-) create mode 100644 test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java create mode 100644 test/javax/management/namespace/LeadingSeparatorsTest.java diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java index e0635c585..aa35c5bda 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java @@ -48,6 +48,19 @@ import javax.management.namespace.JMXNamespaces; *

{@link RoutingConnectionProxy}: to narrow down into an * MBeanServerConnection.

*

{@link RoutingServerProxy}: to narrow down into an MBeanServer.

+ * + *

This class can also be used to "broaden" from a namespace. The same + * class is used for both purposes because in both cases all that happens + * is that ObjectNames are rewritten in one way on the way in (e.g. the + * parameter of getMBeanInfo) and another way on the way out (e.g. the + * return value of queryNames).

+ * + *

Specifically, if you narrow into "a//" then you want to add the + * "a//" prefix to ObjectNames on the way in and subtract it on the way + * out. But ClientContext uses this class to subtract the + * "jmx.context//foo=bar//" prefix on the way in and add it back on the + * way out.

+ * *

* This API is a Sun internal API and is subject to changes without notice. *

@@ -245,8 +258,8 @@ public abstract class RoutingProxy throw x; } catch (MBeanException ex) { throw new IOException("Failed to get "+attributeName+": "+ - ex, - ex.getTargetException()); + ex.getCause(), + ex.getCause()); } catch (Exception ex) { throw new IOException("Failed to get "+attributeName+": "+ ex,ex); diff --git a/src/share/classes/javax/management/InstanceNotFoundException.java b/src/share/classes/javax/management/InstanceNotFoundException.java index baeaed095..3a8376f7b 100644 --- a/src/share/classes/javax/management/InstanceNotFoundException.java +++ b/src/share/classes/javax/management/InstanceNotFoundException.java @@ -61,6 +61,6 @@ public class InstanceNotFoundException extends OperationsException { * @since 1.7 */ public InstanceNotFoundException(ObjectName name) { - this(name.toString()); + this(String.valueOf(name)); } } diff --git a/test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java b/test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java new file mode 100644 index 000000000..300796553 --- /dev/null +++ b/test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6669137 + * @summary Test the constructors of InstanceNotFoundExceptionTest. + * @author Daniel Fuchs + * @compile InstanceNotFoundExceptionTest.java + * @run main InstanceNotFoundExceptionTest + */ + +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; + +public class InstanceNotFoundExceptionTest { + public static void main(String[] args) throws Exception { + final InstanceNotFoundException x = + new InstanceNotFoundException(); + System.out.println("InstanceNotFoundException(): "+x.getMessage()); + + final String msg = "who is toto?"; + final InstanceNotFoundException x2 = + new InstanceNotFoundException(msg); + if (!msg.equals(x2.getMessage())) + throw new Exception("Bad message: expected "+msg+ + ", got "+x2.getMessage()); + System.out.println("InstanceNotFoundException(" + + msg+"): "+x2.getMessage()); + + final InstanceNotFoundException x3 = + new InstanceNotFoundException((String)null); + if (x3.getMessage() != null) + throw new Exception("Bad message: expected "+null+ + ", got "+x3.getMessage()); + System.out.println("InstanceNotFoundException((String)null): "+ + x3.getMessage()); + + final ObjectName n = new ObjectName("who is toto?:type=msg"); + final InstanceNotFoundException x4 = + new InstanceNotFoundException(n); + if (!String.valueOf(n).equals(x4.getMessage())) + throw new Exception("Bad message: expected "+n+ + ", got "+x4.getMessage()); + System.out.println("InstanceNotFoundException(" + + n+"): "+x4.getMessage()); + + final InstanceNotFoundException x5 = + new InstanceNotFoundException((ObjectName)null); + if (!String.valueOf((ObjectName)null).equals(x5.getMessage())) + throw new Exception("Bad message: expected " + + String.valueOf((ObjectName)null)+" got "+x5.getMessage()); + System.out.println("InstanceNotFoundException((ObjectName)null): "+ + x5.getMessage()); + } +} diff --git a/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java b/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java index e26168318..8a8d248cb 100644 --- a/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java +++ b/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java @@ -25,6 +25,7 @@ * @test * @summary Test named MBeanServers. * @author Daniel Fuchs + * @bug 6299231 * @run clean NamedMBeanServerTest * @run build NamedMBeanServerTest * @run main NamedMBeanServerTest diff --git a/test/javax/management/eventService/LeaseManagerDeadlockTest.java b/test/javax/management/eventService/LeaseManagerDeadlockTest.java index 453aafe4b..ab7d14d54 100644 --- a/test/javax/management/eventService/LeaseManagerDeadlockTest.java +++ b/test/javax/management/eventService/LeaseManagerDeadlockTest.java @@ -27,6 +27,7 @@ * @summary Check that a lock is not held when a LeaseManager expires. * @author Eamonn McManus * @compile -XDignore.symbol.file=true LeaseManagerDeadlockTest.java + * @run main LeaseManagerDeadlockTest */ import com.sun.jmx.event.LeaseManager; diff --git a/test/javax/management/namespace/DomainCreationTest.java b/test/javax/management/namespace/DomainCreationTest.java index 93a98dc62..02a09868e 100644 --- a/test/javax/management/namespace/DomainCreationTest.java +++ b/test/javax/management/namespace/DomainCreationTest.java @@ -23,6 +23,7 @@ /* * * @test DomainCreationTest.java + * @bug 5072476 * @summary Test the creation and registration of JMXDomain instances. * @author Daniel Fuchs * @run clean DomainCreationTest Wombat WombatMBean diff --git a/test/javax/management/namespace/EventWithNamespaceControlTest.java b/test/javax/management/namespace/EventWithNamespaceControlTest.java index c1e5a9d46..1e3901104 100644 --- a/test/javax/management/namespace/EventWithNamespaceControlTest.java +++ b/test/javax/management/namespace/EventWithNamespaceControlTest.java @@ -27,6 +27,7 @@ * @summary Check -Djmx.remote.use.event.service=true and * -Djmx.remote.delegate.event.service * @author Daniel Fuchs + * @bug 5072476 5108776 * @run clean EventWithNamespaceTest EventWithNamespaceControlTest * Wombat WombatMBean JMXRemoteTargetNamespace * NamespaceController NamespaceControllerMBean diff --git a/test/javax/management/namespace/EventWithNamespaceTest.java b/test/javax/management/namespace/EventWithNamespaceTest.java index 44fca88c7..748fdbeff 100644 --- a/test/javax/management/namespace/EventWithNamespaceTest.java +++ b/test/javax/management/namespace/EventWithNamespaceTest.java @@ -24,7 +24,7 @@ /* * * @test EventWithNamespaceTest.java 1.8 - * @bug 6539857 + * @bug 6539857 5072476 5108776 * @summary General Namespace & Notifications test. * @author Daniel Fuchs * @run clean EventWithNamespaceTest Wombat WombatMBean diff --git a/test/javax/management/namespace/ExportNamespaceTest.java b/test/javax/management/namespace/ExportNamespaceTest.java index e73452714..ec49a4c0c 100644 --- a/test/javax/management/namespace/ExportNamespaceTest.java +++ b/test/javax/management/namespace/ExportNamespaceTest.java @@ -26,6 +26,7 @@ * @summary Test that you can export a single namespace through a * JMXConnectorServer. * @author Daniel Fuchs + * @bug 5072476 * @run clean ExportNamespaceTest Wombat WombatMBean * @run build ExportNamespaceTest Wombat WombatMBean * @run main ExportNamespaceTest diff --git a/test/javax/management/namespace/JMXDomainTest.java b/test/javax/management/namespace/JMXDomainTest.java index 4a1a14e96..258cead1a 100644 --- a/test/javax/management/namespace/JMXDomainTest.java +++ b/test/javax/management/namespace/JMXDomainTest.java @@ -23,6 +23,7 @@ /* * * @test JMXDomainTest.java + * @bug 5072476 * @summary Basic test for JMXDomain. * @author Daniel Fuchs * @run clean JMXDomainTest Wombat WombatMBean diff --git a/test/javax/management/namespace/JMXNamespaceSecurityTest.java b/test/javax/management/namespace/JMXNamespaceSecurityTest.java index b948012f1..213ffbfb6 100644 --- a/test/javax/management/namespace/JMXNamespaceSecurityTest.java +++ b/test/javax/management/namespace/JMXNamespaceSecurityTest.java @@ -26,6 +26,7 @@ * @test JMXNamespaceSecurityTest.java * @summary General JMXNamespaceSecurityTest test. * @author Daniel Fuchs + * @bug 5072476 6299231 * @run clean JMXNamespaceViewTest JMXNamespaceSecurityTest Wombat WombatMBean * LazyDomainTest * @run build JMXNamespaceSecurityTest JMXNamespaceViewTest Wombat WombatMBean diff --git a/test/javax/management/namespace/JMXNamespaceTest.java b/test/javax/management/namespace/JMXNamespaceTest.java index da553f9c8..1c2ffac9d 100644 --- a/test/javax/management/namespace/JMXNamespaceTest.java +++ b/test/javax/management/namespace/JMXNamespaceTest.java @@ -25,6 +25,7 @@ * * @test JMXNamespaceTest.java * @summary General JMXNamespace test. + * @bug 5072476 * @author Daniel Fuchs * @run clean JMXNamespaceTest * Wombat WombatMBean JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/JMXNamespaceViewTest.java b/test/javax/management/namespace/JMXNamespaceViewTest.java index ad51af301..e13496829 100644 --- a/test/javax/management/namespace/JMXNamespaceViewTest.java +++ b/test/javax/management/namespace/JMXNamespaceViewTest.java @@ -24,6 +24,7 @@ * * @test JMXNamespaceViewTest.java * @summary Test the JMXNamespaceView class. + * @bug 5072476 * @author Daniel Fuchs * @run clean JMXNamespaceViewTest Wombat WombatMBean * @run build JMXNamespaceViewTest Wombat WombatMBean diff --git a/test/javax/management/namespace/JMXNamespacesTest.java b/test/javax/management/namespace/JMXNamespacesTest.java index 4249bf104..4dc7c518a 100644 --- a/test/javax/management/namespace/JMXNamespacesTest.java +++ b/test/javax/management/namespace/JMXNamespacesTest.java @@ -24,6 +24,7 @@ * @test JMXNamespacesTest.java * @summary Test the static method that rewrite ObjectNames in JMXNamespacesTest * @author Daniel Fuchs + * @bug 5072476 * @run clean JMXNamespacesTest * @compile -XDignore.symbol.file=true JMXNamespacesTest.java * @run main JMXNamespacesTest diff --git a/test/javax/management/namespace/JMXRemoteNamespaceTest.java b/test/javax/management/namespace/JMXRemoteNamespaceTest.java index ccc73bfaf..8e5f795a6 100644 --- a/test/javax/management/namespace/JMXRemoteNamespaceTest.java +++ b/test/javax/management/namespace/JMXRemoteNamespaceTest.java @@ -25,6 +25,7 @@ * @test JMXRemoteNamespaceTest.java * @summary Basic tests on a JMXRemoteNamespace. * @author Daniel Fuchs + * @bug 5072476 * @run clean JMXRemoteNamespaceTest Wombat WombatMBean * @run build JMXRemoteNamespaceTest Wombat WombatMBean * @run main JMXRemoteNamespaceTest diff --git a/test/javax/management/namespace/LazyDomainTest.java b/test/javax/management/namespace/LazyDomainTest.java index 83439011e..eda9b6669 100644 --- a/test/javax/management/namespace/LazyDomainTest.java +++ b/test/javax/management/namespace/LazyDomainTest.java @@ -23,6 +23,7 @@ /* * * @test LazyDomainTest.java + * @bug 5072476 * @summary Basic test for Lazy Domains. * @author Daniel Fuchs * @run clean LazyDomainTest Wombat WombatMBean diff --git a/test/javax/management/namespace/LeadingSeparatorsTest.java b/test/javax/management/namespace/LeadingSeparatorsTest.java new file mode 100644 index 000000000..5660b2753 --- /dev/null +++ b/test/javax/management/namespace/LeadingSeparatorsTest.java @@ -0,0 +1,227 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * @test LeadingSeparatorsTest.java + * @summary Test that the semantics of a leading // in ObjectName is respected. + * @author Daniel Fuchs + * @bug 5072476 + * @run clean LeadingSeparatorsTest Wombat WombatMBean + * @compile -XDignore.symbol.file=true LeadingSeparatorsTest.java + * @run build LeadingSeparatorsTest Wombat WombatMBean + * @run main LeadingSeparatorsTest + */ + +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.util.logging.Logger; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXRemoteNamespace; +import javax.management.namespace.JMXNamespace; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * Class LeadingSeparatorsTest + * @author Sun Microsystems, 2005 - All rights reserved. + */ +public class LeadingSeparatorsTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(LeadingSeparatorsTest.class.getName()); + + /** Creates a new instance of NullObjectNameTest */ + public LeadingSeparatorsTest() { + } + + public static interface MyWombatMBean extends WombatMBean { + public Set untrue(ObjectName pat) throws Exception; + } + public static class MyWombat + extends Wombat implements MyWombatMBean { + public MyWombat() throws NotCompliantMBeanException { + super(MyWombatMBean.class); + } + + public Set untrue(ObjectName pat) throws Exception { + final Set res=listMatching(pat.withDomain("*")); + final Set untrue = new HashSet(); + for (ObjectName a:res) { + untrue.add(a.withDomain(pat.getDomain()+"//"+a.getDomain())); + } + return untrue; + } + } + + static String failure=null; + + public static void testRegister() throws Exception { + final MBeanServer top = ManagementFactory.getPlatformMBeanServer(); + final MBeanServer sub = MBeanServerFactory.createMBeanServer(); + final JMXServiceURL url = new JMXServiceURL("rmi",null,0); + final JMXConnectorServer srv = + JMXConnectorServerFactory.newJMXConnectorServer(url,null,sub); + srv.start(); + + try { + + // Create a namespace rmi// that points to 'sub' and flows through + // a JMXRemoteNamespace connected to 'srv' + // The namespace rmi// will accept createMBean, but not registerMBean. + // + final JMXRemoteNamespace rmiHandler = JMXRemoteNamespace. + newJMXRemoteNamespace(srv.getAddress(),null); + top.registerMBean(rmiHandler, + JMXNamespaces.getNamespaceObjectName("rmi")); + top.invoke(JMXNamespaces.getNamespaceObjectName("rmi"), + "connect", null, null); + + // Create a namespace direct// that points to 'sub' and flows + // through a direct reference to 'sub'. + // The namespace direct// will accept createMBean, and registerMBean. + // + final JMXNamespace directHandler = new JMXNamespace(sub); + top.registerMBean(directHandler, + JMXNamespaces.getNamespaceObjectName("direct")); + + final ObjectName n1 = new ObjectName("//direct//w:type=Wombat"); + final ObjectName n2 = new ObjectName("direct//w:type=Wombat"); + final ObjectName n3 = new ObjectName("//rmi//w:type=Wombat"); + final ObjectName n4 = new ObjectName("rmi//w:type=Wombat"); + + // register wombat using an object name with a leading // + final Object obj = new MyWombat(); + // check that returned object name doesn't have the leading // + assertEquals(n2,top.registerMBean(obj, n1).getObjectName()); + System.out.println(n1+" registered"); + + // check that the registered Wombat can be accessed with all its + // names. + System.out.println(n2+" mood is: "+top.getAttribute(n2, "Mood")); + System.out.println(n1+" mood is: "+top.getAttribute(n1, "Mood")); + System.out.println(n4+" mood is: "+top.getAttribute(n4, "Mood")); + System.out.println(n3+" mood is: "+top.getAttribute(n3, "Mood")); + + // call listMatching. The result should not contain any prefix. + final Set res = (Set) + top.invoke(n3, "listMatching", + // remove rmi// from rmi//*:* + JMXNamespaces.deepReplaceHeadNamespace( + new Object[] {ObjectName.WILDCARD.withDomain("rmi//*")}, + "rmi", ""), new String[] {ObjectName.class.getName()}); + + // add rmi// prefix to all names in res. + final Set res1 = + JMXNamespaces.deepReplaceHeadNamespace(res, "", "rmi"); + System.out.println("got: "+res1); + + // compute expected result + final Set res2 = sub.queryNames(null,null); + final Set res3 = new HashSet(); + for (ObjectName o:res2) { + res3.add(o.withDomain("rmi//"+o.getDomain())); + } + System.out.println("expected: "+res3); + assertEquals(res1, res3); + + // invoke "untrue(//niark//niark:*)" + // should return a set were all ObjectNames begin with + // //niark//niark// + // + final Set res4 = (Set) + top.invoke(n3, "untrue", + // remove niark//niark : should remove nothing since + // our ObjectName begins with a leading // + JMXNamespaces.deepReplaceHeadNamespace( + new Object[] { + ObjectName.WILDCARD.withDomain("//niark//niark")}, + "niark//niark", ""), + new String[] {ObjectName.class.getName()}); + System.out.println("got: "+res4); + + // add rmi// should add nothing since the returned names have a + // leading // + // + final Set res5 = + JMXNamespaces.deepReplaceHeadNamespace(res4, "", "rmi"); + System.out.println("got#2: "+res5); + + // compute expected result + final Set res6 = new HashSet(); + for (ObjectName o:res2) { + res6.add(o.withDomain("//niark//niark//"+o.getDomain())); + } + System.out.println("expected: "+res6); + + // both res4 and res5 should be equals to the expected result. + assertEquals(res4, res6); + assertEquals(res5, res6); + + } finally { + srv.stop(); + } + + if (failure != null) + throw new Exception(failure); + + + } + private static void assertEquals(Object x, Object y) { + if (!equal(x, y)) + failed("expected " + string(x) + "; got " + string(y)); + } + + private static boolean equal(Object x, Object y) { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (x.getClass().isArray()) + return Arrays.deepEquals(new Object[] {x}, new Object[] {y}); + return x.equals(y); + } + + private static String string(Object x) { + String s = Arrays.deepToString(new Object[] {x}); + return s.substring(1, s.length() - 1); + } + + + private static void failed(String why) { + failure = why; + new Throwable("FAILED: " + why).printStackTrace(System.out); + } + + public static void main(String[] args) throws Exception { + testRegister(); + } +} diff --git a/test/javax/management/namespace/NamespaceCreationTest.java b/test/javax/management/namespace/NamespaceCreationTest.java index 981cdda42..871bf0220 100644 --- a/test/javax/management/namespace/NamespaceCreationTest.java +++ b/test/javax/management/namespace/NamespaceCreationTest.java @@ -25,6 +25,7 @@ * @test NamespaceCreationTest.java * @summary General JMXNamespace test. * @author Daniel Fuchs + * @bug 5072476 * @run clean NamespaceCreationTest Wombat WombatMBean * @run build NamespaceCreationTest Wombat WombatMBean * @run main NamespaceCreationTest diff --git a/test/javax/management/namespace/NamespaceNotificationsTest.java b/test/javax/management/namespace/NamespaceNotificationsTest.java index ae5bb3c59..9c5a1a04a 100644 --- a/test/javax/management/namespace/NamespaceNotificationsTest.java +++ b/test/javax/management/namespace/NamespaceNotificationsTest.java @@ -25,6 +25,7 @@ * * @test NamespaceNotificationsTest.java 1.12 * @summary General Namespace & Notifications test. + * @bug 5072476 * @author Daniel Fuchs * @run clean NamespaceNotificationsTest * Wombat WombatMBean JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/NullObjectNameTest.java b/test/javax/management/namespace/NullObjectNameTest.java index 7624fb9d9..156e7661d 100644 --- a/test/javax/management/namespace/NullObjectNameTest.java +++ b/test/javax/management/namespace/NullObjectNameTest.java @@ -24,6 +24,7 @@ * @test NullObjectNameTest.java * @summary Test that null ObjectName are correctly handled in namespaces. * @author Daniel Fuchs + * @bug 5072476 * @run clean NullObjectNameTest Wombat WombatMBean * @compile -XDignore.symbol.file=true NullObjectNameTest.java * @run build NullObjectNameTest Wombat WombatMBean diff --git a/test/javax/management/namespace/QueryNamesTest.java b/test/javax/management/namespace/QueryNamesTest.java index 6e15c9a75..1af597ace 100644 --- a/test/javax/management/namespace/QueryNamesTest.java +++ b/test/javax/management/namespace/QueryNamesTest.java @@ -25,6 +25,7 @@ * @test QueryNamesTest.java 1.4 * @summary Test how queryNames works with Namespaces. * @author Daniel Fuchs + * @bug 5072476 * @run clean QueryNamesTest Wombat WombatMBean * @run build QueryNamesTest Wombat WombatMBean * @run main QueryNamesTest diff --git a/test/javax/management/namespace/RemoveNotificationListenerTest.java b/test/javax/management/namespace/RemoveNotificationListenerTest.java index 08375c0db..a8ea2aee3 100644 --- a/test/javax/management/namespace/RemoveNotificationListenerTest.java +++ b/test/javax/management/namespace/RemoveNotificationListenerTest.java @@ -25,6 +25,7 @@ * @test RemoveNotificationListenerTest.java 1.8 * @summary General RemoveNotificationListenerTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean RemoveNotificationListenerTest JMXRemoteTargetNamespace * @compile -XDignore.symbol.file=true JMXRemoteTargetNamespace.java * @run build RemoveNotificationListenerTest JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/RoutingServerProxyTest.java b/test/javax/management/namespace/RoutingServerProxyTest.java index c0302698c..698021321 100644 --- a/test/javax/management/namespace/RoutingServerProxyTest.java +++ b/test/javax/management/namespace/RoutingServerProxyTest.java @@ -25,6 +25,7 @@ * @test RoutingServerProxyTest.java 1.6 * @summary General RoutingServerProxyTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean RoutingServerProxyTest Wombat WombatMBean * @compile -XDignore.symbol.file=true RoutingServerProxyTest.java * @run build RoutingServerProxyTest Wombat WombatMBean diff --git a/test/javax/management/namespace/SerialParamProcessorTest.java b/test/javax/management/namespace/SerialParamProcessorTest.java index 26dd77572..20df761d0 100644 --- a/test/javax/management/namespace/SerialParamProcessorTest.java +++ b/test/javax/management/namespace/SerialParamProcessorTest.java @@ -26,6 +26,7 @@ * @test SerialParamProcessorTest.java 1.8 * @summary General SerialParamProcessorTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean SerialParamProcessorTest Wombat WombatMBean * @compile -XDignore.symbol.file=true SerialParamProcessorTest.java * @run build SerialParamProcessorTest Wombat WombatMBean diff --git a/test/javax/management/namespace/SourceNamespaceTest.java b/test/javax/management/namespace/SourceNamespaceTest.java index 745564b8c..2335eb297 100644 --- a/test/javax/management/namespace/SourceNamespaceTest.java +++ b/test/javax/management/namespace/SourceNamespaceTest.java @@ -24,6 +24,7 @@ * * @test SourceNamespaceTest.java * @summary Test how queryNames works with Namespaces. + * @bug 5072476 * @author Daniel Fuchs * @run clean SourceNamespaceTest Wombat WombatMBean * @run build SourceNamespaceTest Wombat WombatMBean diff --git a/test/javax/management/namespace/VirtualMBeanNotifTest.java b/test/javax/management/namespace/VirtualMBeanNotifTest.java index 301af7acd..cc7bbaf95 100644 --- a/test/javax/management/namespace/VirtualMBeanNotifTest.java +++ b/test/javax/management/namespace/VirtualMBeanNotifTest.java @@ -25,6 +25,7 @@ * @test VirtualMBeanNotifTest.java * @bug 5108776 * @build VirtualMBeanNotifTest Wombat WombatMBean + * @run main VirtualMBeanNotifTest * @summary Test that Virtual MBeans can be implemented and emit notifs. * @author Daniel Fuchs */ diff --git a/test/javax/management/namespace/VirtualMBeanTest.java b/test/javax/management/namespace/VirtualMBeanTest.java index 85860df34..03fd3983e 100644 --- a/test/javax/management/namespace/VirtualMBeanTest.java +++ b/test/javax/management/namespace/VirtualMBeanTest.java @@ -23,7 +23,7 @@ /* * @test VirtualMBeanTest.java - * @bug 5108776 + * @bug 5108776 5072476 * @summary Test that Virtual MBeans can be implemented and emit notifs. * @author Eamonn McManus */ diff --git a/test/javax/management/namespace/VirtualNamespaceQueryTest.java b/test/javax/management/namespace/VirtualNamespaceQueryTest.java index 020c1224f..8af244a7b 100644 --- a/test/javax/management/namespace/VirtualNamespaceQueryTest.java +++ b/test/javax/management/namespace/VirtualNamespaceQueryTest.java @@ -26,6 +26,7 @@ * @test VirtualNamespaceQueryTest.java * @summary General VirtualNamespaceQueryTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean VirtualNamespaceQueryTest Wombat WombatMBean * NamespaceController NamespaceControllerMBean * JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/VirtualPropsTest.java b/test/javax/management/namespace/VirtualPropsTest.java index 8bb57edd5..904cc5359 100644 --- a/test/javax/management/namespace/VirtualPropsTest.java +++ b/test/javax/management/namespace/VirtualPropsTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 5108776 + * @bug 5108776 5072476 * @summary Test the properties use case for Virtual MBeans that is documented * in MBeanServerSupport. * @author Eamonn McManus -- cgit v1.2.3 From 93d284029685169131e764bc0ad929d530230968 Mon Sep 17 00:00:00 2001 From: bristor Date: Thu, 11 Sep 2008 14:58:57 -0700 Subject: 6440786: Cannot create a ZIP file containing zero entries Summary: Allow reading and writing of ZIP files with zero entries. Reviewed-by: alanb --- .../classes/java/util/zip/ZipOutputStream.java | 5 +- src/share/native/java/util/zip/zip_util.c | 26 +++- test/java/util/zip/TestEmptyZip.java | 147 +++++++++++++++++++++ 3 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 test/java/util/zip/TestEmptyZip.java diff --git a/src/share/classes/java/util/zip/ZipOutputStream.java b/src/share/classes/java/util/zip/ZipOutputStream.java index 653942682..797a37392 100644 --- a/src/share/classes/java/util/zip/ZipOutputStream.java +++ b/src/share/classes/java/util/zip/ZipOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -317,9 +317,6 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { if (current != null) { closeEntry(); } - if (xentries.size() < 1) { - throw new ZipException("ZIP file must have at least one entry"); - } // write central directory long off = written; for (XEntry xentry : xentries) diff --git a/src/share/native/java/util/zip/zip_util.c b/src/share/native/java/util/zip/zip_util.c index 1f6e04a1f..5d518cf4c 100644 --- a/src/share/native/java/util/zip/zip_util.c +++ b/src/share/native/java/util/zip/zip_util.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -722,16 +722,22 @@ ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified) } len = zip->len = ZFILE_Lseek(zfd, 0, SEEK_END); - if (len == -1) { - if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0) - *pmsg = errbuf; + if (len <= 0) { + if (len == 0) { /* zip file is empty */ + if (pmsg) { + *pmsg = "zip file is empty"; + } + } else { /* error */ + if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0) + *pmsg = errbuf; + } ZFILE_Close(zfd); freeZip(zip); return NULL; } zip->zfd = zfd; - if (readCEN(zip, -1) <= 0) { + if (readCEN(zip, -1) < 0) { /* An error occurred while trying to read the zip file */ if (pmsg != 0) { /* Set the zip error message */ @@ -947,10 +953,15 @@ jzentry * ZIP_GetEntry(jzfile *zip, char *name, jint ulen) { unsigned int hsh = hash(name); - jint idx = zip->table[hsh % zip->tablelen]; - jzentry *ze; + jint idx; + jzentry *ze = 0; ZIP_Lock(zip); + if (zip->total == 0) { + goto Finally; + } + + idx = zip->table[hsh % zip->tablelen]; /* * This while loop is an optimization where a double lookup @@ -1025,6 +1036,7 @@ ZIP_GetEntry(jzfile *zip, char *name, jint ulen) ulen = 0; } +Finally: ZIP_Unlock(zip); return ze; } diff --git a/test/java/util/zip/TestEmptyZip.java b/test/java/util/zip/TestEmptyZip.java new file mode 100644 index 000000000..d19dee4d4 --- /dev/null +++ b/test/java/util/zip/TestEmptyZip.java @@ -0,0 +1,147 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6334003 6440786 + * @summary Test ability to write and read zip files that have no entries. + * @author Dave Bristor + */ + +import java.io.*; +import java.util.*; +import java.util.zip.*; + +public class TestEmptyZip { + public static void realMain(String[] args) throws Throwable { + String zipName = "foo.zip"; + File f = new File(System.getProperty("test.scratch", "."), zipName); + if (f.exists() && !f.delete()) { + throw new Exception("failed to delete " + zipName); + } + + // Verify 0-length file cannot be read + f.createNewFile(); + ZipFile zf = null; + try { + zf = new ZipFile(f); + fail(); + } catch (Exception ex) { + check(ex.getMessage().contains("zip file is empty")); + } finally { + if (zf != null) { + zf.close(); + } + } + + ZipInputStream zis = null; + try { + zis = new ZipInputStream(new FileInputStream(f)); + ZipEntry ze = zis.getNextEntry(); + check(ze == null); + } catch (Exception ex) { + unexpected(ex); + } finally { + if (zis != null) { + zis.close(); + } + } + + f.delete(); + + // Verify 0-entries file can be written + write(f); + + // Verify 0-entries file can be read + readFile(f); + readStream(f); + + f.delete(); + } + + static void write(File f) throws Exception { + ZipOutputStream zos = null; + try { + zos = new ZipOutputStream(new FileOutputStream(f)); + zos.finish(); + zos.close(); + pass(); + } catch (Exception ex) { + unexpected(ex); + } finally { + if (zos != null) { + zos.close(); + } + } + } + + static void readFile(File f) throws Exception { + ZipFile zf = null; + try { + zf = new ZipFile(f); + + Enumeration e = zf.entries(); + while (e.hasMoreElements()) { + ZipEntry entry = (ZipEntry) e.nextElement(); + fail(); + } + zf.close(); + pass(); + } catch (Exception ex) { + unexpected(ex); + } finally { + if (zf != null) { + zf.close(); + } + } + } + + static void readStream(File f) throws Exception { + ZipInputStream zis = null; + try { + zis = new ZipInputStream(new FileInputStream(f)); + ZipEntry ze = zis.getNextEntry(); + check(ze == null); + byte[] buf = new byte[1024]; + check(zis.read(buf, 0, 1024) == -1); + } finally { + if (zis != null) { + zis.close(); + } + } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static boolean pass() {passed++; return true;} + static boolean fail() {failed++; Thread.dumpStack(); return false;} + static boolean fail(String msg) {System.out.println(msg); return fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} + static boolean equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) return pass(); + else return fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.println("\nPassed = " + passed + " failed = " + failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} -- cgit v1.2.3 From 8849a7c0cc976aa9f3d4be842452099ed8891826 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Fri, 12 Sep 2008 15:17:52 +0200 Subject: 6747411: EventClient causes thread leaks Summary: Reworked thread management in EventClient and related classes. Reviewed-by: sjiang, dfuchs --- .../classes/com/sun/jmx/event/LeaseManager.java | 11 +- .../com/sun/jmx/event/RepeatedSingletonJob.java | 4 +- .../remote/internal/ClientCommunicatorAdmin.java | 4 +- .../javax/management/event/EventClient.java | 5 +- .../javax/management/event/FetchingEventRelay.java | 52 +++--- .../management/event/RMIPushEventForwarder.java | 2 +- .../javax/management/remote/rmi/RMIConnector.java | 2 +- .../eventService/EventClientThreadTest.java | 176 +++++++++++++++++++++ .../management/eventService/SharingThreadTest.java | 2 +- 9 files changed, 220 insertions(+), 38 deletions(-) create mode 100644 test/javax/management/eventService/EventClientThreadTest.java diff --git a/src/share/classes/com/sun/jmx/event/LeaseManager.java b/src/share/classes/com/sun/jmx/event/LeaseManager.java index cb1b88bf5..33409a06c 100644 --- a/src/share/classes/com/sun/jmx/event/LeaseManager.java +++ b/src/share/classes/com/sun/jmx/event/LeaseManager.java @@ -27,7 +27,6 @@ package com.sun.jmx.event; import com.sun.jmx.remote.util.ClassLogger; import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -115,6 +114,7 @@ public class LeaseManager { scheduled = null; } callback.run(); + executor.shutdown(); } } @@ -131,6 +131,13 @@ public class LeaseManager { logger.trace("stop", "canceling lease"); scheduled.cancel(false); scheduled = null; + try { + executor.shutdown(); + } catch (SecurityException e) { + // OK: caller doesn't have RuntimePermission("modifyThread") + // which is unlikely in reality but triggers a test failure otherwise + logger.trace("stop", "exception from executor.shutdown", e); + } } private final Runnable callback; @@ -138,7 +145,7 @@ public class LeaseManager { private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, - new DaemonThreadFactory("LeaseManager")); + new DaemonThreadFactory("JMX LeaseManager %d")); private static final ClassLogger logger = new ClassLogger("javax.management.event", "LeaseManager"); diff --git a/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java index 7de1b40e9..2fe4a3a15 100644 --- a/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java +++ b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java @@ -95,7 +95,9 @@ public abstract class RepeatedSingletonJob implements Runnable { executor.execute(this); } catch (RejectedExecutionException e) { logger.warning( - "setEventReceiver", "Executor threw exception", e); + "execute", + "Executor threw exception (" + this.getClass().getName() + ")", + e); throw new RejectedExecutionException( "Executor.execute threw exception -" + "should not be possible", e); diff --git a/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java b/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java index a6635aad8..f90cbc4c5 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java @@ -32,13 +32,15 @@ import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.EnvHelp; public abstract class ClientCommunicatorAdmin { + private static volatile long threadNo = 1; + public ClientCommunicatorAdmin(long period) { this.period = period; if (period > 0) { checker = new Checker(); - Thread t = new Thread(checker); + Thread t = new Thread(checker, "JMX client heartbeat " + ++threadNo); t.setDaemon(true); t.start(); } else diff --git a/src/share/classes/javax/management/event/EventClient.java b/src/share/classes/javax/management/event/EventClient.java index 4b8101353..10a4df500 100644 --- a/src/share/classes/javax/management/event/EventClient.java +++ b/src/share/classes/javax/management/event/EventClient.java @@ -264,11 +264,12 @@ public class EventClient implements EventConsumer, NotificationManager { new PerThreadGroupPool.Create() { public ScheduledThreadPoolExecutor createThreadPool(ThreadGroup group) { ThreadFactory daemonThreadFactory = new DaemonThreadFactory( - "EventClient lease renewer %d"); + "JMX EventClient lease renewer %d"); ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor( 20, daemonThreadFactory); - exec.setKeepAliveTime(3, TimeUnit.SECONDS); + exec.setKeepAliveTime(1, TimeUnit.SECONDS); exec.allowCoreThreadTimeOut(true); + exec.setRemoveOnCancelPolicy(true); return exec; } }; diff --git a/src/share/classes/javax/management/event/FetchingEventRelay.java b/src/share/classes/javax/management/event/FetchingEventRelay.java index 2b65f9b12..9aa68df0f 100644 --- a/src/share/classes/javax/management/event/FetchingEventRelay.java +++ b/src/share/classes/javax/management/event/FetchingEventRelay.java @@ -31,10 +31,8 @@ import com.sun.jmx.remote.util.ClassLogger; import java.io.IOException; import java.io.NotSerializableException; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import javax.management.MBeanException; @@ -215,50 +213,47 @@ public class FetchingEventRelay implements EventRelay { this.maxNotifs = maxNotifs; if (executor == null) { - executor = Executors.newSingleThreadScheduledExecutor( + ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(1, daemonThreadFactory); - } + stpe.setKeepAliveTime(1, TimeUnit.SECONDS); + stpe.allowCoreThreadTimeOut(true); + executor = stpe; + this.defaultExecutor = stpe; + } else + this.defaultExecutor = null; this.executor = executor; - if (executor instanceof ScheduledExecutorService) - leaseScheduler = (ScheduledExecutorService) executor; - else { - leaseScheduler = Executors.newSingleThreadScheduledExecutor( - daemonThreadFactory); - } startSequenceNumber = 0; fetchingJob = new MyJob(); } - public void setEventReceiver(EventReceiver eventReceiver) { + public synchronized void setEventReceiver(EventReceiver eventReceiver) { if (logger.traceOn()) { logger.trace("setEventReceiver", ""+eventReceiver); } EventReceiver old = this.eventReceiver; - synchronized(fetchingJob) { - this.eventReceiver = eventReceiver; - if (old == null && eventReceiver != null) - fetchingJob.resume(); - } + this.eventReceiver = eventReceiver; + if (old == null && eventReceiver != null) + fetchingJob.resume(); } public String getClientId() { return clientId; } - public void stop() { + public synchronized void stop() { if (logger.traceOn()) { logger.trace("stop", ""); } - synchronized(fetchingJob) { - if (stopped) { - return; - } - - stopped = true; - clientId = null; + if (stopped) { + return; } + + stopped = true; + clientId = null; + if (defaultExecutor != null) + defaultExecutor.shutdown(); } private class MyJob extends RepeatedSingletonJob { @@ -372,10 +367,9 @@ public class FetchingEventRelay implements EventRelay { private final EventClientDelegateMBean delegate; private String clientId; private boolean stopped = false; - private volatile ScheduledFuture leaseRenewalFuture; private final Executor executor; - private final ScheduledExecutorService leaseScheduler; + private final ExecutorService defaultExecutor; private final MyJob fetchingJob; private final long timeout; @@ -385,5 +379,5 @@ public class FetchingEventRelay implements EventRelay { new ClassLogger("javax.management.event", "FetchingEventRelay"); private static final ThreadFactory daemonThreadFactory = - new DaemonThreadFactory("FetchingEventRelay-executor"); + new DaemonThreadFactory("JMX FetchingEventRelay executor %d"); } diff --git a/src/share/classes/javax/management/event/RMIPushEventForwarder.java b/src/share/classes/javax/management/event/RMIPushEventForwarder.java index 2018f98ad..751300d54 100644 --- a/src/share/classes/javax/management/event/RMIPushEventForwarder.java +++ b/src/share/classes/javax/management/event/RMIPushEventForwarder.java @@ -185,7 +185,7 @@ public class RMIPushEventForwarder implements EventForwarder { private static final ExecutorService executor = Executors.newCachedThreadPool( - new DaemonThreadFactory("RMIEventForwarder Executor")); + new DaemonThreadFactory("JMX RMIEventForwarder Executor")); private final SendingJob sendingJob = new SendingJob(); private final BlockingQueue buffer; diff --git a/src/share/classes/javax/management/remote/rmi/RMIConnector.java b/src/share/classes/javax/management/remote/rmi/RMIConnector.java index bdcbb1568..a620235ac 100644 --- a/src/share/classes/javax/management/remote/rmi/RMIConnector.java +++ b/src/share/classes/javax/management/remote/rmi/RMIConnector.java @@ -420,7 +420,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable new PerThreadGroupPool.Create() { public ThreadPoolExecutor createThreadPool(ThreadGroup group) { ThreadFactory daemonThreadFactory = new DaemonThreadFactory( - "RMIConnector listener dispatch %d"); + "JMX RMIConnector listener dispatch %d"); ThreadPoolExecutor exec = new ThreadPoolExecutor( 1, 10, 1, TimeUnit.SECONDS, new LinkedBlockingDeque(), diff --git a/test/javax/management/eventService/EventClientThreadTest.java b/test/javax/management/eventService/EventClientThreadTest.java new file mode 100644 index 000000000..910bc9cc2 --- /dev/null +++ b/test/javax/management/eventService/EventClientThreadTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6747411 + * @summary Check that EventClient instances don't leak threads. + * @author Eamonn McManus + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.Set; +import java.util.TreeSet; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerNotification; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class EventClientThreadTest { + private static final int MAX_TIME_SECONDS = 20; + + private static final BlockingQueue queue = + new ArrayBlockingQueue(100); + + private static final NotificationListener queueListener = + new NotificationListener() { + public void handleNotification(Notification notification, + Object handback) { + queue.add(notification); + } + }; + + private static final NotificationFilter dummyFilter = + new NotificationFilter() { + public boolean isNotificationEnabled(Notification notification) { + return true; + } + }; + + public static void main(String[] args) throws Exception { + long start = System.currentTimeMillis(); + long deadline = start + MAX_TIME_SECONDS * 1000; + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"); + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer( + url, null, mbs); + cs.start(); + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + ThreadMXBean threads = ManagementFactory.getThreadMXBean(); + + System.out.println("Opening and closing some EventClients..."); + // If we create a connection, then create and destroy EventClients + // over it, then close it, there should be no "JMX *" threads left. + for (int i = 0; i < 5; i++) + test(mbsc); + + cc.close(); + + showTime("opening and closing initial EventClients", start); + + Set jmxThreads = threadsMatching("JMX .*"); + while (!jmxThreads.isEmpty() && System.currentTimeMillis() < deadline) { + Set jmxThreadsNow = threadsMatching("JMX .*"); + Set gone = new TreeSet(jmxThreads); + gone.removeAll(jmxThreadsNow); + for (String s : gone) + showTime("expiry of \"" + s + "\"", start); + jmxThreads = jmxThreadsNow; + Thread.sleep(10); + } + if (System.currentTimeMillis() >= deadline) { + showThreads(threads); + throw new Exception("Timed out waiting for JMX threads to expire"); + } + + showTime("waiting for JMX threads to expire", start); + + System.out.println("TEST PASSED"); + } + + static void showThreads(ThreadMXBean threads) throws Exception { + long[] ids = threads.getAllThreadIds(); + for (long id : ids) { + ThreadInfo ti = threads.getThreadInfo(id); + String name = (ti == null) ? "(defunct)" : ti.getThreadName(); + System.out.printf("%4d %s\n", id, name); + } + } + + static void showTime(String what, long start) { + long elapsed = System.currentTimeMillis() - start; + System.out.printf("Time after %s: %.3f s\n", what, elapsed / 1000.0); + } + + static Set threadsMatching(String pattern) { + Set matching = new TreeSet(); + ThreadMXBean threads = ManagementFactory.getThreadMXBean(); + long[] ids = threads.getAllThreadIds(); + for (long id : ids) { + ThreadInfo ti = threads.getThreadInfo(id); + String name = (ti == null) ? "(defunct)" : ti.getThreadName(); + if (name.matches(pattern)) + matching.add(name); + } + return matching; + } + + static void test(MBeanServerConnection mbsc) throws Exception { + final ObjectName delegateName = MBeanServerDelegate.DELEGATE_NAME; + final ObjectName testName = new ObjectName("test:type=Test"); + EventClient ec = new EventClient(mbsc); + ec.addNotificationListener(delegateName, queueListener, null, null); + mbsc.createMBean(MBeanServerDelegate.class.getName(), testName); + mbsc.unregisterMBean(testName); + final String[] expectedTypes = { + MBeanServerNotification.REGISTRATION_NOTIFICATION, + MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + }; + for (String s : expectedTypes) { + Notification n = queue.poll(3, TimeUnit.SECONDS); + if (n == null) + throw new Exception("Timed out waiting for notif: " + s); + if (!(n instanceof MBeanServerNotification)) + throw new Exception("Got notif of wrong class: " + n.getClass()); + if (!n.getType().equals(s)) { + throw new Exception("Got notif of wrong type: " + n.getType() + + " (expecting " + s + ")"); + } + } + ec.removeNotificationListener(delegateName, queueListener); + + ec.addNotificationListener(delegateName, queueListener, dummyFilter, "foo"); + ec.removeNotificationListener(delegateName, queueListener, dummyFilter, "foo"); + + ec.close(); + } +} \ No newline at end of file diff --git a/test/javax/management/eventService/SharingThreadTest.java b/test/javax/management/eventService/SharingThreadTest.java index a3d7fd37a..7339d0806 100644 --- a/test/javax/management/eventService/SharingThreadTest.java +++ b/test/javax/management/eventService/SharingThreadTest.java @@ -1,4 +1,4 @@ -/*/* +/* * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -- cgit v1.2.3 From 126213f6669086ce5252ab9f45872346383c16b2 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Fri, 12 Sep 2008 17:58:15 +0200 Subject: 6747899: jmx namespaces: hooks for permission checks should be defined in HandlerInterceptor Reviewed-by: emcmanus --- .../com/sun/jmx/namespace/HandlerInterceptor.java | 163 ++++++++++++++++++++- .../namespace/RoutingMBeanServerConnection.java | 162 +++----------------- 2 files changed, 174 insertions(+), 151 deletions(-) diff --git a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java index 7c2f39348..566724419 100644 --- a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java @@ -135,7 +135,11 @@ public abstract class HandlerInterceptor public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException { try { - return super.getAttributes(name, attributes); + final String[] authorized = + checkAttributes(name,attributes,"getAttribute"); + final AttributeList attrList = + super.getAttributes(name,authorized); + return attrList; } catch (IOException ex) { throw handleIOException(ex,"getAttributes",name,attributes); } @@ -185,7 +189,8 @@ public abstract class HandlerInterceptor public void removeNotificationListener(ObjectName name, ObjectName listener) throws InstanceNotFoundException, ListenerNotFoundException { try { - super.removeNotificationListener(name, listener); + check(name,null,"removeNotificationListener"); + super.removeNotificationListener(name,listener); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name,listener); } @@ -205,7 +210,9 @@ public abstract class HandlerInterceptor @Override public String[] getDomains() { try { - return super.getDomains(); + check(null,null,"getDomains"); + final String[] domains = super.getDomains(); + return checkDomains(domains,"getDomains"); } catch (IOException ex) { throw handleIOException(ex,"getDomains"); } @@ -228,7 +235,10 @@ public abstract class HandlerInterceptor InvalidAttributeValueException, MBeanException, ReflectionException { try { - super.setAttribute(name, attribute); + check(name, + (attribute==null?null:attribute.getName()), + "setAttribute"); + super.setAttribute(name,attribute); } catch (IOException ex) { throw handleIOException(ex,"setAttribute",name, attribute); } @@ -237,8 +247,10 @@ public abstract class HandlerInterceptor // From MBeanServerConnection: catch & handles IOException @Override public Set queryNames(ObjectName name, QueryExp query) { + if (name == null) name=ObjectName.WILDCARD; try { - return super.queryNames(name, query); + checkPattern(name,null,"queryNames"); + return super.queryNames(name,query); } catch (IOException ex) { throw handleIOException(ex,"queryNames",name, query); } @@ -247,8 +259,10 @@ public abstract class HandlerInterceptor // From MBeanServerConnection: catch & handles IOException @Override public Set queryMBeans(ObjectName name, QueryExp query) { + if (name == null) name=ObjectName.WILDCARD; try { - return super.queryMBeans(name, query); + checkPattern(name,null,"queryMBeans"); + return super.queryMBeans(name,query); } catch (IOException ex) { throw handleIOException(ex,"queryMBeans",name, query); } @@ -259,6 +273,7 @@ public abstract class HandlerInterceptor public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { try { + check(name, null, "isInstanceOf"); return super.isInstanceOf(name, className); } catch (IOException ex) { throw handleIOException(ex,"isInstanceOf",name, className); @@ -272,6 +287,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name); } catch (IOException ex) { throw handleIOException(ex,"createMBean",className, name); @@ -286,6 +303,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name, loaderName); } catch (IOException ex) { throw handleIOException(ex,"createMBean",className, name, loaderName); @@ -298,6 +317,7 @@ public abstract class HandlerInterceptor throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { try { + check(name, attribute, "getAttribute"); return super.getAttribute(name, attribute); } catch (IOException ex) { throw handleIOException(ex,"getAttribute",name, attribute); @@ -310,6 +330,7 @@ public abstract class HandlerInterceptor NotificationFilter filter, Object handback) throws InstanceNotFoundException, ListenerNotFoundException { try { + check(name,null,"removeNotificationListener"); super.removeNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name, @@ -324,6 +345,7 @@ public abstract class HandlerInterceptor Object handback) throws InstanceNotFoundException, ListenerNotFoundException { try { + check(name,null,"removeNotificationListener"); super.removeNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name, @@ -337,6 +359,7 @@ public abstract class HandlerInterceptor NotificationListener listener) throws InstanceNotFoundException, ListenerNotFoundException { try { + check(name,null,"removeNotificationListener"); super.removeNotificationListener(name, listener); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name, @@ -350,6 +373,7 @@ public abstract class HandlerInterceptor NotificationListener listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException { try { + check(name,null,"addNotificationListener"); super.addNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"addNotificationListener",name, @@ -363,6 +387,7 @@ public abstract class HandlerInterceptor NotificationFilter filter, Object handback) throws InstanceNotFoundException { try { + check(name,null,"addNotificationListener"); super.addNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"addNotificationListener",name, @@ -385,6 +410,7 @@ public abstract class HandlerInterceptor public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { try { + check(name, null, "unregisterMBean"); super.unregisterMBean(name); } catch (IOException ex) { throw handleIOException(ex,"unregisterMBean",name); @@ -397,6 +423,7 @@ public abstract class HandlerInterceptor throws InstanceNotFoundException, IntrospectionException, ReflectionException { try { + check(name, null, "getMBeanInfo"); return super.getMBeanInfo(name); } catch (IOException ex) { throw handleIOException(ex,"getMBeanInfo",name); @@ -408,6 +435,7 @@ public abstract class HandlerInterceptor public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException { try { + check(name, null, "getObjectInstance"); return super.getObjectInstance(name); } catch (IOException ex) { throw handleIOException(ex,"getObjectInstance",name); @@ -422,6 +450,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name, params, signature); } catch (IOException ex) { throw handleIOException(ex,"createMBean",className, name, @@ -437,6 +467,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name, loaderName, params, signature); } catch (IOException ex) { @@ -450,7 +482,9 @@ public abstract class HandlerInterceptor public AttributeList setAttributes(ObjectName name,AttributeList attributes) throws InstanceNotFoundException, ReflectionException { try { - return super.setAttributes(name, attributes); + final AttributeList authorized = + checkAttributes(name, attributes, "setAttribute"); + return super.setAttributes(name, authorized); } catch (IOException ex) { throw handleIOException(ex,"setAttributes",name, attributes); } @@ -462,6 +496,7 @@ public abstract class HandlerInterceptor String[] signature) throws InstanceNotFoundException, MBeanException, ReflectionException { try { + check(name, operationName, "invoke"); return super.invoke(name, operationName, params, signature); } catch (IOException ex) { throw handleIOException(ex,"invoke",name, operationName, @@ -582,4 +617,118 @@ public abstract class HandlerInterceptor "Not supported in this namespace: "+namespace)); } + /** + * A result might be excluded for security reasons. + */ + @Override + boolean excludesFromResult(ObjectName targetName, String queryMethod) { + return !checkQuery(targetName, queryMethod); + } + + + //---------------------------------------------------------------------- + // Hooks for checking permissions + //---------------------------------------------------------------------- + + /** + * This method is a hook to implement permission checking in subclasses. + * A subclass may override this method and throw a {@link + * SecurityException} if the permission is denied. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param member The {@link + * javax.management.namespace.JMXNamespacePermission#getMember member} + * name. + * @param action The {@link + * javax.management.namespace.JMXNamespacePermission#getActions action} + * name. + * @throws SecurityException if the caller doesn't have the permission + * to perform the given action on the MBean pointed to + * by routingName. + */ + abstract void check(ObjectName routingName, + String member, String action); + + // called in createMBean and registerMBean + abstract void checkCreate(ObjectName routingName, String className, + String action); + + /** + * This is a hook to implement permission checking in subclasses. + * + * Checks that the caller has sufficient permission for returning + * information about {@code sourceName} in {@code action}. + * + * Subclass may override this method and return false if the caller + * doesn't have sufficient permissions. + * + * @param routingName The name of the MBean to include or exclude from + * the query, expressed in the enclosing context. + * This is of the form {@code //}. + * @param action one of "queryNames" or "queryMBeans" + * @return true if {@code sourceName} can be returned. + */ + abstract boolean checkQuery(ObjectName routingName, String action); + + /** + * This method is a hook to implement permission checking in subclasses. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + * @throws SecurityException if the caller doesn't have the permission + * to perform {@code action} on the MBean pointed to by routingName. + */ + abstract String[] checkAttributes(ObjectName routingName, + String[] attributes, String action); + + /** + * This method is a hook to implement permission checking in subclasses. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + * @throws SecurityException if the caller doesn't have the permission + * to perform {@code action} on the MBean pointed to by routingName. + */ + abstract AttributeList checkAttributes(ObjectName routingName, + AttributeList attributes, String action); + + /** + * This method is a hook to implement permission checking in subclasses. + * Checks that the caller as the necessary permissions to view the + * given domain. If not remove the domains for which the caller doesn't + * have permission from the list. + *

+ * By default, this method always returns {@code domains} + * + * @param domains The domains to return. + * @param action "getDomains" + * @return a filtered list of domains. + */ + String[] checkDomains(String[] domains, String action) { + return domains; + } + + // A priori check for queryNames/queryMBeans/ + void checkPattern(ObjectName routingPattern, + String member, String action) { + // pattern is checked only at posteriori by checkQuery. + // checking it a priori usually doesn't work, because ObjectName.apply + // does not work between two patterns. + // We only check that we have the permission requested for 'action'. + check(null,null,action); + } + + + } diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java index 70df9b504..7022e7e29 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java @@ -161,11 +161,7 @@ public abstract class RoutingMBeanServerConnection tmp = source().queryNames(sourceName,query); final Set out = processOutputNames(tmp); //System.err.println("queryNames: out: "+out); @@ -467,7 +442,6 @@ public abstract class RoutingMBeanServerConnection//}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - String[] checkAttributes(ObjectName routingName, - String[] attributes, String action) { - check(routingName,null,action); - return attributes; - } - - /** - * This method is a hook to implement permission checking in subclasses. - * By default, this method does nothing and simply returns - * {@code attribute}. - * - * @param routingName The name of the MBean in the enclosing context. - * This is of the form {@code //}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - AttributeList checkAttributes(ObjectName routingName, - AttributeList attributes, String action) { - check(routingName,null,action); - return attributes; - } - - /** - * This method is a hook to implement permission checking in subclasses. - * By default, this method does nothing. - * A subclass may override this method and throw a {@link - * SecurityException} if the permission is denied. - * - * @param routingName The name of the MBean in the enclosing context. - * This is of the form {@code //}. - * @param member The {@link - * javax.management.namespace.JMXNamespacePermission#getMember member} - * name. - * @param action The {@link - * javax.management.namespace.JMXNamespacePermission#getActions action} - * name. - */ - void check(ObjectName routingName, - String member, String action) { - } - - // called in createMBean and registerMBean - void checkCreate(ObjectName routingName, String className, - String action) { - } - - // A priori check for queryNames/queryMBeans/ - void checkPattern(ObjectName routingPattern, - String member, String action) { - // pattern is checked only at posteriori by checkQuery. - // checking it a priori usually doesn't work, because ObjectName.apply - // does not work between two patterns. - // We only check that we have the permission requested for 'action'. - check(null,null,action); - } - - /** - * This is a hook to implement permission checking in subclasses. + * Returns true if the given targetName must be excluded from the + * query result. + * In this base class, always return {@code false}. + * By default all object names returned by the sources are + * transmitted to the caller - there is no filtering. * - * Checks that the caller has sufficient permission for returning - * information about {@code sourceName} in {@code action}. - * - * By default always return true. Subclass may override this method - * and return false if the caller doesn't have sufficient permissions. - * - * @param routingName The name of the MBean to include or exclude from - * the query, expressed in the enclosing context. - * This is of the form {@code //}. - * @param action one of "queryNames" or "queryMBeans" - * @return true if {@code sourceName} can be returned. + * @param name A target object name expressed in the caller's + * context. In the case of cascading, where the source + * is a sub agent mounted on e.g. namespace "foo", + * that would be a name prefixed by "foo//"... + * @param queryMethod either "queryNames" or "queryMBeans". + * @return true if the name must be excluded. */ - boolean checkQuery(ObjectName routingName, String action) { - return true; + boolean excludesFromResult(ObjectName targetName, String queryMethod) { + return false; } - /** - * This method is a hook to implement permission checking in subclasses. - * Checks that the caller as the necessary permissions to view the - * given domain. If not remove the domains for which the caller doesn't - * have permission from the list. - *

- * By default, this method always returns {@code domains} - * - * @param domains The domains to return. - * @param action "getDomains" - * @return a filtered list of domains. - */ - String[] checkDomains(String[] domains, String action) { - return domains; - } } -- cgit v1.2.3 From 9b1a8c88c10f5f9187861a46dbf70a52583b50f4 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Fri, 12 Sep 2008 19:06:38 +0200 Subject: 6747983: jmx namespace: unspecified self-link detection logic Reviewed-by: emcmanus --- .../sun/jmx/namespace/NamespaceInterceptor.java | 226 +---------------- .../management/namespace/JMXRemoteNamespace.java | 112 +-------- .../management/namespace/JMXNamespaceTest.java | 278 +++------------------ 3 files changed, 42 insertions(+), 574 deletions(-) diff --git a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java index 11afeb2af..6862066d2 100644 --- a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java @@ -25,22 +25,15 @@ package com.sun.jmx.namespace; import com.sun.jmx.defaults.JmxProperties; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.UUID; import java.util.logging.Logger; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.MBeanServer; -import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; -import javax.management.QueryExp; -import javax.management.namespace.JMXNamespaces; import javax.management.namespace.JMXNamespace; import javax.management.namespace.JMXNamespacePermission; @@ -54,8 +47,6 @@ import javax.management.namespace.JMXNamespacePermission; */ public class NamespaceInterceptor extends HandlerInterceptor { - private static final Logger PROBE_LOG = Logger.getLogger( - JmxProperties.NAMESPACE_LOGGER+".probe"); // The target name space in which the NamepsaceHandler is mounted. private final String targetNs; @@ -64,21 +55,6 @@ public class NamespaceInterceptor extends HandlerInterceptor { private final ObjectNameRouter proc; - /** - * Internal hack. The JMXRemoteNamespace can be closed and reconnected. - * Each time the JMXRemoteNamespace connects, a probe should be sent - * to detect cycle. The MBeanServer exposed by JMXRemoteNamespace thus - * implements the DynamicProbe interface, which makes it possible for - * this handler to know that it should send a new probe. - * - * XXX: TODO this probe thing is way too complex and fragile. - * This *must* go away or be replaced by something simpler. - * ideas are welcomed. - **/ - public static interface DynamicProbe { - public boolean isProbeRequested(); - } - /** * Creates a new instance of NamespaceInterceptor */ @@ -100,164 +76,6 @@ public class NamespaceInterceptor extends HandlerInterceptor { ", namespace="+this.targetNs+")"; } - /* - * XXX: TODO this probe thing is way too complex and fragile. - * This *must* go away or be replaced by something simpler. - * ideas are welcomed. - */ - private volatile boolean probed = false; - private volatile ObjectName probe; - - // Query Pattern that we will send through the source server in order - // to detect self-linking namespaces. - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - final ObjectName makeProbePattern(ObjectName probe) - throws MalformedObjectNameException { - - // we could probably link the probe pattern with the probe - e.g. - // using the UUID as key in the pattern - but is it worth it? it - // also has some side effects on the context namespace - because - // such a probe may get rejected by the jmx.context// namespace. - // - // The trick here is to devise a pattern that is not likely to - // be blocked by intermediate levels. Querying for all namespace - // handlers in the source (or source namespace) is more likely to - // achieve this goal. - // - return ObjectName.getInstance("*" + - JMXNamespaces.NAMESPACE_SEPARATOR + ":" + - JMXNamespace.TYPE_ASSIGNMENT); - } - - // tell whether the name pattern corresponds to what might have been - // sent as a probe. - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - final boolean isProbePattern(ObjectName name) { - final ObjectName p = probe; - if (p == null) return false; - try { - return String.valueOf(name).endsWith(targetNs+ - JMXNamespaces.NAMESPACE_SEPARATOR + "*" + - JMXNamespaces.NAMESPACE_SEPARATOR + ":" + - JMXNamespace.TYPE_ASSIGNMENT); - } catch (RuntimeException x) { - // should not happen. - PROBE_LOG.finest("Ignoring unexpected exception in self link detection: "+ - x); - return false; - } - } - - // The first time a request reaches this NamespaceInterceptor, the - // interceptor will send a probe to detect whether the underlying - // JMXNamespace links to itslef. - // - // One way to create such self-linking namespace would be for instance - // to create a JMXNamespace whose getSourceServer() method would return: - // JMXNamespaces.narrowToNamespace(getMBeanServer(), - // getObjectName().getDomain()) - // - // If such an MBeanServer is returned, then any call to that MBeanServer - // will trigger an infinite loop. - // There can be even trickier configurations if remote connections are - // involved. - // - // In order to prevent this from happening, the NamespaceInterceptor will - // send a probe, in an attempt to detect whether it will receive it at - // the other end. If the probe is received, an exception will be thrown - // in order to break the recursion. The probe is only sent once - when - // the first request to the namespace occurs. The DynamicProbe interface - // can also be used by a Sun JMXNamespace implementation to request the - // emission of a probe at any time (see JMXRemoteNamespace - // implementation). - // - // Probes work this way: the NamespaceInterceptor sets a flag and sends - // a queryNames() request. If a queryNames() request comes in when the flag - // is on, then it deduces that there is a self-linking loop - and instead - // of calling queryNames() on the source MBeanServer of the JMXNamespace - // handler (which would cause the loop to go on) it breaks the recursion - // by returning the probe ObjectName. - // If the NamespaceInterceptor receives the probe ObjectName as result of - // its original sendProbe() request it knows that it has been looping - // back on itslef and throws an IOException... - // - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - // - final void sendProbe(MBeanServerConnection msc) - throws IOException { - try { - PROBE_LOG.fine("Sending probe"); - - // This is just to prevent any other thread to modify - // the probe while the detection cycle is in progress. - // - final ObjectName probePattern; - // we don't want to synchronize on this - we use targetNs - // because it's non null and final. - synchronized (targetNs) { - probed = false; - if (probe != null) { - throw new IOException("concurent connection in progress"); - } - final String uuid = UUID.randomUUID().toString(); - final String endprobe = - JMXNamespaces.NAMESPACE_SEPARATOR + uuid + - ":type=Probe,key="+uuid; - final ObjectName newprobe = - ObjectName.getInstance(endprobe); - probePattern = makeProbePattern(newprobe); - probe = newprobe; - } - - try { - PROBE_LOG.finer("Probe query: "+probePattern+" expecting: "+probe); - final Set res = msc.queryNames(probePattern, null); - final ObjectName expected = probe; - PROBE_LOG.finer("Probe res: "+res); - if (res.contains(expected)) { - throw new IOException("namespace " + - targetNs + " is linking to itself: " + - "cycle detected by probe"); - } - } catch (SecurityException x) { - PROBE_LOG.finer("Can't check for cycles: " + x); - // can't do anything.... - } catch (RuntimeException x) { - PROBE_LOG.finer("Exception raised by queryNames: " + x); - throw x; - } finally { - probe = null; - } - } catch (MalformedObjectNameException x) { - final IOException io = - new IOException("invalid name space: probe failed"); - io.initCause(x); - throw io; - } - PROBE_LOG.fine("Probe returned - no cycles"); - probed = true; - } - - // allows a Sun implementation JMX Namespace, such as the - // JMXRemoteNamespace, to control when a probe should be sent. - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - private boolean isProbeRequested(Object o) { - if (o instanceof DynamicProbe) - return ((DynamicProbe)o).isProbeRequested(); - return false; - } - /** * This method will send a probe to detect self-linking name spaces. * A self linking namespace is a namespace that links back directly @@ -277,29 +95,9 @@ public class NamespaceInterceptor extends HandlerInterceptor { * (see JMXRemoteNamespace implementation). */ private MBeanServer connection() { - try { - final MBeanServer c = super.source(); - if (probe != null) // should not happen - throw new RuntimeException("connection is being probed"); - - if (probed == false || isProbeRequested(c)) { - try { - // Should not happen if class well behaved. - // Never probed. Force it. - //System.err.println("sending probe for " + - // "target="+targetNs+", source="+srcNs); - sendProbe(c); - } catch (IOException io) { - throw new RuntimeException(io.getMessage(), io); - } - } - - if (c != null) { - return c; - } - } catch (RuntimeException x) { - throw x; - } + final MBeanServer c = super.source(); + if (c != null) return c; + // should not come here throw new NullPointerException("getMBeanServerConnection"); } @@ -315,24 +113,6 @@ public class NamespaceInterceptor extends HandlerInterceptor { return super.source(); } - /** - * Calls {@link MBeanServerConnection#queryNames queryNames} - * on the underlying - * {@link #getMBeanServerConnection MBeanServerConnection}. - **/ - @Override - public final Set queryNames(ObjectName name, QueryExp query) { - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - PROBE_LOG.finer("probe is: "+probe+" pattern is: "+name); - if (probe != null && isProbePattern(name)) { - PROBE_LOG.finer("Return probe: "+probe); - return Collections.singleton(probe); - } - return super.queryNames(name, query); - } - @Override protected ObjectName toSource(ObjectName targetName) throws MalformedObjectNameException { diff --git a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java index 1e877e0ce..6958f57f2 100644 --- a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java +++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java @@ -28,11 +28,9 @@ package javax.management.namespace; import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.namespace.JMXNamespaceUtils; -import com.sun.jmx.namespace.NamespaceInterceptor.DynamicProbe; import com.sun.jmx.remote.util.EnvHelp; import java.io.IOException; -import java.security.AccessControlException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; @@ -44,9 +42,7 @@ import javax.management.AttributeChangeNotification; import javax.management.InstanceNotFoundException; import javax.management.ListenerNotFoundException; import javax.management.MBeanNotificationInfo; -import javax.management.MBeanPermission; import javax.management.MBeanServerConnection; -import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationEmitter; @@ -118,9 +114,6 @@ public class JMXRemoteNamespace */ private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; - private static final Logger PROBE_LOG = Logger.getLogger( - JmxProperties.NAMESPACE_LOGGER_NAME+".probe"); - // This connection listener is used to listen for connection events from // the underlying JMXConnector. It is used in particular to maintain the @@ -153,8 +146,7 @@ public class JMXRemoteNamespace // because the one that is actually used is the one supplied by the // override of getMBeanServerConnection(). private static class JMXRemoteNamespaceDelegate - extends MBeanServerConnectionWrapper - implements DynamicProbe { + extends MBeanServerConnectionWrapper { private volatile JMXRemoteNamespace parent=null; JMXRemoteNamespaceDelegate() { @@ -180,9 +172,6 @@ public class JMXRemoteNamespace } - public boolean isProbeRequested() { - return this.parent.isProbeRequested(); - } } private static final MBeanNotificationInfo connectNotification = @@ -201,7 +190,6 @@ public class JMXRemoteNamespace private volatile MBeanServerConnection server = null; private volatile JMXConnector conn = null; private volatile ClassLoader defaultClassLoader = null; - private volatile boolean probed; /** * Creates a new instance of {@code JMXRemoteNamespace}. @@ -241,9 +229,6 @@ public class JMXRemoteNamespace // handles (dis)connection events this.listener = new ConnectionListener(); - - // XXX TODO: remove the probe, or simplify it. - this.probed = false; } /** @@ -274,10 +259,6 @@ public class JMXRemoteNamespace return optionsMap; } - boolean isProbeRequested() { - return probed==false; - } - public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { broadcaster.addNotificationListener(listener, filter, handback); @@ -603,26 +584,7 @@ public class JMXRemoteNamespace } public void connect() throws IOException { - if (conn != null) { - try { - // This is much too fragile. It must go away! - PROBE_LOG.finest("Probing again..."); - triggerProbe(getMBeanServerConnection()); - } catch(Exception x) { - close(); - Throwable cause = x; - // if the cause is a security exception - rethrows it... - while (cause != null) { - if (cause instanceof SecurityException) - throw (SecurityException) cause; - cause = cause.getCause(); - } - throw new IOException("connection failed: cycle?",x); - } - } LOG.fine("connecting..."); - // TODO remove these traces - // System.err.println(getInitParameter()+" connecting"); final Map env = new HashMap(getEnvMap()); try { @@ -652,79 +614,9 @@ public class JMXRemoteNamespace throw x; } - - // XXX Revisit here - // Note from the author: This business of switching connection is - // incredibly complex. Isn't there any means to simplify it? - // switchConnection(conn,aconn,msc); - try { - triggerProbe(msc); - } catch(Exception x) { - close(); - Throwable cause = x; - // if the cause is a security exception - rethrows it... - while (cause != null) { - if (cause instanceof SecurityException) - throw (SecurityException) cause; - cause = cause.getCause(); - } - throw new IOException("connection failed: cycle?",x); - } - LOG.fine("connected."); - } - // If this is a self-linking namespace, this method should trigger - // the emission of a probe in the wrapping NamespaceInterceptor. - // The first call to source() in the wrapping NamespaceInterceptor - // causes the emission of the probe. - // - // Note: the MBeanServer returned by getSourceServer - // (our private JMXRemoteNamespaceDelegate inner class) - // implements a sun private interface (DynamicProbe) which is - // used by the NamespaceInterceptor to determine whether it should - // send a probe or not. - // We needed this interface here because the NamespaceInterceptor - // has otherwise no means to knows that this object has just - // connected, and that a new probe should be sent. - // - // Probes work this way: the NamespaceInterceptor sets a flag and sends - // a queryNames() request. If a queryNames() request comes in when the flag - // is on, then it deduces that there is a self-linking loop - and instead - // of calling queryNames() on the JMXNamespace (which would cause the - // loop to go on) it breaks the recursion by returning the probe ObjectName. - // If the NamespaceInterceptor receives the probe ObjectName as result of - // its original queryNames() it knows that it has been looping back on - // itslef and throws an Exception - which will be raised through this - // method, thus preventing the connection to be established... - // - // More info in the com.sun.jmx.namespace.NamespaceInterceptor class - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - // - private void triggerProbe(final MBeanServerConnection msc) - throws MalformedObjectNameException, IOException { - // Query Pattern that we will send through the source server in order - // to detect self-linking namespaces. - // - // - final ObjectName pattern; - pattern = ObjectName.getInstance("*" + - JMXNamespaces.NAMESPACE_SEPARATOR + ":" + - JMXNamespace.TYPE_ASSIGNMENT); - probed = false; - try { - msc.queryNames(pattern, null); - probed = true; - } catch (AccessControlException x) { - // if we have an MBeanPermission missing then do nothing... - if (!(x.getPermission() instanceof MBeanPermission)) - throw x; - PROBE_LOG.finer("Can't check for cycles: " + x); - probed = false; // no need to do it again... - } + LOG.fine("connected."); } public void close() throws IOException { diff --git a/test/javax/management/namespace/JMXNamespaceTest.java b/test/javax/management/namespace/JMXNamespaceTest.java index 1c2ffac9d..a35377112 100644 --- a/test/javax/management/namespace/JMXNamespaceTest.java +++ b/test/javax/management/namespace/JMXNamespaceTest.java @@ -35,7 +35,6 @@ * NamespaceController.java NamespaceControllerMBean.java * @run main/othervm JMXNamespaceTest */ -import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.reflect.InvocationTargetException; @@ -52,10 +51,10 @@ import javax.management.InvalidAttributeValueException; import javax.management.JMX; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; import javax.management.NotificationEmitter; import javax.management.ObjectInstance; import javax.management.ObjectName; -import javax.management.RuntimeOperationsException; import javax.management.StandardMBean; import javax.management.namespace.JMXNamespaces; import javax.management.namespace.JMXNamespace; @@ -155,7 +154,7 @@ public class JMXNamespaceTest { } } - private static class SimpleTestConf { + public static class SimpleTestConf { public final Wombat wombat; public final StandardMBean mbean; public final String dirname; @@ -457,259 +456,56 @@ public class JMXNamespaceTest { } } - /** - * Test cycle detection. - * mkdir test ; cd test ; ln -s . kanga ; ln -s kanga/kanga/roo/kanga roo - * touch kanga/roo/wombat - **/ - public static void probeKangaRooTest(String[] args) { - final SimpleTestConf conf; + public static void verySimpleTest(String[] args) { + System.err.println("verySimpleTest: starting"); try { - conf = new SimpleTestConf(args); - try { - final JMXServiceURL url = - new JMXServiceURL("rmi","localHost",0); - final Map empty = Collections.emptyMap(); - final JMXConnectorServer server = - JMXConnectorServerFactory.newJMXConnectorServer(url, - empty,conf.server); - server.start(); - final JMXServiceURL address = server.getAddress(); - final JMXConnector client = - JMXConnectorFactory.connect(address, - empty); - final String[] signature = { - JMXServiceURL.class.getName(), - Map.class.getName(), - }; - - final Object[] params = { - address, - null, - }; - final MBeanServerConnection c = - client.getMBeanServerConnection(); - - // ln -s . kanga - final ObjectName dirName1 = - new ObjectName("kanga//:type=JMXNamespace"); - c.createMBean(JMXRemoteTargetNamespace.class.getName(), - dirName1, params,signature); - c.invoke(dirName1, "connect", null, null); - try { - // ln -s kanga//kanga//roo//kanga roo - final JMXNamespace local = new JMXNamespace( - new MBeanServerConnectionWrapper(null, - JMXNamespaceTest.class.getClassLoader()){ - - @Override - protected MBeanServerConnection getMBeanServerConnection() { - return JMXNamespaces.narrowToNamespace(c, - "kanga//kanga//roo//kanga" - ); - } - - }); - final ObjectName dirName2 = - new ObjectName("roo//:type=JMXNamespace"); - conf.server.registerMBean(local,dirName2); - System.out.println(dirName2 + " created!"); - try { - // touch kanga/roo/wombat - final ObjectName wombatName1 = - new ObjectName("kanga//roo//"+conf.wombatName); - final WombatMBean wombat1 = - JMX.newMBeanProxy(c,wombatName1,WombatMBean.class); - final String newCaption="I am still the same old wombat"; - Exception x = null; - try { - wombat1.setCaption(newCaption); - } catch (RuntimeOperationsException r) { - x=r.getTargetException(); - System.out.println("Got expected exception: " + x); - // r.printStackTrace(); - } - if (x == null) - throw new RuntimeException("cycle not detected!"); - } finally { - c.unregisterMBean(dirName2); - } - } finally { - c.unregisterMBean(dirName1); - client.close(); - server.stop(); - } - } finally { - conf.close(); - } - System.err.println("probeKangaRooTest PASSED"); + final MBeanServer srv = MBeanServerFactory.createMBeanServer(); + srv.registerMBean(new JMXNamespace( + JMXNamespaces.narrowToNamespace(srv, "foo")), + JMXNamespaces.getNamespaceObjectName("foo")); + throw new Exception("Excpected IllegalArgumentException not raised."); + } catch (IllegalArgumentException x) { + System.err.println("verySimpleTest: got expected exception: "+x); } catch (Exception x) { - System.err.println("probeKangaRooTest FAILED: " +x); + System.err.println("verySimpleTest FAILED: " +x); x.printStackTrace(); throw new RuntimeException(x); } + System.err.println("verySimpleTest: PASSED"); } - /** - * Test cycle detection 2. - * mkdir test ; cd test ; ln -s . roo ; ln -s roo/roo kanga - * touch kanga/roo/wombat ; rm roo ; ln -s kanga roo ; - * touch kanga/roo/wombat - * - **/ - public static void probeKangaRooCycleTest(String[] args) { - final SimpleTestConf conf; - try { - conf = new SimpleTestConf(args); - Exception failed = null; - try { - final JMXServiceURL url = - new JMXServiceURL("rmi","localHost",0); - final Map empty = Collections.emptyMap(); - final JMXConnectorServer server = - JMXConnectorServerFactory.newJMXConnectorServer(url, - empty,conf.server); - server.start(); - final JMXServiceURL address = server.getAddress(); - final JMXConnector client = - JMXConnectorFactory.connect(address, - empty); - final String[] signature = { - JMXServiceURL.class.getName(), - Map.class.getName(), - }; - final String[] signature2 = { - JMXServiceURL.class.getName(), - Map.class.getName(), - String.class.getName() - }; - final Object[] params = { - address, - Collections.emptyMap(), - }; - final Object[] params2 = { - address, - null, - "kanga", - }; - final MBeanServerConnection c = - client.getMBeanServerConnection(); - // ln -s . roo - final ObjectName dirName1 = - new ObjectName("roo//:type=JMXNamespace"); - c.createMBean(JMXRemoteTargetNamespace.class.getName(), - dirName1, params,signature); - c.invoke(dirName1, "connect",null,null); - try { - final Map emptyMap = - Collections.emptyMap(); - final JMXNamespace local = new JMXNamespace( - new MBeanServerConnectionWrapper( - JMXNamespaces.narrowToNamespace(c, - "roo//roo//"), - JMXNamespaceTest.class.getClassLoader())) { - }; - // ln -s roo/roo kanga - final ObjectName dirName2 = - new ObjectName("kanga//:type=JMXNamespace"); - conf.server.registerMBean(local,dirName2); - System.out.println(dirName2 + " created!"); - try { - // touch kanga/roo/wombat - final ObjectName wombatName1 = - new ObjectName("kanga//roo//"+conf.wombatName); - final WombatMBean wombat1 = - JMX.newMBeanProxy(c,wombatName1,WombatMBean.class); - final String newCaption="I am still the same old wombat"; - wombat1.setCaption(newCaption); - // rm roo - c.unregisterMBean(dirName1); - // ln -s kanga roo - System.err.println("**** Creating " + dirName1 + - " ****"); - c.createMBean(JMXRemoteTargetNamespace.class.getName(), - dirName1, params2,signature2); - System.err.println("**** Created " + dirName1 + - " ****"); - Exception x = null; - try { - // touch kanga/roo/wombat - wombat1.setCaption(newCaption+" I hope"); - } catch (RuntimeOperationsException r) { - x=(Exception)r.getCause(); - System.out.println("Got expected exception: " + x); - //r.printStackTrace(); - } - if (x == null) - throw new RuntimeException("should have failed!"); - x = null; - try { - // ls kanga/roo/wombat - System.err.println("**** Connecting " + dirName1 + - " ****"); - JMX.newMBeanProxy(c,dirName1, - JMXRemoteNamespaceMBean.class).connect(); - System.err.println("**** Connected " + dirName1 + - " ****"); - } catch (IOException r) { - x=r; - System.out.println("Got expected exception: " + x); - //r.printStackTrace(); - } - System.err.println("**** Expected Exception Not Raised ****"); - if (x == null) { - System.out.println(dirName1+" contains: "+ - c.queryNames(new ObjectName( - dirName1.getDomain()+"*:*"),null)); - throw new RuntimeException("cycle not detected!"); - } - } catch (Exception t) { - if (failed == null) failed = t; - } finally { - c.unregisterMBean(dirName2); - } - } finally { - try { - c.unregisterMBean(dirName1); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to unregister "+dirName1+ - ": "+t); - } - try { - client.close(); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to close client: "+t); - } - try { - server.stop(); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to stop server: "+t); - } - } - } finally { - try { - conf.close(); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to stop server: "+t); - } - } - if (failed != null) throw failed; - System.err.println("probeKangaRooCycleTest PASSED"); + public static void verySimpleTest2(String[] args) { + System.err.println("verySimpleTest2: starting"); + try { + final MBeanServer srv = MBeanServerFactory.createMBeanServer(); + final JMXConnectorServer cs = JMXConnectorServerFactory. + newJMXConnectorServer(new JMXServiceURL("rmi",null,0), + null, srv); + cs.start(); + final JMXConnector cc = JMXConnectorFactory.connect(cs.getAddress()); + + srv.registerMBean(new JMXNamespace( + new MBeanServerConnectionWrapper( + JMXNamespaces.narrowToNamespace( + cc.getMBeanServerConnection(), + "foo"))), + JMXNamespaces.getNamespaceObjectName("foo")); + throw new Exception("Excpected IllegalArgumentException not raised."); + } catch (IllegalArgumentException x) { + System.err.println("verySimpleTest2: got expected exception: "+x); } catch (Exception x) { - System.err.println("probeKangaRooCycleTest FAILED: " +x); + System.err.println("verySimpleTest2 FAILED: " +x); x.printStackTrace(); throw new RuntimeException(x); } + System.err.println("verySimpleTest2: PASSED"); } + public static void main(String[] args) { simpleTest(args); recursiveTest(args); - probeKangaRooTest(args); - probeKangaRooCycleTest(args); + verySimpleTest(args); + verySimpleTest2(args); } } -- cgit v1.2.3