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
/*
* 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 JMXNamespaceViewTest.java
* @summary Test the JMXNamespaceView class.
* @bug 5072476
* @author Daniel Fuchs
* @run clean JMXNamespaceViewTest Wombat WombatMBean
* @run build JMXNamespaceViewTest Wombat WombatMBean
* @run main JMXNamespaceViewTest
*/
import java.lang.management.ManagementFactory;
import java.net.ServerSocket;
import java.rmi.registry.LocateRegistry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.JMException;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.namespace.JMXNamespace;
import javax.management.namespace.JMXNamespaceView;
import javax.management.namespace.JMXNamespaces;
import javax.management.namespace.JMXRemoteNamespace;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
/**
* A simple test to test the JMXNamespaceViewTest...
* @author dfuchs
*/
public class JMXNamespaceViewTest {
// TODO: Remove this when contexts are added.
public static class ClientContext {
public final static String NAMESPACE = "jmx.context";
}
/**
* Describe the configuration of a namespace
*/
public static class NamespaceConfig {
/** name of the namespace - no // allowed **/
public String name;
/**
* JMXServiceURL through which the namespace is exported, if it
* is a remote namespace. {@code null} if the namespace is local.
* This is an inpur URL - eg: new JMXServiceURL("rmi",null,0).toString()
* is acceptable here.
*/
public String jmxurl;
/**
* Values of the name= key for each WombatMBean contained in the
* namespace.
*/
public String[] wombats;
/** list of child namespace **/
public NamespaceConfig[] children;
}
/**
* Creates a NamespaceConfig record for a local namespace.
* @param name name of the namespace
* @param wombats names of WombatMBean it should contain.
* @return a NamespaceConfig.
*/
public static NamespaceConfig config(String name, String[] wombats) {
return config(name,null,wombats);
}
/**
* Creates a NamespaceConfig record for a remote namespace.
* @param name name of the namespace
* @param jmxurl input JMXServiceURL for creating the JMXConnectorServer
* @param wombats names of WombatMBean it should contain.
* @return a NamespaceConfig.
*/
public static NamespaceConfig config(String name, String jmxurl,
String[] wombats) {
return config(name,jmxurl,wombats,(NamespaceConfig[])null);
}
/**
* Creates a NamespaceConfig record for a local namespace.
* @param name name of the namespace
* @param wombats names of WombatMBean it should contain.
* @param children list of sub namespaces.
* @return a NamespaceConfig.
*/
public static NamespaceConfig config(String name, String[] wombats,
NamespaceConfig... children) {
return config(name,null,wombats,children);
}
/**
* Creates a NamespaceConfig record for a remote namespace.
* @param name name of the namespace
* @param jmxurl input JMXServiceURL for creating the JMXConnectorServer
* @param wombats names of WombatMBean it should contain.
* @param children list of sub namespaces.
* @return a NamespaceConfig.
*/
static NamespaceConfig config(String name, String jmxurl, String[] wombats,
NamespaceConfig... children) {
final NamespaceConfig cfg = new NamespaceConfig();
cfg.name=name; cfg.jmxurl=jmxurl; cfg.wombats=wombats;
cfg.children=children;
return cfg;
}
/**
* Returns the given names. This is a utility method to ease code
* reading.
* @param names names of Wombat MBeans.
* @return the given names.
*/
static String[] wombats(String... names) {
return names;
}
/**
* Creates a JMXServiceURL string for the given protocol.
* This is also a utility method to ease code reading.
* @param protocol The protocol name (e.g. "rmi")
* @return A JMXServiceURL string.
* @throws Exception if creation of the JMXServiceURL fails.
*/
static String url(String protocol) throws Exception {
return new JMXServiceURL(protocol,null,0).toString();
}
/**
* Creates a config for a hierarchy of namespaces, mixing local namespaces
* and remote namespaces using the given protocol.
* @param protocol The protocol that should be used for remote namespaces.
* @return A namespace config hierarchy.
* @throws java.lang.Exception
*/
public static NamespaceConfig[] makeConfig(String protocol)
throws Exception {
final NamespaceConfig[] config = {
// Top level namespace "top1" (local)
config("top1",wombats("wchief","w1","w2","w3"),
// top1//local1
config("local1",wombats("wchief","ww1","ww2")),
// top1//local2
config("local2",wombats("wchief","ww4","ww5","ww6"),
// top1//local2//local3
config("local3",wombats("wchief","www1","www2")),
// top1//local2//rmi1
config("rmi1",url(protocol),wombats("wchief","www3","www4","www5"))),
// top1//rmi2
config("rmi2",url(protocol),wombats("wchief","ww7","ww8","ww9"),
// top1//rmi2//local4
config("local4",wombats("wchief","www6","www7")),
// top1//rmi2//rmi3
config("rmi3",url(protocol),wombats("wchief","www3","www4","www5"),
// top1//rmi2//rmi3//local5
config("local5",wombats("wchief","wwww1"))))),
// Top level namespace "top2" (local)
config("top2",wombats("wchief","w21","w22","w23"),
// top2//local21
config("local21",wombats("wchief","ww21","ww22")),
// top2//rmi22
config("rmi22",url(protocol),wombats("wchief","ww27","ww28","ww29"),
// top2//rmi22//local24
config("local24",wombats("wchief","www26","www27")),
// top2//rmi22//rmi23
config("rmi23",url(protocol),wombats("wchief","www23","www24","www25"),
// top2//rmi22//rmi23//local25
config("local25",wombats("wchief","wwww21"))))),
// Top level namespace "top3" (remote)
config("top3",url(protocol),wombats("wchief","w31","w32","w33"),
// top3//local31
config("local31",wombats("wchief","ww31","ww32")),
// top3//rmi32
config("rmi32",url(protocol),wombats("wchief","ww37","ww38","ww39"),
// top3//rmi32//local34
config("local34",wombats("wchief","www36","www37")),
// top3//rmi32//rmi33
config("rmi33",url(protocol),wombats("wchief","www33","www34","www35"),
// top3//rmi32//local35
config("local35",wombats("wchief","wwww31"))))),
};
return config;
}
/**
* Close all connector servers in the list.
* @param cslist List of connector servers to close.
*/
public static void closeAll(List<JMXConnectorServer> cslist) {
for (JMXConnectorServer cs : cslist) {
try {
cs.stop();
} catch (Exception xx) {
System.err.println("Failed to stop connector: " + xx);
}
}
}
public static class MBeanServerConfigCreator {
public MBeanServer createMBeanServerFor(NamespaceConfig config) {
return MBeanServerFactory.newMBeanServer();
}
}
/**
* Load the given namespace configuration inside the given MBeanServer.
* Return a list of connector servers created in the process.
* @param server The MBeanServer in which the namespaces must
* be created.
* @param namespaces The list of namespaces to create.
* @return a list of started connector servers.
* @throws java.lang.Exception failed to create the specified namespaces.
*/
public static List<JMXConnectorServer> load(MBeanServer server,
MBeanServerConfigCreator factory,
NamespaceConfig... namespaces) throws Exception {
final List<JMXConnectorServer> cslist =
new ArrayList<JMXConnectorServer>();
try {
final ObjectName creator =
new ObjectName("jmx.creator:type=JMXNamespaceCreator");
if (System.getProperty("jmx.wait")!=null
&& !server.isRegistered(creator)) {
server.registerMBean(new JMXNamespaceCreator(),creator);
}
for (NamespaceConfig cfg : namespaces) {
final MBeanServer srv = factory.createMBeanServerFor(cfg);
if (System.getProperty("jmx.wait")!=null
&& !srv.isRegistered(creator)) {
srv.registerMBean(new JMXNamespaceCreator(),creator);
}
if (cfg.wombats != null) {
for (String w : cfg.wombats) {
final ObjectName n =
new ObjectName("wombat:type=Wombat,name=" + w);
final WombatMBean ww = new Wombat();
srv.registerMBean(ww, n);
}
}
if (cfg.children != null) {
cslist.addAll(load(srv, factory, cfg.children));
}
JMXNamespace nm;
if (cfg.jmxurl == null) {
nm = new JMXNamespace(srv);
} else {
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL(cfg.jmxurl),
null, srv);
srv.registerMBean(cs,
new ObjectName("jmx.remote:type=JMXConnectorServer"));
cs.start();
cslist.add(cs);
nm = JMXRemoteNamespace.
newJMXRemoteNamespace(cs.getAddress(),
null);
}
server.registerMBean(nm,
JMXNamespaces.getNamespaceObjectName(cfg.name));
if (nm instanceof JMXRemoteNamespace) {
server.invoke(
JMXNamespaces.getNamespaceObjectName(cfg.name),
"connect", null, null);
}
}
} catch (Exception x) {
closeAll(cslist);
throw x;
}
return cslist;
}
/**
* Add an entry {@code <path,NamespaceConfig>} in the map for the given
* namespace and its subnamespaces.
* @param map A {@code Map<path,NamespaceConfig>}.
* @param parent The path of the parent workspace.
* @param cfg The NamespaceConfig hierarchy to index in the map.
*/
public static void fillMap(Map<String,NamespaceConfig> map, String parent,
NamespaceConfig cfg) {
final String where;
if (parent == null || parent.equals(""))
where=cfg.name;
else
where=parent+JMXNamespaces.NAMESPACE_SEPARATOR+cfg.name;
map.put(where,cfg);
if (cfg.children==null) return;
for(NamespaceConfig child:cfg.children) {
fillMap(map,where,child);
}
}
/**
* Compare a list of namespace names obtained from JMXNamespaceView.list()
* with the expected clildren list of the corresponding NamespaceConfig.
* @param list A list of namespace names
* @param children A list of NamespaceConfig correspondng to expected
* namespace.
* @param fail If true and the comparison yields false, throws an
* exception instead of simply returning false.
* @return true if OK, false if NOK.
*/
private static boolean compare(String[] list, NamespaceConfig[] children,
boolean fail) {
final List<String> found = new ArrayList<String>(Arrays.asList(list));
if (found.contains(ClientContext.NAMESPACE))
found.remove(ClientContext.NAMESPACE);
if (children == null && found.size()==0) return true;
if (children == null && fail == false) return false;
if (children == null) throw new RuntimeException(
"No child expected. Found "+Arrays.toString(list));
final Set<String> names = new HashSet<String>();
for (NamespaceConfig cfg : children) {
names.add(cfg.name);
if (found.contains(cfg.name)) continue;
if (!fail) return false;
throw new RuntimeException(cfg.name+" not found in "+
found);
}
found.removeAll(names);
if (found.size()==0) return true;
if (fail==false) return false;
throw new RuntimeException("found additional namespaces: "+
found);
}
/**
* Compares the result of queryNames(null,null) with a set of expected
* wombats.
* @param where The path of the namespace that was queried.
* @param list The set of ObjectNames found.
* @param wombats The expected list of wombats.
* @param fail If true and the comparison yields false, throws an
* exception instead of simply returning false.
* @return true if OK, false if NOK.
* @throws java.lang.Exception something went wrong.
*/
private static boolean compare(String where,
Set<ObjectName>list, String[] wombats,
boolean fail) throws Exception {
final Set<ObjectName> found = new HashSet<ObjectName>();
final Set<ObjectName> expected = new HashSet<ObjectName>();
for (ObjectName n : list) {
if ("Wombat".equals(n.getKeyProperty("type")))
found.add(n);
}
for(String w : wombats) {
final ObjectName n =
new ObjectName("wombat:type=Wombat,name=" + w);
expected.add(n);
if (found.contains(n)) continue;
if (fail == false) return false;
throw new RuntimeException(where+
": Wombat "+w+" not found in "+found);
}
found.removeAll(expected);
if (found.size()==0) {
System.out.println(where+": found all expected: "+expected);
return true;
}
if (fail==false) return false;
throw new RuntimeException(where+": found additional MBeans: "+
found);
}
/**
* A generic test to test JMXNamespaceView over a namespace configuration.
* @param server The MBeanServer in which to load the namespace
* config.
* @param namespaces The namespace config to run the test over...
* @throws java.lang.Exception
*/
public static void doTest(MBeanServer server, NamespaceConfig... namespaces)
throws Exception {
List<JMXConnectorServer> cslist = load(server,
new MBeanServerConfigCreator(), namespaces);
Map<String,NamespaceConfig> inputMap =
new HashMap<String,NamespaceConfig>();
for (NamespaceConfig cfg : namespaces) {
fillMap(inputMap,"",cfg);
}
try {
final JMXNamespaceView root = new JMXNamespaceView(server);
List<JMXNamespaceView> vlist = new ArrayList<JMXNamespaceView>();
vlist.add(root);
while (!vlist.isEmpty()) {
JMXNamespaceView v = vlist.remove(0);
final String where = v.isRoot()?"root":v.where();
System.out.println(where+": "+
v.getMBeanServerConnection().queryNames(null,null));
for (String ns : v.list()) {
final JMXNamespaceView down = v.down(ns);
vlist.add(down);
if (!down.where().equals(v.isRoot()?ns:where+
JMXNamespaces.NAMESPACE_SEPARATOR+ns)) {
throw new RuntimeException("path of "+down.where()+
" should be "+(v.isRoot()?ns:where+
JMXNamespaces.NAMESPACE_SEPARATOR+ns));
}
if (down.up().equals(v)) continue;
throw new RuntimeException("parent of "+down.where()+
" should be "+where);
}
final NamespaceConfig[] children;
final NamespaceConfig cfg;
if (v.isRoot()) {
children=namespaces;
cfg = null;
} else {
cfg = inputMap.get(where);
children = cfg==null?null:cfg.children;
}
compare(v.list(),children,true);
if (!v.isRoot()) {
if (where.endsWith(ClientContext.NAMESPACE)) {
System.out.println(where+": skipping queryNames analysis");
continue;
}
//System.out.println(where+": cfg is: "+cfg);
compare(where,v.getMBeanServerConnection().
queryNames(null, null),cfg.wombats,true);
}
}
exportAndWaitIfNeeded(server);
} finally {
closeAll(cslist);
}
}
public static interface JMXNamespaceCreatorMBean {
public ObjectInstance createLocalNamespace(String namespace)
throws JMException ;
public void removeLocalNamespace(String namespace)
throws JMException;
}
public static class JMXNamespaceCreator
implements MBeanRegistration,
JMXNamespaceCreatorMBean {
private volatile MBeanServer mbeanServer;
public ObjectInstance createLocalNamespace(String namespace)
throws JMException {
return mbeanServer.registerMBean(
new JMXNamespace(MBeanServerFactory.newMBeanServer()),
JMXNamespaces.getNamespaceObjectName(namespace));
}
public void removeLocalNamespace(String namespace)
throws JMException {
mbeanServer.unregisterMBean(
JMXNamespaces.getNamespaceObjectName(namespace));
}
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception {
mbeanServer = server;
return name;
}
public void postRegister(Boolean registrationDone) {
}
public void preDeregister() throws Exception {
}
public void postDeregister() {
}
}
public static void exportAndWaitIfNeeded(MBeanServer server)
throws Exception {
if (System.getProperty("jmx.wait")!=null) {
final int port = getPortFor("rmi");
LocateRegistry.createRegistry(port);
final JMXServiceURL url =
new JMXServiceURL("rmi",null,port,
"/jndi/rmi://localhost:"+port+"/jmxrmi");
final JMXConnectorServer cs =
JMXConnectorServerFactory.
newJMXConnectorServer(url, null, server);
cs.start();
try {
System.out.println("RMI Server waiting at: "+cs.getAddress());
System.in.read();
} finally {
cs.stop();
}
}
}
public static int getPortFor(String protocol) throws Exception {
final int aport =
Integer.valueOf(System.getProperty("jmx."+protocol+".port","0"));
if (aport > 0) return aport;
final ServerSocket s = new ServerSocket(0);
try {
final int port = s.getLocalPort();
return port;
} finally {
s.close();
}
}
public static void main(String[] args) throws Exception {
doTest(ManagementFactory.getPlatformMBeanServer(),makeConfig("rmi"));
}
}