/* * Copyright 2007-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 RemoteContextTest.java * @bug 5072267 * @summary Test client contexts with namespaces. * @author Eamonn McManus, Daniel Fuchs */ import java.lang.management.ManagementFactory; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.URLEncoder; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.Callable; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.ClientContext; import javax.management.DynamicMBean; import javax.management.JMX; import javax.management.ListenerNotFoundException; import javax.management.MBeanNotificationInfo; import javax.management.MBeanRegistration; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MBeanServerDelegate; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.StandardMBean; import javax.management.loading.MLet; import javax.management.namespace.JMXNamespaces; import javax.management.namespace.JMXRemoteNamespace; import javax.management.namespace.JMXNamespace; import static java.util.Collections.singletonMap; import javax.management.MBeanServerFactory; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; public class RemoteContextTest { private static String failure; public static interface ShowContextMBean { public Map getContext(); public Map getCreationContext(); public Set getCalledOps(); public String getThing(); public void setThing(String x); public int add(int x, int y); } public static class ShowContext extends NotificationBroadcasterSupport implements ShowContextMBean, MBeanRegistration { private final Map creationContext; private final Set calledOps = new HashSet(); public ShowContext() { creationContext = getContext(); } public Map getContext() { return ClientContext.getContext(); } public Map getCreationContext() { return creationContext; } public Set getCalledOps() { return calledOps; } public String getThing() { return "x"; } public void setThing(String x) { } public int add(int x, int y) { return x + y; } public ObjectName preRegister(MBeanServer server, ObjectName name) { assertEquals(creationContext, getContext()); calledOps.add("preRegister"); return name; } public void postRegister(Boolean registrationDone) { assertEquals(creationContext, getContext()); calledOps.add("postRegister"); } // The condition checked here is not guaranteed universally true, // but is true every time we unregister an instance of this MBean // in this test. public void preDeregister() throws Exception { assertEquals(creationContext, getContext()); } public void postDeregister() { assertEquals(creationContext, getContext()); } // Same remark as for preDeregister @Override public MBeanNotificationInfo[] getNotificationInfo() { calledOps.add("getNotificationInfo"); return super.getNotificationInfo(); } @Override public void addNotificationListener( NotificationListener listener, NotificationFilter filter, Object handback) { calledOps.add("addNotificationListener"); super.addNotificationListener(listener, filter, handback); } @Override public void removeNotificationListener( NotificationListener listener) throws ListenerNotFoundException { calledOps.add("removeNL1"); super.removeNotificationListener(listener); } @Override public void removeNotificationListener( NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { calledOps.add("removeNL3"); super.removeNotificationListener(listener, filter, handback); } } private static class LogRecord { final String op; final Object[] params; final Map context; LogRecord(String op, Object[] params, Map context) { this.op = op; this.params = params; this.context = context; } @Override public String toString() { return op + Arrays.deepToString(params) + " " + context; } } private static class LogIH implements InvocationHandler { private final Object wrapped; Queue log = new LinkedList(); LogIH(Object wrapped) { this.wrapped = wrapped; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getDeclaringClass() != Object.class) { LogRecord lr = new LogRecord( method.getName(), args, ClientContext.getContext()); log.add(lr); } try { return method.invoke(wrapped, args); } catch (InvocationTargetException e) { throw e.getCause(); } } } private static T newSnoop(Class wrappedClass, LogIH logIH) { return wrappedClass.cast(Proxy.newProxyInstance( wrappedClass.getClassLoader(), new Class[] {wrappedClass}, logIH)); } public static void main(String[] args) throws Exception { final String subnamespace = "sub"; final ObjectName locname = new ObjectName("a:b=c"); final ObjectName name = JMXNamespaces.insertPath(subnamespace,locname); final MBeanServer mbs = ClientContext.newContextForwarder( ManagementFactory.getPlatformMBeanServer(), null); final MBeanServer sub = ClientContext.newContextForwarder( MBeanServerFactory.newMBeanServer(), null); final JMXServiceURL anonym = new JMXServiceURL("rmi",null,0); final Map env = Collections.emptyMap(); final Map emptyContext = Collections.emptyMap(); final JMXConnectorServer srv = JMXConnectorServerFactory.newJMXConnectorServer(anonym,env,sub); sub.registerMBean(new ShowContext(), locname); srv.start(); try { JMXRemoteNamespace subns = JMXRemoteNamespace. newJMXRemoteNamespace(srv.getAddress(),null); mbs.registerMBean(subns, JMXNamespaces.getNamespaceObjectName("sub")); mbs.invoke(JMXNamespaces.getNamespaceObjectName("sub"), "connect", null,null); final ShowContextMBean show = JMX.newMBeanProxy(mbs, name, ShowContextMBean.class); assertEquals(emptyContext, show.getContext()); ClientContext.doWithContext(singletonMap("foo", "bar"), new Callable() { public Void call() { assertEquals(singletonMap("foo", "bar"), show.getContext()); return null; } }); assertEquals(emptyContext, show.getContext()); String got = ClientContext.doWithContext( singletonMap("foo", "baz"), new Callable() { public String call() { return ClientContext.getContext().get("foo"); } }); assertEquals("baz", got); Map combined = ClientContext.doWithContext( singletonMap("foo", "baz"), new Callable>() { public Map call() throws Exception { return ClientContext.doWithContext( singletonMap("fred", "jim"), new Callable>() { public Map call() { return ClientContext.getContext(); } }); } }); assertEquals(singletonMap("fred", "jim"), combined); final String ugh = "a!?//*=:\"% "; ClientContext.doWithContext(singletonMap(ugh, ugh), new Callable() { public Void call() { assertEquals(Collections.singletonMap(ugh, ugh), ClientContext.getContext()); return null; } }); // Basic withContext tests LogIH mbsIH = new LogIH(mbs); MBeanServer snoopMBS = newSnoop(MBeanServer.class, mbsIH); MBeanServer ughMBS = ClientContext.withContext(snoopMBS, ugh, ugh); // ughMBS is never referenced but we check that the withContext call // included a call to snoopMBS.isRegistered. String encodedUgh = URLEncoder.encode(ugh, "UTF-8").replace("*", "%2A"); ObjectName expectedName = new ObjectName( ClientContext.NAMESPACE + ObjectName.NAMESPACE_SEPARATOR + encodedUgh + "=" + encodedUgh + ObjectName.NAMESPACE_SEPARATOR + ":" + JMXNamespace.TYPE_ASSIGNMENT); assertCalled(mbsIH, "isRegistered", new Object[] {expectedName}, emptyContext); // Test withDynamicContext MBeanServerConnection dynamicSnoop = ClientContext.withDynamicContext(snoopMBS); assertCalled(mbsIH, "isRegistered", new Object[] { JMXNamespaces.getNamespaceObjectName(ClientContext.NAMESPACE) }, emptyContext); final ShowContextMBean dynamicShow = JMX.newMBeanProxy(dynamicSnoop, name, ShowContextMBean.class); assertEquals(Collections.emptyMap(), dynamicShow.getContext()); assertCalled(mbsIH, "getAttribute", new Object[] {name, "Context"}, emptyContext); Map expectedDynamic = Collections.singletonMap("gladstone", "gander"); Map dynamic = ClientContext.doWithContext( expectedDynamic, new Callable>() { public Map call() throws Exception { return dynamicShow.getContext(); } }); assertEquals(expectedDynamic, dynamic); ObjectName expectedDynamicName = new ObjectName( ClientContext.encode(expectedDynamic) + ObjectName.NAMESPACE_SEPARATOR + name); assertCalled(mbsIH, "getAttribute", new Object[] {expectedDynamicName, "Context"}, dynamic); MBeanServer cmbs = ClientContext.withContext( mbs, "mickey", "mouse"); ShowContextMBean cshow = JMX.newMBeanProxy(cmbs, name, ShowContextMBean.class); assertEquals(Collections.singletonMap("mickey", "mouse"), cshow.getContext()); MBeanServer ccmbs = ClientContext.withContext( cmbs, "donald", "duck"); ShowContextMBean ccshow = JMX.newMBeanProxy(ccmbs, name, ShowContextMBean.class); Map disney = new HashMap(); disney.put("mickey", "mouse"); disney.put("donald", "duck"); assertEquals(disney, ccshow.getContext()); // Test that all MBS ops produce reasonable results ObjectName logger = new ObjectName("a:type=Logger"); DynamicMBean showMBean = new StandardMBean(new ShowContext(), ShowContextMBean.class); LogIH mbeanLogIH = new LogIH(showMBean); DynamicMBean logMBean = newSnoop(DynamicMBean.class, mbeanLogIH); ObjectInstance loggerOI = ccmbs.registerMBean(logMBean, logger); assertEquals(logger, loggerOI.getObjectName()); // We get a getMBeanInfo call to determine the className in the // ObjectInstance to return from registerMBean. assertCalled(mbeanLogIH, "getMBeanInfo", disney); ccmbs.getAttribute(logger, "Thing"); assertCalled(mbeanLogIH, "getAttribute", disney); ccmbs.getAttributes(logger, new String[] {"Thing", "Context"}); assertCalled(mbeanLogIH, "getAttributes", disney); ccmbs.setAttribute(logger, new Attribute("Thing", "bar")); assertCalled(mbeanLogIH, "setAttribute", disney); ccmbs.setAttributes(logger, new AttributeList( Arrays.asList(new Attribute("Thing", "baz")))); assertCalled(mbeanLogIH, "setAttributes", disney); ccmbs.getMBeanInfo(logger); assertCalled(mbeanLogIH, "getMBeanInfo", disney); Set names = ccmbs.queryNames(null, null); Set expectedNames = new HashSet( Collections.singleton(MBeanServerDelegate.DELEGATE_NAME)); expectedNames.removeAll(names); assertEquals(0, expectedNames.size()); Set nsNames = ccmbs.queryNames(new ObjectName("**?*?//:*"), null); Set expectedNsNames = new HashSet( Arrays.asList( new ObjectName(ClientContext.NAMESPACE + ObjectName.NAMESPACE_SEPARATOR + ":" + JMXNamespace.TYPE_ASSIGNMENT))); expectedNsNames.removeAll(nsNames); assertEquals(0, expectedNsNames.size()); Set insts = ccmbs.queryMBeans( MBeanServerDelegate.DELEGATE_NAME, null); assertEquals(1, insts.size()); assertEquals(MBeanServerDelegate.DELEGATE_NAME, insts.iterator().next().getObjectName()); ObjectName createdName = new ObjectName("a:type=Created"); ObjectInstance createdOI = ccmbs.createMBean(ShowContext.class.getName(), createdName); assertEquals(ShowContext.class.getName(), createdOI.getClassName()); assertEquals(createdName, createdOI.getObjectName()); assertEquals(disney, ccmbs.getAttribute(createdName, "CreationContext")); NotificationListener nothingListener = new NotificationListener() { public void handleNotification(Notification n, Object h) {} }; ccmbs.addNotificationListener(createdName, nothingListener, null, null); ccmbs.removeNotificationListener(createdName, nothingListener, null, null); ccmbs.addNotificationListener(createdName, nothingListener, null, null); ccmbs.removeNotificationListener(createdName, nothingListener); Set expectedOps = new HashSet(Arrays.asList( "preRegister", "postRegister", "addNotificationListener", "removeNL1", "removeNL3", "getNotificationInfo")); assertEquals(expectedOps, ccmbs.getAttribute(createdName, "CalledOps")); assertEquals(ShowContext.class.getClassLoader(), ccmbs.getClassLoaderFor(createdName)); assertEquals(true, ccmbs.isRegistered(createdName)); assertEquals(true, ccmbs.isInstanceOf(createdName, ShowContext.class.getName())); assertEquals(false, ccmbs.isInstanceOf(createdName, DynamicMBean.class.getName())); ccmbs.unregisterMBean(createdName); assertEquals(false, ccmbs.isRegistered(createdName)); MLet mlet = new MLet(); ObjectName defaultMLetName = new ObjectName("DefaultDomain:type=MLet"); ccmbs.registerMBean(mlet, defaultMLetName); assertEquals(mlet, ccmbs.getClassLoader(defaultMLetName)); assertEquals(0, mbeanLogIH.log.size()); // Test that contexts still work when we can't combine two encoded contexts. // Here, we wrap cmbs (mickey=mouse) so that ccmbs2 (donald=duck) cannot // see that it already contains a context and therefore cannot combine // into mickey=mouse;donald=duck. We don't actually use the snoop // capabilities of the returned object -- we just want an opaque // MBeanServer wrapper MBeanServer cmbs2 = newSnoop(MBeanServer.class, new LogIH(cmbs)); MBeanServer ccmbs2 = ClientContext.withContext(cmbs2, "donald", "duck"); assertEquals(disney, ccmbs2.getAttribute(name, "Context")); // ADD NEW TESTS HERE ^^^ if (failure != null) throw new Exception(failure); } finally { srv.stop(); } } 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 assertCalled( LogIH logIH, String op, Map expectedContext) { assertCalled(logIH, op, null, expectedContext); } private static void assertCalled( LogIH logIH, String op, Object[] params, Map expectedContext) { LogRecord lr = logIH.log.remove(); assertEquals(op, lr.op); if (params != null) assertEquals(params, lr.params); assertEquals(expectedContext, lr.context); } private static void failed(String why) { failure = why; new Throwable("FAILED: " + why).printStackTrace(System.out); } }