提交 ca1683b0 编写于 作者: D dfuchs

6747983: jmx namespace: unspecified self-link detection logic

Reviewed-by: emcmanus
上级 672a5836
......@@ -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<JMXNamespace> {
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<JMXNamespace> {
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<JMXNamespace> {
", 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<ObjectName> 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<JMXNamespace> {
* (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<JMXNamespace> {
return super.source();
}
/**
* Calls {@link MBeanServerConnection#queryNames queryNames}
* on the underlying
* {@link #getMBeanServerConnection MBeanServerConnection}.
**/
@Override
public final Set<ObjectName> 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 {
......
......@@ -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<String,Object> env =
new HashMap<String,Object>(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 {
......
......@@ -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<String,Object> 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<String,Object> 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<String,Object> 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);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册