jdk/test/javax/management/context/RemoteContextTest.java
changeset 4159 9e3aae7675f1
parent 4158 0b4d21bc8b5c
parent 4156 acaa49a2768a
child 4160 bda0a85afcb7
equal deleted inserted replaced
4158:0b4d21bc8b5c 4159:9e3aae7675f1
     1 /*
       
     2  * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    20  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    21  * have any questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test RemoteContextTest.java
       
    26  * @bug 5072267
       
    27  * @summary Test client contexts with namespaces.
       
    28  * @author Eamonn McManus, Daniel Fuchs
       
    29  */
       
    30 
       
    31 import java.lang.management.ManagementFactory;
       
    32 import java.lang.reflect.InvocationHandler;
       
    33 import java.lang.reflect.InvocationTargetException;
       
    34 import java.lang.reflect.Method;
       
    35 import java.lang.reflect.Proxy;
       
    36 import java.net.URLEncoder;
       
    37 import java.util.Arrays;
       
    38 import java.util.Collections;
       
    39 import java.util.HashMap;
       
    40 import java.util.HashSet;
       
    41 import java.util.LinkedList;
       
    42 import java.util.Map;
       
    43 import java.util.Queue;
       
    44 import java.util.Set;
       
    45 import java.util.concurrent.Callable;
       
    46 import javax.management.Attribute;
       
    47 import javax.management.AttributeList;
       
    48 import javax.management.ClientContext;
       
    49 import javax.management.DynamicMBean;
       
    50 import javax.management.JMX;
       
    51 import javax.management.ListenerNotFoundException;
       
    52 import javax.management.MBeanNotificationInfo;
       
    53 import javax.management.MBeanRegistration;
       
    54 import javax.management.MBeanServer;
       
    55 import javax.management.MBeanServerConnection;
       
    56 import javax.management.MBeanServerDelegate;
       
    57 import javax.management.Notification;
       
    58 import javax.management.NotificationBroadcasterSupport;
       
    59 import javax.management.NotificationFilter;
       
    60 import javax.management.NotificationListener;
       
    61 import javax.management.ObjectInstance;
       
    62 import javax.management.ObjectName;
       
    63 import javax.management.StandardMBean;
       
    64 import javax.management.loading.MLet;
       
    65 import javax.management.namespace.JMXNamespaces;
       
    66 import javax.management.namespace.JMXRemoteNamespace;
       
    67 import javax.management.namespace.JMXNamespace;
       
    68 
       
    69 import static java.util.Collections.singletonMap;
       
    70 import javax.management.MBeanServerFactory;
       
    71 import javax.management.remote.JMXConnectorServer;
       
    72 import javax.management.remote.JMXConnectorServerFactory;
       
    73 import javax.management.remote.JMXServiceURL;
       
    74 
       
    75 public class RemoteContextTest {
       
    76     private static String failure;
       
    77 
       
    78     public static interface ShowContextMBean {
       
    79         public Map<String, String> getContext();
       
    80         public Map<String, String> getCreationContext();
       
    81         public Set<String> getCalledOps();
       
    82         public String getThing();
       
    83         public void setThing(String x);
       
    84         public int add(int x, int y);
       
    85     }
       
    86 
       
    87     public static class ShowContext
       
    88             extends NotificationBroadcasterSupport
       
    89             implements ShowContextMBean, MBeanRegistration {
       
    90         private final Map<String, String> creationContext;
       
    91         private final Set<String> calledOps = new HashSet<String>();
       
    92 
       
    93         public ShowContext() {
       
    94             creationContext = getContext();
       
    95         }
       
    96 
       
    97         public Map<String, String> getContext() {
       
    98             return ClientContext.getContext();
       
    99         }
       
   100 
       
   101         public Map<String, String> getCreationContext() {
       
   102             return creationContext;
       
   103         }
       
   104 
       
   105         public Set<String> getCalledOps() {
       
   106             return calledOps;
       
   107         }
       
   108 
       
   109         public String getThing() {
       
   110             return "x";
       
   111         }
       
   112 
       
   113         public void setThing(String x) {
       
   114         }
       
   115 
       
   116         public int add(int x, int y) {
       
   117             return x + y;
       
   118         }
       
   119 
       
   120         public ObjectName preRegister(MBeanServer server, ObjectName name) {
       
   121             assertEquals(creationContext, getContext());
       
   122             calledOps.add("preRegister");
       
   123             return name;
       
   124         }
       
   125 
       
   126         public void postRegister(Boolean registrationDone) {
       
   127             assertEquals(creationContext, getContext());
       
   128             calledOps.add("postRegister");
       
   129         }
       
   130 
       
   131         // The condition checked here is not guaranteed universally true,
       
   132         // but is true every time we unregister an instance of this MBean
       
   133         // in this test.
       
   134         public void preDeregister() throws Exception {
       
   135             assertEquals(creationContext, getContext());
       
   136         }
       
   137 
       
   138         public void postDeregister() {
       
   139             assertEquals(creationContext, getContext());
       
   140         }
       
   141 
       
   142         // Same remark as for preDeregister
       
   143         @Override
       
   144         public MBeanNotificationInfo[] getNotificationInfo() {
       
   145             calledOps.add("getNotificationInfo");
       
   146             return super.getNotificationInfo();
       
   147         }
       
   148 
       
   149         @Override
       
   150         public void addNotificationListener(
       
   151                 NotificationListener listener, NotificationFilter filter, Object handback) {
       
   152             calledOps.add("addNotificationListener");
       
   153             super.addNotificationListener(listener, filter, handback);
       
   154         }
       
   155 
       
   156         @Override
       
   157         public void removeNotificationListener(
       
   158                 NotificationListener listener)
       
   159         throws ListenerNotFoundException {
       
   160             calledOps.add("removeNL1");
       
   161             super.removeNotificationListener(listener);
       
   162         }
       
   163 
       
   164         @Override
       
   165         public void removeNotificationListener(
       
   166                 NotificationListener listener, NotificationFilter filter, Object handback)
       
   167         throws ListenerNotFoundException {
       
   168             calledOps.add("removeNL3");
       
   169             super.removeNotificationListener(listener, filter, handback);
       
   170         }
       
   171     }
       
   172 
       
   173     private static class LogRecord {
       
   174         final String op;
       
   175         final Object[] params;
       
   176         final Map<String, String> context;
       
   177         LogRecord(String op, Object[] params, Map<String, String> context) {
       
   178             this.op = op;
       
   179             this.params = params;
       
   180             this.context = context;
       
   181         }
       
   182 
       
   183         @Override
       
   184         public String toString() {
       
   185             return op + Arrays.deepToString(params) + " " + context;
       
   186         }
       
   187     }
       
   188 
       
   189     private static class LogIH implements InvocationHandler {
       
   190         private final Object wrapped;
       
   191         Queue<LogRecord> log = new LinkedList<LogRecord>();
       
   192 
       
   193         LogIH(Object wrapped) {
       
   194             this.wrapped = wrapped;
       
   195         }
       
   196 
       
   197         public Object invoke(Object proxy, Method method, Object[] args)
       
   198         throws Throwable {
       
   199             if (method.getDeclaringClass() != Object.class) {
       
   200                 LogRecord lr =
       
   201                     new LogRecord(
       
   202                         method.getName(), args, ClientContext.getContext());
       
   203                 log.add(lr);
       
   204             }
       
   205             try {
       
   206                 return method.invoke(wrapped, args);
       
   207             } catch (InvocationTargetException e) {
       
   208                 throw e.getCause();
       
   209             }
       
   210         }
       
   211     }
       
   212 
       
   213     private static <T> T newSnoop(Class<T> wrappedClass, LogIH logIH) {
       
   214         return wrappedClass.cast(Proxy.newProxyInstance(
       
   215                 wrappedClass.getClassLoader(),
       
   216                 new Class<?>[] {wrappedClass},
       
   217                 logIH));
       
   218     }
       
   219 
       
   220     public static void main(String[] args) throws Exception {
       
   221         final String subnamespace = "sub";
       
   222         final ObjectName locname = new ObjectName("a:b=c");
       
   223         final ObjectName name = JMXNamespaces.insertPath(subnamespace,locname);
       
   224         final MBeanServer mbs = ClientContext.newContextForwarder(
       
   225                 ManagementFactory.getPlatformMBeanServer(), null);
       
   226         final MBeanServer sub = ClientContext.newContextForwarder(
       
   227                 MBeanServerFactory.newMBeanServer(), null);
       
   228         final JMXServiceURL anonym = new JMXServiceURL("rmi",null,0);
       
   229         final Map<String, Object> env = Collections.emptyMap();
       
   230         final Map<String, String> emptyContext = Collections.emptyMap();
       
   231         final JMXConnectorServer srv =
       
   232                 JMXConnectorServerFactory.newJMXConnectorServer(anonym,env,sub);
       
   233         sub.registerMBean(new ShowContext(), locname);
       
   234 
       
   235         srv.start();
       
   236 
       
   237         try {
       
   238         JMXRemoteNamespace subns = JMXRemoteNamespace.
       
   239             newJMXRemoteNamespace(srv.getAddress(),null);
       
   240         mbs.registerMBean(subns, JMXNamespaces.getNamespaceObjectName("sub"));
       
   241         mbs.invoke(JMXNamespaces.getNamespaceObjectName("sub"),
       
   242                "connect", null,null);
       
   243         final ShowContextMBean show =
       
   244                 JMX.newMBeanProxy(mbs, name, ShowContextMBean.class);
       
   245 
       
   246         assertEquals(emptyContext, show.getContext());
       
   247         ClientContext.doWithContext(singletonMap("foo", "bar"), new Callable<Void>() {
       
   248             public Void call() {
       
   249                 assertEquals(singletonMap("foo", "bar"), show.getContext());
       
   250                 return null;
       
   251             }
       
   252         });
       
   253         assertEquals(emptyContext, show.getContext());
       
   254         String got = ClientContext.doWithContext(
       
   255                 singletonMap("foo", "baz"), new Callable<String>() {
       
   256             public String call() {
       
   257                 return ClientContext.getContext().get("foo");
       
   258             }
       
   259         });
       
   260         assertEquals("baz", got);
       
   261 
       
   262         Map<String, String> combined = ClientContext.doWithContext(
       
   263                 singletonMap("foo", "baz"), new Callable<Map<String, String>>() {
       
   264             public Map<String, String> call() throws Exception {
       
   265                 return ClientContext.doWithContext(
       
   266                         singletonMap("fred", "jim"),
       
   267                         new Callable<Map<String, String>>() {
       
   268                     public Map<String, String> call() {
       
   269                         return ClientContext.getContext();
       
   270                     }
       
   271                 });
       
   272             }
       
   273         });
       
   274         assertEquals(singletonMap("fred", "jim"), combined);
       
   275 
       
   276         final String ugh = "a!?//*=:\"% ";
       
   277         ClientContext.doWithContext(singletonMap(ugh, ugh), new Callable<Void>() {
       
   278             public Void call() {
       
   279                 assertEquals(Collections.singletonMap(ugh, ugh),
       
   280                         ClientContext.getContext());
       
   281                 return null;
       
   282             }
       
   283         });
       
   284 
       
   285         // Basic withContext tests
       
   286 
       
   287         LogIH mbsIH = new LogIH(mbs);
       
   288         MBeanServer snoopMBS = newSnoop(MBeanServer.class, mbsIH);
       
   289         MBeanServer ughMBS = ClientContext.withContext(snoopMBS, ugh, ugh);
       
   290         // ughMBS is never referenced but we check that the withContext call
       
   291         // included a call to snoopMBS.isRegistered.
       
   292         String encodedUgh = URLEncoder.encode(ugh, "UTF-8").replace("*", "%2A");
       
   293         ObjectName expectedName = new ObjectName(
       
   294                 ClientContext.NAMESPACE + ObjectName.NAMESPACE_SEPARATOR +
       
   295                 encodedUgh + "=" + encodedUgh +
       
   296                 ObjectName.NAMESPACE_SEPARATOR + ":" +
       
   297                 JMXNamespace.TYPE_ASSIGNMENT);
       
   298         assertCalled(mbsIH, "isRegistered", new Object[] {expectedName},
       
   299                 emptyContext);
       
   300 
       
   301         // Test withDynamicContext
       
   302 
       
   303         MBeanServerConnection dynamicSnoop =
       
   304                 ClientContext.withDynamicContext(snoopMBS);
       
   305         assertCalled(mbsIH, "isRegistered",
       
   306                 new Object[] {
       
   307                     JMXNamespaces.getNamespaceObjectName(ClientContext.NAMESPACE)
       
   308                 },
       
   309                 emptyContext);
       
   310         final ShowContextMBean dynamicShow =
       
   311                 JMX.newMBeanProxy(dynamicSnoop, name, ShowContextMBean.class);
       
   312         assertEquals(Collections.emptyMap(), dynamicShow.getContext());
       
   313         assertCalled(mbsIH, "getAttribute", new Object[] {name, "Context"},
       
   314                 emptyContext);
       
   315 
       
   316         Map<String, String> expectedDynamic =
       
   317                 Collections.singletonMap("gladstone", "gander");
       
   318         Map<String, String> dynamic = ClientContext.doWithContext(
       
   319                 expectedDynamic,
       
   320                 new Callable<Map<String, String>>() {
       
   321                     public Map<String, String> call() throws Exception {
       
   322                         return dynamicShow.getContext();
       
   323                     }
       
   324                 });
       
   325         assertEquals(expectedDynamic, dynamic);
       
   326         ObjectName expectedDynamicName = new ObjectName(
       
   327                 ClientContext.encode(expectedDynamic) +
       
   328                 ObjectName.NAMESPACE_SEPARATOR + name);
       
   329         assertCalled(mbsIH, "getAttribute",
       
   330                 new Object[] {expectedDynamicName, "Context"}, dynamic);
       
   331 
       
   332         MBeanServer cmbs = ClientContext.withContext(
       
   333                 mbs, "mickey", "mouse");
       
   334         ShowContextMBean cshow =
       
   335                 JMX.newMBeanProxy(cmbs, name, ShowContextMBean.class);
       
   336         assertEquals(Collections.singletonMap("mickey", "mouse"), cshow.getContext());
       
   337 
       
   338         MBeanServer ccmbs = ClientContext.withContext(
       
   339                 cmbs, "donald", "duck");
       
   340         ShowContextMBean ccshow =
       
   341                 JMX.newMBeanProxy(ccmbs, name, ShowContextMBean.class);
       
   342         Map<String, String> disney = new HashMap<String, String>();
       
   343         disney.put("mickey", "mouse");
       
   344         disney.put("donald", "duck");
       
   345         assertEquals(disney, ccshow.getContext());
       
   346 
       
   347         // Test that all MBS ops produce reasonable results
       
   348 
       
   349         ObjectName logger = new ObjectName("a:type=Logger");
       
   350         DynamicMBean showMBean =
       
   351                 new StandardMBean(new ShowContext(), ShowContextMBean.class);
       
   352         LogIH mbeanLogIH = new LogIH(showMBean);
       
   353         DynamicMBean logMBean = newSnoop(DynamicMBean.class, mbeanLogIH);
       
   354         ObjectInstance loggerOI = ccmbs.registerMBean(logMBean, logger);
       
   355         assertEquals(logger, loggerOI.getObjectName());
       
   356 
       
   357         // We get a getMBeanInfo call to determine the className in the
       
   358         // ObjectInstance to return from registerMBean.
       
   359         assertCalled(mbeanLogIH, "getMBeanInfo", disney);
       
   360 
       
   361         ccmbs.getAttribute(logger, "Thing");
       
   362         assertCalled(mbeanLogIH, "getAttribute", disney);
       
   363 
       
   364         ccmbs.getAttributes(logger, new String[] {"Thing", "Context"});
       
   365         assertCalled(mbeanLogIH, "getAttributes", disney);
       
   366 
       
   367         ccmbs.setAttribute(logger, new Attribute("Thing", "bar"));
       
   368         assertCalled(mbeanLogIH, "setAttribute", disney);
       
   369 
       
   370         ccmbs.setAttributes(logger, new AttributeList(
       
   371                 Arrays.asList(new Attribute("Thing", "baz"))));
       
   372         assertCalled(mbeanLogIH, "setAttributes", disney);
       
   373 
       
   374         ccmbs.getMBeanInfo(logger);
       
   375         assertCalled(mbeanLogIH, "getMBeanInfo", disney);
       
   376 
       
   377         Set<ObjectName> names = ccmbs.queryNames(null, null);
       
   378         Set<ObjectName> expectedNames = new HashSet<ObjectName>(
       
   379                 Collections.singleton(MBeanServerDelegate.DELEGATE_NAME));
       
   380         expectedNames.removeAll(names);
       
   381         assertEquals(0, expectedNames.size());
       
   382 
       
   383         Set<ObjectName> nsNames =
       
   384                 ccmbs.queryNames(new ObjectName("**?*?//:*"), null);
       
   385         Set<ObjectName> expectedNsNames = new HashSet<ObjectName>(
       
   386                 Arrays.asList(
       
   387                 new ObjectName(ClientContext.NAMESPACE +
       
   388                 ObjectName.NAMESPACE_SEPARATOR + ":" +
       
   389                 JMXNamespace.TYPE_ASSIGNMENT)));
       
   390         expectedNsNames.removeAll(nsNames);
       
   391         assertEquals(0, expectedNsNames.size());
       
   392 
       
   393         Set<ObjectInstance> insts = ccmbs.queryMBeans(
       
   394                 MBeanServerDelegate.DELEGATE_NAME, null);
       
   395         assertEquals(1, insts.size());
       
   396         assertEquals(MBeanServerDelegate.DELEGATE_NAME,
       
   397                 insts.iterator().next().getObjectName());
       
   398 
       
   399         ObjectName createdName = new ObjectName("a:type=Created");
       
   400         ObjectInstance createdOI =
       
   401                 ccmbs.createMBean(ShowContext.class.getName(), createdName);
       
   402         assertEquals(ShowContext.class.getName(), createdOI.getClassName());
       
   403         assertEquals(createdName, createdOI.getObjectName());
       
   404         assertEquals(disney, ccmbs.getAttribute(createdName, "CreationContext"));
       
   405 
       
   406         NotificationListener nothingListener = new NotificationListener() {
       
   407             public void handleNotification(Notification n, Object h) {}
       
   408         };
       
   409         ccmbs.addNotificationListener(createdName, nothingListener, null, null);
       
   410         ccmbs.removeNotificationListener(createdName, nothingListener, null, null);
       
   411         ccmbs.addNotificationListener(createdName, nothingListener, null, null);
       
   412         ccmbs.removeNotificationListener(createdName, nothingListener);
       
   413         Set<String> expectedOps = new HashSet<String>(Arrays.asList(
       
   414                 "preRegister", "postRegister", "addNotificationListener",
       
   415                 "removeNL1", "removeNL3", "getNotificationInfo"));
       
   416         assertEquals(expectedOps, ccmbs.getAttribute(createdName, "CalledOps"));
       
   417 
       
   418         assertEquals(ShowContext.class.getClassLoader(),
       
   419                 ccmbs.getClassLoaderFor(createdName));
       
   420 
       
   421         assertEquals(true, ccmbs.isRegistered(createdName));
       
   422         assertEquals(true, ccmbs.isInstanceOf(createdName,
       
   423                 ShowContext.class.getName()));
       
   424         assertEquals(false, ccmbs.isInstanceOf(createdName,
       
   425                 DynamicMBean.class.getName()));
       
   426         ccmbs.unregisterMBean(createdName);
       
   427         assertEquals(false, ccmbs.isRegistered(createdName));
       
   428 
       
   429         MLet mlet = new MLet();
       
   430         ObjectName defaultMLetName = new ObjectName("DefaultDomain:type=MLet");
       
   431 
       
   432         ccmbs.registerMBean(mlet, defaultMLetName);
       
   433 
       
   434         assertEquals(mlet, ccmbs.getClassLoader(defaultMLetName));
       
   435 
       
   436         assertEquals(0, mbeanLogIH.log.size());
       
   437 
       
   438         // Test that contexts still work when we can't combine two encoded contexts.
       
   439         // Here, we wrap cmbs (mickey=mouse) so that ccmbs2 (donald=duck) cannot
       
   440         // see that it already contains a context and therefore cannot combine
       
   441         // into mickey=mouse;donald=duck.  We don't actually use the snoop
       
   442         // capabilities of the returned object -- we just want an opaque
       
   443         // MBeanServer wrapper
       
   444         MBeanServer cmbs2 = newSnoop(MBeanServer.class, new LogIH(cmbs));
       
   445         MBeanServer ccmbs2 = ClientContext.withContext(cmbs2, "donald", "duck");
       
   446         assertEquals(disney, ccmbs2.getAttribute(name, "Context"));
       
   447 
       
   448         // ADD NEW TESTS HERE ^^^
       
   449 
       
   450         if (failure != null)
       
   451             throw new Exception(failure);
       
   452         } finally {
       
   453             srv.stop();
       
   454         }
       
   455     }
       
   456 
       
   457     private static void assertEquals(Object x, Object y) {
       
   458         if (!equal(x, y))
       
   459             failed("expected " + string(x) + "; got " + string(y));
       
   460     }
       
   461 
       
   462     private static boolean equal(Object x, Object y) {
       
   463         if (x == y)
       
   464             return true;
       
   465         if (x == null || y == null)
       
   466             return false;
       
   467         if (x.getClass().isArray())
       
   468             return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
       
   469         return x.equals(y);
       
   470     }
       
   471 
       
   472     private static String string(Object x) {
       
   473         String s = Arrays.deepToString(new Object[] {x});
       
   474         return s.substring(1, s.length() - 1);
       
   475     }
       
   476 
       
   477     private static void assertCalled(
       
   478             LogIH logIH, String op, Map<String, String> expectedContext) {
       
   479         assertCalled(logIH, op, null, expectedContext);
       
   480     }
       
   481 
       
   482     private static void assertCalled(
       
   483             LogIH logIH, String op, Object[] params,
       
   484             Map<String, String> expectedContext) {
       
   485         LogRecord lr = logIH.log.remove();
       
   486         assertEquals(op, lr.op);
       
   487         if (params != null)
       
   488             assertEquals(params, lr.params);
       
   489         assertEquals(expectedContext, lr.context);
       
   490     }
       
   491 
       
   492     private static void failed(String why) {
       
   493         failure = why;
       
   494         new Throwable("FAILED: " + why).printStackTrace(System.out);
       
   495     }
       
   496 }