/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.jmx; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE; import java.io.IOException; import java.lang.management.ManagementFactory; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.management.Attribute; import javax.management.AttributeChangeNotification; import javax.management.AttributeList; import javax.management.InstanceNotFoundException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MBeanServerDelegate; import javax.management.MBeanServerNotification; import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.NotificationFilterSupport; import javax.management.NotificationListener; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.Query; import javax.management.QueryExp; import javax.management.RuntimeOperationsException; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenMBeanAttributeInfo; import javax.management.openmbean.OpenMBeanOperationInfo; import javax.management.openmbean.OpenMBeanParameterInfo; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularType; 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; import org.jboss.as.controller.Extension; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ProcessType; import org.jboss.as.controller.capability.registry.RuntimeCapabilityRegistry; import org.jboss.as.controller.extension.ExtensionRegistry; import org.jboss.as.controller.extension.ExtensionRegistryType; import org.jboss.as.controller.operations.common.ResolveExpressionHandler; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.Resource; import org.jboss.as.jmx.model.ModelControllerMBeanHelper; import org.jboss.as.network.SocketBinding; import org.jboss.as.remoting.EndpointService; import org.jboss.as.remoting.RemotingServices; import org.jboss.as.remoting.management.ManagementRemotingServices; import org.jboss.as.subsystem.test.AbstractSubsystemTest; import org.jboss.as.subsystem.test.AdditionalInitialization; import org.jboss.as.subsystem.test.ControllerInitializer; import org.jboss.as.subsystem.test.KernelServices; import org.jboss.dmr.ModelType; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.jboss.staxmapper.XMLMapper; import org.junit.After; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.xnio.IoUtils; import org.xnio.OptionMap; /** * * This test modifies the {@link java.lang.management.ManagementFactory#getPlatformMBeanServer()} by setting the * @{code javax.management.builder.initial} system property. * * The jmx module runs its test target in Maven by always forking the JVM (and not reuse one) to ensure that the * platform mbean server is not modified outside of this test case. * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> */ public class ModelControllerMBeanTestCase extends AbstractSubsystemTest { private static final int JMX_PORT = 27258; private static final String LEGACY_DOMAIN = "jboss.resolved"; private static final ObjectName LEGACY_ROOT_NAME = ModelControllerMBeanHelper.createRootObjectName(LEGACY_DOMAIN); private static final ObjectName LEGACY_INTERFACE_NAME = createObjectName(LEGACY_DOMAIN + ":interface=test-interface"); private static final ObjectName LEGACY_SOCKET_BINDING_GROUP_NAME = createObjectName(LEGACY_DOMAIN + ":socket-binding-group=test-socket-binding-group"); private static final ObjectName LEGACY_SERVER_SOCKET_BINDING_NAME = createObjectName(LEGACY_DOMAIN + ":socket-binding-group=test-socket-binding-group,socket-binding=server"); private static final ObjectName LEGACY_SERVER_SOCKET_BINDING_NAME_2 = createObjectName(LEGACY_DOMAIN + ":socket-binding=server,socket-binding-group=test-socket-binding-group"); private static final ObjectName LEGACY_SUBSYSTEM_NAME = createObjectName(LEGACY_DOMAIN + ":subsystem=jmx"); private static final ObjectName LEGACY_BAD_NAME = createObjectName(LEGACY_DOMAIN + ":type=bad"); private static final String EXPR_DOMAIN = "jboss.as.expr"; private static final ObjectName EXPR_ROOT_NAME = ModelControllerMBeanHelper.createRootObjectName(EXPR_DOMAIN); private static final ObjectName EXPR_INTERFACE_NAME = createObjectName(EXPR_DOMAIN + ":interface=test-interface"); private static final ObjectName EXPR_SOCKET_BINDING_GROUP_NAME = createObjectName(EXPR_DOMAIN + ":socket-binding-group=test-socket-binding-group"); private static final ObjectName EXPR_SERVER_SOCKET_BINDING_NAME = createObjectName(EXPR_DOMAIN + ":socket-binding-group=test-socket-binding-group,socket-binding=server"); private static final ObjectName EXPR_SERVER_SOCKET_BINDING_NAME_2 = createObjectName(EXPR_DOMAIN + ":socket-binding=server,socket-binding-group=test-socket-binding-group"); private static final ObjectName EXPR_SUBSYSTEM_NAME = createObjectName(EXPR_DOMAIN + ":subsystem=jmx"); private static final ObjectName EXPR_BAD_NAME = createObjectName(LEGACY_DOMAIN + ":type=bad"); private static final QueryExp PORT_QUERY_EXP = Query.eq(Query.attr("port"), Query.value(JMX_PORT)); private static final QueryExp PORT_STRING_QUERY_EXP = Query.eq(Query.attr("port"), Query.value(Integer.toString(JMX_PORT))); private static final QueryExp DEFAULT_INTERFACE_QUERY_EXP = Query.eq(Query.attr("defaultInterface"), Query.value("test-interface")); private JMXConnector jmxConnector; public ModelControllerMBeanTestCase() { super(JMXExtension.SUBSYSTEM_NAME, new JMXExtension()); } @BeforeClass public static void beforeClass() { System.setProperty("javax.management.builder.initial", PluggableMBeanServerBuilder.class.getName()); } @Override @After public void cleanup() throws Exception { super.cleanup(); IoUtils.safeClose(jmxConnector); jmxConnector = null; } @Test public void testExposedMBeans() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new BaseAdditionalInitialization(ProcessType.STANDALONE_SERVER)); int count = connection.getMBeanCount(); checkQueryMBeans(connection, count, null); checkQueryMBeans(connection, count, new ObjectName("*:*")); Set<ObjectInstance> filteredInstances = connection.queryMBeans(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), null); Set<ObjectName> filteredNames = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), null); Assert.assertEquals(2, filteredInstances.size()); Assert.assertEquals(2, filteredNames.size()); checkSameMBeans(filteredInstances, filteredNames); assertContainsNames(filteredNames, LEGACY_SOCKET_BINDING_GROUP_NAME, LEGACY_SERVER_SOCKET_BINDING_NAME); filteredInstances = connection.queryMBeans(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), null); filteredNames = connection.queryNames(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), null); Assert.assertEquals(2, filteredInstances.size()); Assert.assertEquals(2, filteredNames.size()); checkSameMBeans(filteredInstances, filteredNames); assertContainsNames(filteredNames, EXPR_SOCKET_BINDING_GROUP_NAME, EXPR_SERVER_SOCKET_BINDING_NAME); // WFCORE-1716 Test with a property list pattern where the non-wildcard keys are for items later in the address filteredInstances = connection.queryMBeans(createObjectName(LEGACY_DOMAIN + ":socket-binding=*,*"), null); filteredNames = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":socket-binding=*,*"), null); Assert.assertEquals(1, filteredInstances.size()); Assert.assertEquals(1, filteredNames.size()); checkSameMBeans(filteredInstances, filteredNames); assertContainsNames(filteredNames, LEGACY_SERVER_SOCKET_BINDING_NAME); // WFCORE-1257 -- Test with QueryExp // First a numeric query (port) = (12345) filteredInstances = connection.queryMBeans(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), PORT_QUERY_EXP); filteredNames = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), PORT_QUERY_EXP); Assert.assertEquals(1, filteredInstances.size()); Assert.assertEquals(1, filteredNames.size()); checkSameMBeans(filteredInstances, filteredNames); assertContainsNames(filteredNames, LEGACY_SERVER_SOCKET_BINDING_NAME); // Doesn't match on jboss.as.expr as the value is a string filteredInstances = connection.queryMBeans(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), PORT_QUERY_EXP); filteredNames = connection.queryNames(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), PORT_QUERY_EXP); Assert.assertEquals(0, filteredInstances.size()); Assert.assertEquals(0, filteredNames.size()); // Next a port string query (port) = ("12345") // Doesn't match on jboss.as as the value is an int filteredInstances = connection.queryMBeans(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), PORT_STRING_QUERY_EXP); filteredNames = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), PORT_STRING_QUERY_EXP); Assert.assertEquals(0, filteredInstances.size()); Assert.assertEquals(0, filteredNames.size()); // Does match on jboss.as.expr as the value is a string filteredInstances = connection.queryMBeans(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), PORT_STRING_QUERY_EXP); filteredNames = connection.queryNames(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), PORT_STRING_QUERY_EXP); Assert.assertEquals(1, filteredInstances.size()); Assert.assertEquals(1, filteredNames.size()); checkSameMBeans(filteredInstances, filteredNames); assertContainsNames(filteredNames, EXPR_SERVER_SOCKET_BINDING_NAME); // Next a straight string query (defaultInterface) = ("test-interface") // Note this also checks a bit on the default-interface -> defaultInterface camel case handling filteredInstances = connection.queryMBeans(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), DEFAULT_INTERFACE_QUERY_EXP); filteredNames = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":socket-binding-group=*,*"), DEFAULT_INTERFACE_QUERY_EXP); Assert.assertEquals(1, filteredInstances.size()); Assert.assertEquals(1, filteredNames.size()); checkSameMBeans(filteredInstances, filteredNames); assertContainsNames(filteredNames, LEGACY_SOCKET_BINDING_GROUP_NAME); filteredInstances = connection.queryMBeans(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), DEFAULT_INTERFACE_QUERY_EXP); filteredNames = connection.queryNames(createObjectName(EXPR_DOMAIN + ":socket-binding-group=*,*"), DEFAULT_INTERFACE_QUERY_EXP); Assert.assertEquals(1, filteredInstances.size()); Assert.assertEquals(1, filteredNames.size()); checkSameMBeans(filteredInstances, filteredNames); assertContainsNames(filteredNames, EXPR_SOCKET_BINDING_GROUP_NAME); } private void checkQueryMBeans(MBeanServerConnection connection, int count, ObjectName filter) throws Exception { Set<ObjectInstance> instances = connection.queryMBeans(filter, null); Set<ObjectName> objectNames = connection.queryNames(filter, null); Assert.assertEquals(count, instances.size()); Assert.assertEquals(count, objectNames.size()); checkSameMBeans(instances, objectNames); assertContainsNames(objectNames, LEGACY_ROOT_NAME, LEGACY_INTERFACE_NAME, LEGACY_SOCKET_BINDING_GROUP_NAME, LEGACY_SERVER_SOCKET_BINDING_NAME, LEGACY_SUBSYSTEM_NAME, EXPR_ROOT_NAME, EXPR_INTERFACE_NAME, EXPR_SOCKET_BINDING_GROUP_NAME, EXPR_SERVER_SOCKET_BINDING_NAME, EXPR_SUBSYSTEM_NAME); } @Test public void testGetObjectInstance() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new BaseAdditionalInitialization(ProcessType.STANDALONE_SERVER)); Assert.assertNotNull(connection.getObjectInstance(LEGACY_ROOT_NAME)); Assert.assertEquals(LEGACY_ROOT_NAME, connection.getObjectInstance(LEGACY_ROOT_NAME).getObjectName()); Assert.assertNotNull(connection.getObjectInstance(LEGACY_INTERFACE_NAME)); Assert.assertNotNull(connection.getObjectInstance(LEGACY_SERVER_SOCKET_BINDING_NAME)); Assert.assertNotNull(connection.getObjectInstance(LEGACY_SERVER_SOCKET_BINDING_NAME_2)); try { connection.getObjectInstance(LEGACY_BAD_NAME); Assert.fail(); } catch (InstanceNotFoundException expected) { //expected } Assert.assertNotNull(connection.getObjectInstance(EXPR_ROOT_NAME)); Assert.assertEquals(EXPR_ROOT_NAME, connection.getObjectInstance(EXPR_ROOT_NAME).getObjectName()); Assert.assertNotNull(connection.getObjectInstance(EXPR_INTERFACE_NAME)); Assert.assertNotNull(connection.getObjectInstance(EXPR_SERVER_SOCKET_BINDING_NAME)); Assert.assertNotNull(connection.getObjectInstance(EXPR_SERVER_SOCKET_BINDING_NAME_2)); try { connection.getObjectInstance(EXPR_BAD_NAME); Assert.fail(); } catch (InstanceNotFoundException expected) { //expected } } @Test public void testGetMBeanInfoStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); MBeanInfo info = connection.getMBeanInfo(LEGACY_ROOT_NAME); Assert.assertNotNull(info); //Make sure all occurrences of "-" have gone for (MBeanAttributeInfo attr : info.getAttributes()) { Assert.assertFalse(attr.getName().contains("-")); } for (MBeanOperationInfo op : info.getOperations()) { Assert.assertFalse(op.getName().contains("-")); for (MBeanParameterInfo param : op.getSignature()) { Assert.assertFalse(param.getName().contains("-")); } } // Make sure that the description gets set for things using resource // bundles info = connection.getMBeanInfo(createObjectName(LEGACY_DOMAIN + ":subsystem=jmx")); Assert.assertNotNull(info); Assert.assertEquals(JMXExtension.getResourceDescriptionResolver("").getResourceBundle(Locale.getDefault()) .getString(CommonAttributes.JMX), info.getDescription()); info = connection.getMBeanInfo(createObjectName(LEGACY_DOMAIN + ":subsystem=test")); Assert.assertNotNull(info); Assert.assertEquals("description", info.getDescription()); checkMBeanInfoAttributes(info, true, false); MBeanOperationInfo[] operations = info.getOperations(); Assert.assertEquals(4, operations.length); OpenMBeanOperationInfo op = findOperation(operations, ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME); Assert.assertEquals(ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME, op.getName()); Assert.assertEquals("void-no-params", op.getDescription()); Assert.assertEquals(0, op.getSignature().length); Assert.assertEquals(Void.class.getName(), op.getReturnType()); op = findOperation(operations, ModelControllerResourceDefinition.IntOperationWithParams.OPERATION_JMX_NAME); Assert.assertEquals(ModelControllerResourceDefinition.IntOperationWithParams.OPERATION_JMX_NAME, op.getName()); Assert.assertEquals("int-with-params", op.getDescription()); Assert.assertEquals(String.class.getName(), op.getReturnType()); Assert.assertEquals(5, op.getSignature().length); Assert.assertEquals("param1", op.getSignature()[0].getName()); Assert.assertEquals("int-with-params-param1", op.getSignature()[0].getDescription()); Assert.assertEquals(Long.class.getName(), op.getSignature()[0].getType()); Assert.assertEquals("param2", op.getSignature()[1].getName()); Assert.assertEquals("int-with-params-param2", op.getSignature()[1].getDescription()); Assert.assertEquals(String[].class.getName(), op.getSignature()[1].getType()); Assert.assertEquals("param3", op.getSignature()[2].getName()); Assert.assertEquals("int-with-params-param3", op.getSignature()[2].getDescription()); Assert.assertEquals(TabularData.class.getName(), op.getSignature()[2].getType()); assertMapType(assertCast(OpenMBeanParameterInfo.class, op.getSignature()[2]).getOpenType(), SimpleType.STRING, SimpleType.INTEGER); Assert.assertEquals("param4", op.getSignature()[3].getName()); Assert.assertEquals("int-with-params-param4", op.getSignature()[3].getDescription()); Assert.assertEquals(Integer.class.getName(), op.getSignature()[3].getType()); OpenMBeanParameterInfo parameterInfo = assertCast(OpenMBeanParameterInfo.class, op.getSignature()[3]); Assert.assertEquals(6, parameterInfo.getDefaultValue()); //Assert.assertEquals(5, parameterInfo.getMinValue()); //todo min & max with expressions have lots of problems WFLY-3500 //Assert.assertEquals(10, parameterInfo.getMaxValue()); Assert.assertEquals("param5", op.getSignature()[4].getName()); Assert.assertEquals("int-with-params-param5", op.getSignature()[4].getDescription()); Assert.assertEquals(Integer.class.getName(), op.getSignature()[4].getType()); parameterInfo = assertCast(OpenMBeanParameterInfo.class, op.getSignature()[4]); Assert.assertNull(parameterInfo.getDefaultValue()); Assert.assertEquals(new HashSet<Object>(Arrays.asList(3, 5, 7)), parameterInfo.getLegalValues()); //todo add support for allowed values to AD op = findOperation(operations, ModelControllerResourceDefinition.ComplexOperation.OPERATION_NAME); Assert.assertEquals(ModelControllerResourceDefinition.ComplexOperation.OPERATION_NAME, op.getName()); Assert.assertEquals("complex", op.getDescription()); checkComplexTypeInfo(assertCast(CompositeType.class, op.getReturnOpenType()), false, "complex."); Assert.assertEquals(1, op.getSignature().length); checkComplexTypeInfo(assertCast(CompositeType.class, assertCast(OpenMBeanParameterInfo.class, op.getSignature()[0]).getOpenType()), false, "param1."); MBeanNotificationInfo[] notifications = info.getNotifications(); Set<String> notificationTypes = getNotificationTypes(notifications); Assert.assertEquals(1, notificationTypes.size()); Assert.assertTrue(notificationTypes.contains(AttributeChangeNotification.ATTRIBUTE_CHANGE)); } private Set<String> getNotificationTypes(MBeanNotificationInfo[] notifications) { Set<String> notificationTypes = new HashSet<String>(); for (MBeanNotificationInfo notification : notifications) { for (String notificationType : notification.getNotifTypes()) { notificationTypes.add(notificationType); } } return notificationTypes; } @Test public void testGetMBeanInfoDomain() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.DOMAIN_SERVER, new TestExtension())); MBeanInfo info = connection.getMBeanInfo(LEGACY_ROOT_NAME); Assert.assertNotNull(info); //Make sure all occurrences of "-" have gone for (MBeanAttributeInfo attr : info.getAttributes()) { Assert.assertFalse(attr.getName().contains("-")); } for (MBeanOperationInfo op : info.getOperations()) { Assert.assertFalse(op.getName().contains("-")); for (MBeanParameterInfo param : op.getSignature()) { Assert.assertFalse(param.getName().contains("-")); } } // Make sure that the description gets set for things using resource // bundles info = connection.getMBeanInfo(createObjectName(LEGACY_DOMAIN + ":subsystem=jmx")); Assert.assertNotNull(info); Assert.assertEquals(JMXExtension.getResourceDescriptionResolver("").getResourceBundle(Locale.getDefault()) .getString(CommonAttributes.JMX), info.getDescription()); info = connection.getMBeanInfo(createObjectName(LEGACY_DOMAIN + ":subsystem=test")); Assert.assertNotNull(info); Assert.assertEquals("description", info.getDescription()); //All attributes should be read-only checkMBeanInfoAttributes(info, false, false); MBeanOperationInfo[] operations = info.getOperations(); Assert.assertEquals(2, operations.length); OpenMBeanOperationInfo op = findOperation(operations, ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME); Assert.assertEquals(ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME, op.getName()); Assert.assertEquals("void-no-params", op.getDescription()); Assert.assertEquals(0, op.getSignature().length); Assert.assertEquals(Void.class.getName(), op.getReturnType()); } @Test public void testGetMBeanInfoExpressionsStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension(true))); MBeanInfo info = connection.getMBeanInfo(EXPR_ROOT_NAME); Assert.assertNotNull(info); //Make sure all occurrences of "-" have gone for (MBeanAttributeInfo attr : info.getAttributes()) { Assert.assertFalse(attr.getName().contains("-")); } for (MBeanOperationInfo op : info.getOperations()) { Assert.assertFalse(op.getName().contains("-")); for (MBeanParameterInfo param : op.getSignature()) { Assert.assertFalse(param.getName().contains("-")); } } // Make sure that the description gets set for things using resource // bundles info = connection.getMBeanInfo(createObjectName(EXPR_DOMAIN + ":subsystem=jmx")); Assert.assertNotNull(info); Assert.assertEquals(JMXExtension.getResourceDescriptionResolver("").getResourceBundle(Locale.getDefault()) .getString(CommonAttributes.JMX), info.getDescription()); info = connection.getMBeanInfo(createObjectName(EXPR_DOMAIN + ":subsystem=test")); Assert.assertNotNull(info); Assert.assertEquals("description", info.getDescription()); checkMBeanInfoAttributes(info, true, true); MBeanOperationInfo[] operations = info.getOperations(); Assert.assertEquals(4, operations.length); OpenMBeanOperationInfo op = findOperation(operations, ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME); Assert.assertEquals(ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME, op.getName()); Assert.assertEquals("void-no-params", op.getDescription()); Assert.assertEquals(0, op.getSignature().length); Assert.assertEquals(Void.class.getName(), op.getReturnType()); op = findOperation(operations, ModelControllerResourceDefinition.IntOperationWithParams.OPERATION_JMX_NAME); Assert.assertEquals(ModelControllerResourceDefinition.IntOperationWithParams.OPERATION_JMX_NAME, op.getName()); Assert.assertEquals("int-with-params", op.getDescription()); Assert.assertEquals(String.class.getName(), op.getReturnType()); Assert.assertEquals(5, op.getSignature().length); Assert.assertEquals("param1", op.getSignature()[0].getName()); Assert.assertEquals("int-with-params-param1", op.getSignature()[0].getDescription()); Assert.assertEquals(String.class.getName(), op.getSignature()[0].getType()); Assert.assertEquals("param2", op.getSignature()[1].getName()); Assert.assertEquals("int-with-params-param2", op.getSignature()[1].getDescription()); Assert.assertEquals(String[].class.getName(), op.getSignature()[1].getType()); Assert.assertEquals("param3", op.getSignature()[2].getName()); Assert.assertEquals("int-with-params-param3", op.getSignature()[2].getDescription()); Assert.assertEquals(TabularData.class.getName(), op.getSignature()[2].getType()); assertMapType(assertCast(OpenMBeanParameterInfo.class, op.getSignature()[2]).getOpenType(), SimpleType.STRING, SimpleType.STRING); Assert.assertEquals("param4", op.getSignature()[3].getName()); Assert.assertEquals("int-with-params-param4", op.getSignature()[3].getDescription()); Assert.assertEquals(String.class.getName(), op.getSignature()[3].getType()); OpenMBeanParameterInfo parameterInfo = assertCast(OpenMBeanParameterInfo.class, op.getSignature()[3]); Assert.assertNotNull(parameterInfo.getDefaultValue()); Assert.assertNull(parameterInfo.getMinValue()); Assert.assertNull(parameterInfo.getMaxValue()); Assert.assertEquals("param5", op.getSignature()[4].getName()); Assert.assertEquals("int-with-params-param5", op.getSignature()[4].getDescription()); Assert.assertEquals(String.class.getName(), op.getSignature()[4].getType()); parameterInfo = assertCast(OpenMBeanParameterInfo.class, op.getSignature()[4]); Assert.assertNull(parameterInfo.getDefaultValue()); Assert.assertNotNull(parameterInfo.getLegalValues()); Assert.assertNull(parameterInfo.getDefaultValue()); op = findOperation(operations, ModelControllerResourceDefinition.ComplexOperation.OPERATION_NAME); Assert.assertEquals(ModelControllerResourceDefinition.ComplexOperation.OPERATION_NAME, op.getName()); Assert.assertEquals("complex", op.getDescription()); checkComplexTypeInfo(assertCast(CompositeType.class, op.getReturnOpenType()), true, "complex."); Assert.assertEquals(1, op.getSignature().length); checkComplexTypeInfo(assertCast(CompositeType.class, assertCast(OpenMBeanParameterInfo.class, op.getSignature()[0]).getOpenType()), true, "param1."); } private void checkMBeanInfoAttributes(MBeanInfo info, boolean writable, boolean expressions) { //All attributes should be read-only MBeanAttributeInfo[] attributes = info.getAttributes(); Assert.assertEquals(14, attributes.length); Arrays.sort(attributes, new Comparator<MBeanAttributeInfo>() { @Override public int compare(MBeanAttributeInfo o1, MBeanAttributeInfo o2) { return o1.getName().compareTo(o2.getName()); } }); assertAttributeDescription(attributes[0], "bigdec", expressions ? String.class.getName() : BigDecimal.class.getName(), "bigdec", true, writable); assertAttributeDescription(attributes[1], "bigint", expressions ? String.class.getName() : BigInteger.class.getName(), "bigint", true, writable); assertAttributeDescription(attributes[2], "boolean", expressions ? String.class.getName() : Boolean.class.getName(), "boolean", true, writable); assertAttributeDescription(attributes[3], "bytes", byte[].class.getName(), "bytes", true, writable); checkComplexTypeInfo(assertCast(CompositeType.class, assertCast(OpenMBeanAttributeInfo.class, attributes[4]).getOpenType()), expressions, "complex."); assertAttributeDescription(attributes[5], "double", expressions ? String.class.getName() : Double.class.getName(), "double", true, writable); assertAttributeDescription(attributes[6], "int", expressions ? String.class.getName() : Integer.class.getName(), "int", true, writable); assertAttributeDescription(attributes[7], "list", expressions ? String[].class.getName() : Integer[].class.getName(), "list", true, writable); assertAttributeDescription(attributes[8], "long", expressions ? String.class.getName() : Long.class.getName(), "long", true, writable); //type=OBJECT, value-type=a simple type -> a map assertAttributeDescription(attributes[9], "map", TabularData.class.getName(), "map", true, writable); assertMapType(assertCast(OpenMBeanAttributeInfo.class, attributes[9]).getOpenType(), SimpleType.STRING, expressions ? SimpleType.STRING : SimpleType.INTEGER); assertAttributeDescription(attributes[10], "roInt", expressions ? String.class.getName() : Integer.class.getName(), "ro-int", true, false); assertAttributeDescription(attributes[11], "string", expressions ? String.class.getName() : String.class.getName(), "string", true, writable); assertAttributeDescription(attributes[12], "type", String.class.getName(), "type", true, writable); assertAttributeDescription(attributes[13], "undefinedInt", expressions ? String.class.getName() : Integer.class.getName(), "undefined-int", true, writable); } @Test public void testReadWriteAttributeStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); ObjectName name = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); checkAttributeValues(connection, name, 1, null, 2, BigInteger.valueOf(3), BigDecimal.valueOf(4), false, new byte[]{5, 6}, 7.0, "8", Collections.singletonList(9), 10, ModelType.INT, "key1", 11, "key2", 12); Assert.assertNull(connection.getAttribute(name, "complex")); try { connection.setAttribute(name, new Attribute("roInt", 101)); Assert.fail("roInt not writable"); } catch (Exception expected) { //expected } connection.setAttribute(name, new Attribute("int", 102)); connection.setAttribute(name, new Attribute("undefinedInt", 103)); connection.setAttribute(name, new Attribute("bigint", BigInteger.valueOf(104))); connection.setAttribute(name, new Attribute("bigdec", BigDecimal.valueOf(105))); connection.setAttribute(name, new Attribute("boolean", Boolean.TRUE)); connection.setAttribute(name, new Attribute("bytes", new byte[]{106, 107})); connection.setAttribute(name, new Attribute("double", 108.0)); connection.setAttribute(name, new Attribute("string", "109")); connection.setAttribute(name, new Attribute("list", new Integer[]{110})); connection.setAttribute(name, new Attribute("long", 111L)); connection.setAttribute(name, new Attribute("type", ModelType.STRING.toString())); Map<String, Integer> map = new HashMap<String, Integer>(); map.put("keyA", 112); map.put("keyB", 113); connection.setAttribute(name, new Attribute("map", map)); MBeanInfo info = connection.getMBeanInfo(name); CompositeType complexType = assertCast(CompositeType.class, findAttribute(info.getAttributes(), "complex").getOpenType()); connection.setAttribute(name, new Attribute("complex", createComplexData(connection, complexType, 1, BigDecimal.valueOf(2.0)))); checkAttributeValues(connection, name, 1, 103, 102, BigInteger.valueOf(104), BigDecimal.valueOf(105), true, new byte[]{106, 107}, 108.0, "109", Collections.singletonList(110), 111, ModelType.STRING, "keyA", 112, "keyB", 113); CompositeData compositeData = assertCast(CompositeData.class, connection.getAttribute(name, "complex")); Assert.assertEquals(1, compositeData.get("int-value")); Assert.assertEquals(BigDecimal.valueOf(2.0), compositeData.get("bigdecimal-value")); } @Test public void testReadWriteAttributeDomain() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.DOMAIN_SERVER, new TestExtension())); ObjectName name = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); checkAttributeValues(connection, name, 1, null, 2, BigInteger.valueOf(3), BigDecimal.valueOf(4), false, new byte[]{5, 6}, 7.0, "8", Collections.singletonList(9), 10, ModelType.INT, "key1", 11, "key2", 12); Assert.assertNull(connection.getAttribute(name, "complex")); try { connection.setAttribute(name, new Attribute("roInt", 101)); Assert.fail("roInt not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("int", 102)); Assert.fail("int not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("undefinedInt", 103)); Assert.fail("undefinedInt not writable"); } catch (Exception expected) { //expected } try { //TODO BigInteger not working in current DMR version //connection.setAttribute(name, new Attribute("bigint", BigInteger.valueOf(104))); connection.setAttribute(name, new Attribute("bigdec", BigDecimal.valueOf(105))); Assert.fail("bigdec not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("boolean", Boolean.TRUE)); Assert.fail("boolean not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("bytes", new byte[]{106, 107})); Assert.fail("bytes not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("double", 108.0)); Assert.fail("double not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("string", "109")); Assert.fail("string not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("list", new Integer[]{110})); Assert.fail("list not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("long", 111L)); Assert.fail("long not writable"); } catch (Exception expected) { //expected } try { connection.setAttribute(name, new Attribute("type", ModelType.STRING.toString())); Assert.fail("type not writable"); } catch (Exception expected) { //expected } try { Map<String, Integer> map = new HashMap<String, Integer>(); map.put("keyA", 112); map.put("keyB", 113); connection.setAttribute(name, new Attribute("map", map)); Assert.fail("map not writable"); } catch (Exception expected) { //expected } MBeanInfo info = connection.getMBeanInfo(name); CompositeType complexType = assertCast(CompositeType.class, findAttribute(info.getAttributes(), "complex").getOpenType()); try { connection.setAttribute(name, new Attribute("complex", createComplexData(connection, complexType, 1, BigDecimal.valueOf(2.0)))); Assert.fail("Complex not writable"); } catch (Exception expected) { //expected } checkAttributeValues(connection, name, 1, null, 2, BigInteger.valueOf(3), BigDecimal.valueOf(4), false, new byte[]{5, 6}, 7.0, "8", Collections.singletonList(9), 10, ModelType.INT, "key1", 11, "key2", 12); Assert.assertNull(connection.getAttribute(name, "complex")); } private void checkAttributeValues(MBeanServerConnection connection, ObjectName name, int roInt, Integer undefinedInt, int i, BigInteger bigInt, BigDecimal bigDecimal, boolean bool, byte[] bytes, double dbl, String s, List<Integer> list, long lng, ModelType type, String tblKey1, int tblValue1, String tblKey2, int tblValue2) throws Exception { Assert.assertEquals(roInt, assertCast(Integer.class, connection.getAttribute(name, "roInt")).intValue()); if (undefinedInt == null) { Assert.assertNull(connection.getAttribute(name, "undefinedInt")); } else { Assert.assertEquals(undefinedInt, assertCast(Integer.class, connection.getAttribute(name, "undefinedInt"))); } Assert.assertEquals(i, assertCast(Integer.class, connection.getAttribute(name, "int")).intValue()); //TODO BigInteger not working in current DMR version //Assert.assertEquals(BigInteger.valueOf(3), assertCast(BigInteger.class, connection.getAttribute(name, "bigint"))); Assert.assertEquals(bigDecimal, assertCast(BigDecimal.class, connection.getAttribute(name, "bigdec"))); Assert.assertEquals(bool, assertCast(Boolean.class, connection.getAttribute(name, "boolean"))); assertEqualByteArray(assertCast(byte[].class, connection.getAttribute(name, "bytes")), bytes); Assert.assertEquals(dbl, assertCast(Double.class, connection.getAttribute(name, "double")), 0.0d); Assert.assertEquals(s, assertCast(String.class, connection.getAttribute(name, "string"))); Integer[] listValue = assertCast(Integer[].class, connection.getAttribute(name, "list")); Assert.assertEquals(list.size(), listValue.length); for (int ctr = 0; ctr < list.size(); ctr++) { Assert.assertEquals(list.get(ctr), listValue[ctr]); } Assert.assertEquals(lng, assertCast(Long.class, connection.getAttribute(name, "long")).longValue()); Assert.assertEquals(type, ModelType.valueOf(assertCast(String.class, connection.getAttribute(name, "type")))); TabularData tabularData = assertCast(TabularData.class, connection.getAttribute(name, "map")); Assert.assertEquals(2, tabularData.size()); Assert.assertEquals(tblValue1, assertCast(Integer.class, tabularData.get(new Object[]{tblKey1}).get("value")).intValue()); Assert.assertEquals(tblValue2, assertCast(Integer.class, tabularData.get(new Object[]{tblKey2}).get("value")).intValue()); } @Test public void testReadWriteAttributeExpressionsStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension(true))); ObjectName name = createObjectName(EXPR_DOMAIN + ":subsystem=test"); checkAttributeValues(connection, name, "1", null, "2", "3", "4", "false", new byte[]{5, 6}, "7.0", "8", Collections.singletonList("9"), "10", ModelType.INT.toString(), "key1", "11", "key2", "12"); Assert.assertNull(connection.getAttribute(name, "complex")); try { connection.setAttribute(name, new Attribute("roInt", 101)); Assert.fail("roInt not writable"); } catch (Exception expected) { //expected } connection.setAttribute(name, new Attribute("int", "${should.not.exist!!!!!:102}")); connection.setAttribute(name, new Attribute("undefinedInt", "${should.not.exist!!!!!:103}")); connection.setAttribute(name, new Attribute("bigint", "${should.not.exist!!!!!:104}")); connection.setAttribute(name, new Attribute("bigdec", "${should.not.exist!!!!!:105}")); connection.setAttribute(name, new Attribute("boolean", "${should.not.exist!!!!!:true}")); connection.setAttribute(name, new Attribute("bytes", new byte[]{106, 107})); connection.setAttribute(name, new Attribute("double", "${should.not.exist!!!!!:108.0}")); connection.setAttribute(name, new Attribute("string", "${should.not.exist!!!!!:109}")); connection.setAttribute(name, new Attribute("list", new String[]{"${should.not.exist!!!!!:110}"})); connection.setAttribute(name, new Attribute("long", "${should.not.exist!!!!!:111}")); connection.setAttribute(name, new Attribute("type", "${should.not.exist!!!!!:STRING}")); Map<String, String> map = new HashMap<String, String>(); map.put("keyA", "${should.not.exist!!!!!:112}"); map.put("keyB", "${should.not.exist!!!!!:113}"); connection.setAttribute(name, new Attribute("map", map)); MBeanInfo info = connection.getMBeanInfo(name); CompositeType complexType = assertCast(CompositeType.class, findAttribute(info.getAttributes(), "complex").getOpenType()); connection.setAttribute(name, new Attribute("complex", createComplexData(connection, complexType, "${should.not.exist!!!!!:1}", "${should.not.exist!!!!!:2.0}"))); checkAttributeValues(connection, name, "1", "${should.not.exist!!!!!:103}", "${should.not.exist!!!!!:102}", "${should.not.exist!!!!!:104}", "${should.not.exist!!!!!:105}", "${should.not.exist!!!!!:true}", new byte[]{106, 107}, "${should.not.exist!!!!!:108.0}", "${should.not.exist!!!!!:109}", Collections.singletonList("${should.not.exist!!!!!:110}"), "${should.not.exist!!!!!:111}", "${should.not.exist!!!!!:STRING}", "keyA", "${should.not.exist!!!!!:112}", "keyB", "${should.not.exist!!!!!:113}"); CompositeData compositeData = assertCast(CompositeData.class, connection.getAttribute(name, "complex")); Assert.assertEquals("${should.not.exist!!!!!:1}", compositeData.get("int-value")); Assert.assertEquals("${should.not.exist!!!!!:2.0}", compositeData.get("bigdecimal-value")); } private void checkAttributeValues(MBeanServerConnection connection, ObjectName name, String roInt, String undefinedInt, String i, String bigInt, String bigDecimal, String bool, byte[] bytes, String dbl, String s, List<String> list, String lng, String type, String tblKey1, String tblValue1, String tblKey2, String tblValue2) throws Exception { Assert.assertEquals(roInt, assertCast(String.class, connection.getAttribute(name, "roInt"))); if (undefinedInt == null) { Assert.assertNull(connection.getAttribute(name, "undefinedInt")); } else { Assert.assertEquals(undefinedInt, assertCast(String.class, connection.getAttribute(name, "undefinedInt"))); } Assert.assertEquals(i, assertCast(String.class, connection.getAttribute(name, "int"))); Assert.assertEquals(bigInt, assertCast(String.class, connection.getAttribute(name, "bigint"))); Assert.assertEquals(bigDecimal, assertCast(String.class, connection.getAttribute(name, "bigdec"))); Assert.assertEquals(bool, assertCast(String.class, connection.getAttribute(name, "boolean"))); assertEqualByteArray(bytes, assertCast(byte[].class, connection.getAttribute(name, "bytes"))); Assert.assertEquals(dbl, assertCast(String.class, connection.getAttribute(name, "double"))); Assert.assertEquals(s, assertCast(String.class, connection.getAttribute(name, "string"))); String[] listValue = assertCast(String[].class, connection.getAttribute(name, "list")); Assert.assertEquals(list.size(), listValue.length); for (int ctr = 0; ctr < list.size(); ctr++) { Assert.assertEquals(list.get(ctr), listValue[ctr]); } Assert.assertEquals(lng, assertCast(String.class, connection.getAttribute(name, "long"))); Assert.assertEquals(type, assertCast(String.class, connection.getAttribute(name, "type"))); TabularData tabularData = assertCast(TabularData.class, connection.getAttribute(name, "map")); Assert.assertEquals(2, tabularData.size()); Assert.assertEquals(tblValue1, assertCast(String.class, tabularData.get(new Object[]{tblKey1}).get("value"))); Assert.assertEquals(tblValue2, assertCast(String.class, tabularData.get(new Object[]{tblKey2}).get("value"))); } @Test public void testReadWriteAttributeListStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); ObjectName name = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); String[] attrNames = new String[]{"roInt", "int", "bigint", "bigdec", "boolean", "bytes", "double", "string", "list", "long", "type"}; AttributeList list = connection.getAttributes(name, attrNames); Assert.assertEquals(list.size(), attrNames.length); checkAttributeList(attrNames, list, 1, 2, BigInteger.valueOf(3), BigDecimal.valueOf(4), false, new byte[]{5, 6}, 7.0, "8", Collections.singletonList(9), 10, ModelType.INT); list = new AttributeList(); list.add(new Attribute("int", 102)); list.add(new Attribute("bigint", BigInteger.valueOf(103))); list.add(new Attribute("bigdec", BigDecimal.valueOf(104))); list.add(new Attribute("boolean", true)); list.add(new Attribute("bytes", new byte[]{105, 106})); list.add(new Attribute("double", 107.0)); list.add(new Attribute("string", "108")); list.add(new Attribute("list", new Integer[]{109})); list.add(new Attribute("long", 110L)); list.add(new Attribute("type", ModelType.STRING.toString())); connection.setAttributes(name, list); list = connection.getAttributes(name, attrNames); checkAttributeList(attrNames, list, 1, 102, BigInteger.valueOf(103), BigDecimal.valueOf(104), true, new byte[]{105, 106}, 107.0, "108", Collections.singletonList(109), 110, ModelType.STRING); } @Test public void testReadWriteAttributeListDomain() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.DOMAIN_SERVER, new TestExtension())); ObjectName name = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); String[] attrNames = new String[]{"roInt", "int", "bigint", "bigdec", "boolean", "bytes", "double", "string", "list", "long", "type"}; AttributeList list = connection.getAttributes(name, attrNames); Assert.assertEquals(list.size(), attrNames.length); checkAttributeList(attrNames, list, 1, 2, BigInteger.valueOf(3), BigDecimal.valueOf(4), false, new byte[]{5, 6}, 7.0, "8", Collections.singletonList(9), 10, ModelType.INT); list = new AttributeList(); try { list.add(new Attribute("int", 102)); //TODO BigInteger not working in current DMR version //list.add(new Attribute("bigint", BigInteger.valueOf(103))); list.add(new Attribute("bigdec", BigDecimal.valueOf(104))); list.add(new Attribute("boolean", true)); list.add(new Attribute("bytes", new byte[]{105, 106})); list.add(new Attribute("double", 107.0)); list.add(new Attribute("string", "108")); list.add(new Attribute("list", new Integer[]{109})); list.add(new Attribute("long", 110L)); list.add(new Attribute("type", ModelType.STRING.toString())); connection.setAttributes(name, list); Assert.fail("Should not have been able to set attributes"); } catch (Exception expected) { //expected } list = connection.getAttributes(name, attrNames); checkAttributeList(attrNames, list, 1, 2, BigInteger.valueOf(3), BigDecimal.valueOf(4), false, new byte[]{5, 6}, 7.0, "8", Collections.singletonList(9), 10, ModelType.INT); } private void checkAttributeList(String[] attrNames, AttributeList list, int roInt, int i, BigInteger bi, BigDecimal bd, boolean b, byte[] bytes, double d, String s, List<Integer> lst, long l, ModelType type) { Assert.assertEquals(list.size(), attrNames.length); Assert.assertEquals(roInt, assertGetFromList(Integer.class, list, "roInt").intValue()); Assert.assertEquals(i, assertGetFromList(Integer.class, list, "int").intValue()); Assert.assertEquals(bi, assertGetFromList(BigInteger.class, list, "bigint")); Assert.assertEquals(bd, assertGetFromList(BigDecimal.class, list, "bigdec")); Assert.assertEquals(b, assertGetFromList(Boolean.class, list, "boolean")); assertEqualByteArray(assertGetFromList(byte[].class, list, "bytes"), bytes); Assert.assertEquals(d, assertGetFromList(Double.class, list, "double"), 0.0d); Assert.assertEquals(s, assertGetFromList(String.class, list, "string")); Integer[] listValue = assertGetFromList(Integer[].class, list, "list"); Assert.assertEquals(lst.size(), listValue.length); for (int ctr = 0; ctr < lst.size(); ctr++) { Assert.assertEquals(lst.get(ctr), listValue[ctr]); } Assert.assertEquals(l, assertGetFromList(Long.class, list, "long").longValue()); Assert.assertEquals(type, ModelType.valueOf(assertGetFromList(String.class, list, "type"))); } @Test public void testReadWriteAttributeListExpressionsStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension(true))); ObjectName name = createObjectName(EXPR_DOMAIN + ":subsystem=test"); String[] attrNames = new String[]{"roInt", "int", "bigint", "bigdec", "boolean", "bytes", "double", "string", "list", "long", "type"}; AttributeList list = connection.getAttributes(name, attrNames); Assert.assertEquals(list.size(), attrNames.length); checkAttributeList(attrNames, list, "1", "2", "3", "4", "false", new byte[]{5, 6}, "7.0", "8", Collections.singletonList("9"), "10", "INT"); list = new AttributeList(); list.add(new Attribute("int", "${should.not.exist!!!!!:102}")); list.add(new Attribute("bigint", "${should.not.exist!!!!!:103}")); list.add(new Attribute("bigdec", "${should.not.exist!!!!!:104}")); list.add(new Attribute("boolean", "${should.not.exist!!!!!:true}")); list.add(new Attribute("bytes", new byte[]{105, 106})); list.add(new Attribute("double", "${should.not.exist!!!!!:107.0}")); list.add(new Attribute("string", "${should.not.exist!!!!!:108}")); list.add(new Attribute("list", new String[]{"${should.not.exist!!!!!:109}"})); list.add(new Attribute("long", "${should.not.exist!!!!!:110L}")); list.add(new Attribute("type", "${should.not.exist!!!!!:STRING}")); connection.setAttributes(name, list); list = connection.getAttributes(name, attrNames); checkAttributeList(attrNames, list, "1", "${should.not.exist!!!!!:102}", "${should.not.exist!!!!!:103}", "${should.not.exist!!!!!:104}", "${should.not.exist!!!!!:true}", new byte[]{105, 106}, "${should.not.exist!!!!!:107.0}", "${should.not.exist!!!!!:108}", Collections.singletonList("${should.not.exist!!!!!:109}"), "${should.not.exist!!!!!:110L}", "${should.not.exist!!!!!:STRING}"); } private void checkAttributeList(String[] attrNames, AttributeList list, String roInt, String i, String bi, String bd, String b, byte[] bytes, String d, String s, List<String> lst, String l, String type) { Assert.assertEquals(list.size(), attrNames.length); Assert.assertEquals(roInt, assertGetFromList(String.class, list, "roInt")); Assert.assertEquals(i, assertGetFromList(String.class, list, "int")); Assert.assertEquals(bi, assertGetFromList(String.class, list, "bigint")); Assert.assertEquals(bd, assertGetFromList(String.class, list, "bigdec")); Assert.assertEquals(b, assertGetFromList(String.class, list, "boolean")); assertEqualByteArray(assertGetFromList(byte[].class, list, "bytes"), bytes); Assert.assertEquals(d, assertGetFromList(String.class, list, "double")); Assert.assertEquals(s, assertGetFromList(String.class, list, "string")); String[] listValue = assertGetFromList(String[].class, list, "list"); Assert.assertEquals(lst.size(), listValue.length); for (int ctr = 0; ctr < lst.size(); ctr++) { Assert.assertEquals(lst.get(ctr), listValue[ctr]); } Assert.assertEquals(l, assertGetFromList(String.class, list, "long")); Assert.assertEquals(type, assertGetFromList(String.class, list, "type")); } @Test public void testInvokeOperationStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); ObjectName name = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); ModelControllerResourceDefinition.VoidOperationNoParams.INSTANCE.invoked = false; Assert.assertNull(connection.invoke(name, ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME, null, null)); Assert.assertTrue(ModelControllerResourceDefinition.VoidOperationNoParams.INSTANCE.invoked); String result = assertCast(String.class, connection.invoke( name, ModelControllerResourceDefinition.IntOperationWithParams.OPERATION_JMX_NAME, new Object[]{100L, new String[]{"A"}, Collections.singletonMap("test", 3), 5, 5}, new String[]{Long.class.getName(), String[].class.getName(), Map.class.getName(), Integer.class.getName(), Integer.class.getName()})); Assert.assertEquals("A105", result); Assert.assertTrue(ModelControllerResourceDefinition.IntOperationWithParams.INSTANCE_NO_EXPRESSIONS.invoked); MBeanInfo info = connection.getMBeanInfo(name); CompositeType complexType = assertCast(CompositeType.class, findAttribute(info.getAttributes(), "complex").getOpenType()); CompositeData complexData = createComplexData(connection, complexType, 5, BigDecimal.valueOf(10.3d)); Assert.assertEquals(complexData, assertCast(CompositeData.class, connection.invoke( name, ModelControllerResourceDefinition.ComplexOperation.OPERATION_NAME, new Object[]{complexData}, new String[]{CompositeData.class.getName()}))); } @Test public void testInvokeOperationDomain() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.DOMAIN_SERVER, new TestExtension())); ObjectName name = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); ModelControllerResourceDefinition.VoidOperationNoParams.INSTANCE.invoked = false; Assert.assertNull(connection.invoke(name, ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME, new Object[0], new String[0])); Assert.assertTrue(ModelControllerResourceDefinition.VoidOperationNoParams.INSTANCE.invoked); connection.invoke( name, ModelControllerResourceDefinition.IntOperationWithParams.OPERATION_JMX_NAME, new Object[]{100L, new String[]{"A"}, Collections.singletonMap("test", 3), 5, 5}, new String[]{Long.class.getName(), String[].class.getName(), Map.class.getName(), Integer.class.getName(), Integer.class.getName()}); } @Test public void testInvokeOperationExpressionsStandalone() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension(true))); ObjectName name = createObjectName(EXPR_DOMAIN + ":subsystem=test"); ModelControllerResourceDefinition.VoidOperationNoParams.INSTANCE.invoked = false; Assert.assertNull(connection.invoke(name, ModelControllerResourceDefinition.VoidOperationNoParams.OPERATION_JMX_NAME, null, null)); Assert.assertTrue(ModelControllerResourceDefinition.VoidOperationNoParams.INSTANCE.invoked); String result = assertCast(String.class, connection.invoke( name, ModelControllerResourceDefinition.IntOperationWithParams.OPERATION_JMX_NAME, new Object[]{"${should.not.exist!!!!!:100}", new String[]{"${should.not.exist!!!!!:A}"}, Collections.singletonMap("test", "${should.not.exist!!!!!:3}"), "${should.not.exist!!!!!:5}", "${should.not.exist!!!!!:5}"}, new String[]{Long.class.getName(), String[].class.getName(), Map.class.getName(), Integer.class.getName(), Integer.class.getName()})); Assert.assertEquals("A105", result); Assert.assertTrue(ModelControllerResourceDefinition.IntOperationWithParams.INSTANCE_EXPRESSIONS.invoked); MBeanInfo info = connection.getMBeanInfo(name); CompositeType complexType = assertCast(CompositeType.class, findAttribute(info.getAttributes(), "complex").getOpenType()); CompositeData complexData = createComplexData(connection, complexType, "${should.not.exist!!!!!:5}", "${should.not.exist!!!!!:10}"); Assert.assertEquals(complexData, assertCast(CompositeData.class, connection.invoke( name, ModelControllerResourceDefinition.ComplexOperation.OPERATION_NAME, new Object[]{complexData}, new String[]{CompositeData.class.getName()}))); } @Test public void testAddMethodSingleFixedChild() throws Exception { final ObjectName testObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); final ObjectName childObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test,single=only"); MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); Set<ObjectName> names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(1, names.size()); Assert.assertTrue(names.contains(testObjectName)); MBeanInfo subsystemInfo = connection.getMBeanInfo(testObjectName); Assert.assertEquals(0, subsystemInfo.getAttributes().length); Assert.assertEquals(1, subsystemInfo.getOperations().length); OpenMBeanOperationInfo op = findOperation(subsystemInfo.getOperations(), "addSingleOnly"); Assert.assertEquals("add", op.getDescription()); Assert.assertEquals(1, op.getSignature().length); Assert.assertEquals(Integer.class.getName(), op.getSignature()[0].getType()); connection.invoke(testObjectName, "addSingleOnly", new Object[]{123}, new String[]{String.class.getName()}); names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(2, names.size()); Assert.assertTrue(names.contains(testObjectName)); Assert.assertTrue(names.contains(childObjectName)); subsystemInfo = connection.getMBeanInfo(testObjectName); Assert.assertEquals(0, subsystemInfo.getAttributes().length); Assert.assertEquals(1, subsystemInfo.getOperations().length); op = findOperation(subsystemInfo.getOperations(), "addSingleOnly"); Assert.assertEquals("add", op.getDescription()); Assert.assertEquals(1, op.getSignature().length); Assert.assertEquals(Integer.class.getName(), op.getSignature()[0].getType()); MBeanInfo childInfo = connection.getMBeanInfo(childObjectName); Assert.assertEquals(1, childInfo.getAttributes().length); Assert.assertEquals(Integer.class.getName(), childInfo.getAttributes()[0].getType()); Assert.assertEquals(1, childInfo.getOperations().length); op = findOperation(childInfo.getOperations(), REMOVE); Assert.assertEquals("remove", op.getDescription()); Assert.assertEquals(0, op.getSignature().length); Assert.assertEquals(123, connection.getAttribute(childObjectName, "attr")); try { connection.invoke(testObjectName, "addSingleOnly", new Object[]{123}, new String[]{String.class.getName()}); Assert.fail("Should not have been able to register a duplicate resource"); } catch (Exception expected) { //expected } connection.invoke(childObjectName, REMOVE, new Object[]{}, new String[]{}); names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(1, names.size()); Assert.assertTrue(names.contains(testObjectName)); } @Test public void testAddMethodSiblingChildren() throws Exception { final ObjectName testObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); final ObjectName child1ObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test,siblings=test1"); final ObjectName child2ObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test,siblings=test2"); MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSiblingChildrenChildExtension())); Set<ObjectName> names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(1, names.size()); Assert.assertTrue(names.contains(testObjectName)); MBeanInfo subsystemInfo = connection.getMBeanInfo(testObjectName); Assert.assertEquals(0, subsystemInfo.getAttributes().length); Assert.assertEquals(1, subsystemInfo.getOperations().length); OpenMBeanOperationInfo op = findOperation(subsystemInfo.getOperations(), "addSiblings"); Assert.assertEquals("add", op.getDescription()); Assert.assertEquals(2, op.getSignature().length); Assert.assertEquals(String.class.getName(), op.getSignature()[0].getType()); Assert.assertEquals(Integer.class.getName(), op.getSignature()[1].getType()); connection.invoke(testObjectName, "addSiblings", new Object[]{"test1", 123}, new String[]{String.class.getName(), String.class.getName()}); names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(2, names.size()); Assert.assertTrue(names.contains(testObjectName)); Assert.assertTrue(names.contains(child1ObjectName)); subsystemInfo = connection.getMBeanInfo(testObjectName); Assert.assertEquals(0, subsystemInfo.getAttributes().length); Assert.assertEquals(1, subsystemInfo.getOperations().length); op = findOperation(subsystemInfo.getOperations(), "addSiblings"); Assert.assertEquals("add", op.getDescription()); Assert.assertEquals(2, op.getSignature().length); Assert.assertEquals(String.class.getName(), op.getSignature()[0].getType()); Assert.assertEquals(Integer.class.getName(), op.getSignature()[1].getType()); MBeanInfo childInfo = connection.getMBeanInfo(child1ObjectName); Assert.assertEquals(1, childInfo.getAttributes().length); Assert.assertEquals(Integer.class.getName(), childInfo.getAttributes()[0].getType()); Assert.assertEquals(1, childInfo.getOperations().length); op = findOperation(childInfo.getOperations(), REMOVE); Assert.assertEquals("remove", op.getDescription()); Assert.assertEquals(0, op.getSignature().length); connection.invoke(testObjectName, "addSiblings", new Object[]{"test2", 456}, new String[]{String.class.getName(), String.class.getName()}); names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(3, names.size()); Assert.assertTrue(names.contains(testObjectName)); Assert.assertTrue(names.contains(child1ObjectName)); Assert.assertTrue(names.contains(child2ObjectName)); subsystemInfo = connection.getMBeanInfo(testObjectName); Assert.assertEquals(0, subsystemInfo.getAttributes().length); Assert.assertEquals(1, subsystemInfo.getOperations().length); op = findOperation(subsystemInfo.getOperations(), "addSiblings"); Assert.assertEquals("add", op.getDescription()); Assert.assertEquals(2, op.getSignature().length); Assert.assertEquals(String.class.getName(), op.getSignature()[0].getType()); Assert.assertEquals(Integer.class.getName(), op.getSignature()[1].getType()); childInfo = connection.getMBeanInfo(child1ObjectName); Assert.assertEquals(1, childInfo.getAttributes().length); Assert.assertEquals(Integer.class.getName(), childInfo.getAttributes()[0].getType()); Assert.assertEquals(1, childInfo.getOperations().length); op = findOperation(childInfo.getOperations(), REMOVE); Assert.assertEquals("remove", op.getDescription()); Assert.assertEquals(0, op.getSignature().length); childInfo = connection.getMBeanInfo(child2ObjectName); Assert.assertEquals(1, childInfo.getAttributes().length); Assert.assertEquals(Integer.class.getName(), childInfo.getAttributes()[0].getType()); Assert.assertEquals(1, childInfo.getOperations().length); op = findOperation(childInfo.getOperations(), REMOVE); Assert.assertEquals("remove", op.getDescription()); Assert.assertEquals(0, op.getSignature().length); Assert.assertEquals(123, connection.getAttribute(child1ObjectName, "attr")); Assert.assertEquals(456, connection.getAttribute(child2ObjectName, "attr")); connection.invoke(child1ObjectName, REMOVE, new Object[]{}, new String[]{}); names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(2, names.size()); Assert.assertTrue(names.contains(testObjectName)); Assert.assertTrue(names.contains(child2ObjectName)); connection.invoke(child2ObjectName, REMOVE, new Object[]{}, new String[]{}); names = connection.queryNames(createObjectName(LEGACY_DOMAIN + ":subsystem=test,*"), null); Assert.assertEquals(1, names.size()); Assert.assertTrue(names.contains(testObjectName)); } @Test public void testResolveExpressions() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new BaseAdditionalInitialization(ProcessType.STANDALONE_SERVER)); System.clearProperty("jboss.test.resolve.expressions.test"); Assert.assertEquals("123", connection.invoke(LEGACY_ROOT_NAME, "resolveExpression", new String[]{"${jboss.test.resolve.expressions.test:123}"}, new String[]{String.class.getName()})); try { connection.invoke(LEGACY_ROOT_NAME, "resolveExpression", new String[]{"${jboss.test.resolve.expressions.test}"}, new String[]{String.class.getName()}); Assert.fail("Should not have been able to resolve non-existent property"); } catch (Exception expected) { //expected } } @Test public void testAttributeChangeNotificationUsingMSc() throws Exception { KernelServices kernelServices = setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); ServiceController<?> service = kernelServices.getContainer().getService(MBeanServerService.SERVICE_NAME); MBeanServer mbeanServer = MBeanServer.class.cast(service.getValue()); doTestAttributeChangeNotification(mbeanServer, true); } @Test public void testAttributeChangeNotificationUsingManagementFactory() throws Exception { setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); doTestAttributeChangeNotification(mbeanServer, true); } @Test public void testAttributeChangeNotificationUsingRemoteJMXConnector() throws Exception { setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); JMXConnectorServer connectorServer = startLocalConnectorServer(ManagementFactory.getPlatformMBeanServer()); JMXConnector connector = JMXConnectorFactory.connect(connectorServer.getAddress()); try { doTestAttributeChangeNotification(connector.getMBeanServerConnection(), true); } finally { connector.close(); connectorServer.stop(); } } @Test public void testAttributeChangeNotificationUsingRemotingConnector() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new TestExtension())); doTestAttributeChangeNotification(connection, false); } private void doTestAttributeChangeNotification(MBeanServerConnection connection, boolean notificationListenerOperationsMustSucceed) throws Exception { ObjectName name = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); final CountDownLatch notificationEmitted = new CountDownLatch(1); final AtomicReference<Notification> notification = new AtomicReference<>(); NotificationListener listener = new NotificationListener() { @Override public void handleNotification(javax.management.Notification notif, Object handback) { notification.set(notif); notificationEmitted.countDown(); } }; NotificationFilterSupport filter = new NotificationFilterSupport(); filter.enableType(AttributeChangeNotification.ATTRIBUTE_CHANGE); try { connection.addNotificationListener(name, listener, filter, null); if (!notificationListenerOperationsMustSucceed) { Assert.fail("Adding the notification listener must fail"); } } catch (IOException | RuntimeOperationsException e) { if (notificationListenerOperationsMustSucceed) { Assert.fail("Unexpected exception when adding the notification listener"); } else { Assert.assertTrue(e.getCause() instanceof UnsupportedOperationException || e.getCause().getCause() instanceof UnsupportedOperationException); } } connection.setAttribute(name, new Attribute("int", 102)); if (notificationListenerOperationsMustSucceed) { Assert.assertTrue("Did not receive expected notification", notificationEmitted.await(1, TimeUnit.SECONDS)); Notification notif = notification.get(); Assert.assertTrue(notif instanceof AttributeChangeNotification); AttributeChangeNotification attributeChangeNotification = (AttributeChangeNotification) notif; Assert.assertEquals(AttributeChangeNotification.ATTRIBUTE_CHANGE, attributeChangeNotification.getType()); Assert.assertEquals(name, attributeChangeNotification.getSource()); Assert.assertEquals(Integer.class.getName(), attributeChangeNotification.getAttributeType()); Assert.assertEquals("int", attributeChangeNotification.getAttributeName()); Assert.assertEquals(2, attributeChangeNotification.getOldValue()); Assert.assertEquals(102, attributeChangeNotification.getNewValue()); } else { Assert.assertFalse("Did receive unexpected notification", notificationEmitted.await(500, TimeUnit.MILLISECONDS)); } connection.removeNotificationListener(name, listener, filter, null); } @Test public void testMBeanServerNotification_REGISTRATION_NOTIFICATIONUsingMsc() throws Exception { KernelServices kernelServices = setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); ServiceController<?> service = kernelServices.getContainer().getService(MBeanServerService.SERVICE_NAME); MBeanServer mbeanServer = MBeanServer.class.cast(service.getValue()); doTestMBeanServerNotification_REGISTRATION_NOTIFICATION(mbeanServer, true); } @Test public void testMBeanServerNotification_REGISTRATION_NOTIFICATIONUsingManagementFactory() throws Exception { setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); doTestMBeanServerNotification_REGISTRATION_NOTIFICATION(mbeanServer, true); } @Test public void testMBeanServerNotification_REGISTRATION_NOTIFICATIONUsingRemotingConnector() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); doTestMBeanServerNotification_REGISTRATION_NOTIFICATION(connection, false); } @Test public void testMBeanServerNotification_REGISTRATION_NOTIFICATIONUsingRemoteJMXConnector() throws Exception { setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); JMXConnectorServer connectorServer = startLocalConnectorServer(ManagementFactory.getPlatformMBeanServer()); JMXConnector connector = JMXConnectorFactory.connect(connectorServer.getAddress()); try { doTestMBeanServerNotification_REGISTRATION_NOTIFICATION(connector.getMBeanServerConnection(), true); } finally { connector.close(); connectorServer.stop(); } } public void doTestMBeanServerNotification_REGISTRATION_NOTIFICATION(MBeanServerConnection connection, boolean mustReceiveNotification) throws Exception { final ObjectName testObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); final ObjectName childObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test,single=only"); final CountDownLatch notificationEmitted = new CountDownLatch(1); final AtomicReference<Notification> notification = new AtomicReference<>(); NotificationListener listener = new MbeanServerNotificationListener(notification, notificationEmitted, LEGACY_DOMAIN); NotificationFilterSupport filter = new NotificationFilterSupport(); filter.enableType(MBeanServerNotification.REGISTRATION_NOTIFICATION); connection.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, listener, filter, null); // add a management resource connection.invoke(testObjectName, "addSingleOnly", new Object[]{123}, new String[]{String.class.getName()}); if (mustReceiveNotification) { Assert.assertTrue("Did not receive expected notification", notificationEmitted.await(1, TimeUnit.SECONDS)); Notification notif = notification.get(); Assert.assertNotNull(notif); Assert.assertTrue(notif instanceof MBeanServerNotification); MBeanServerNotification mBeanServerNotification = (MBeanServerNotification) notif; Assert.assertEquals(MBeanServerNotification.REGISTRATION_NOTIFICATION, notif.getType()); Assert.assertEquals(childObjectName, mBeanServerNotification.getMBeanName()); } else { Assert.assertFalse("Did receive unexpected notification", notificationEmitted.await(500, TimeUnit.MILLISECONDS)); } connection.removeNotificationListener(MBeanServerDelegate.DELEGATE_NAME, listener, filter, null); } @Test public void testMBeanServerNotification_UNREGISTRATION_NOTIFICATIONUsingMsc() throws Exception { KernelServices kernelServices = setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); ServiceController<?> service = kernelServices.getContainer().getService(MBeanServerService.SERVICE_NAME); MBeanServer mBeanServer = MBeanServer.class.cast(service.getValue()); doTestMBeanServerNotification_UNREGISTRATION_NOTIFICATION(mBeanServer, true); } @Test public void testMBeanServerNotification_UNREGISTRATION_NOTIFICATIONUsingManagementFactory() throws Exception { setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); doTestMBeanServerNotification_UNREGISTRATION_NOTIFICATION(mbeanServer, true); } @Test public void testMBeanServerNotification_UNREGISTRATION_NOTIFICATIONUsingRemotingConnector() throws Exception { MBeanServerConnection connection = setupAndGetConnection(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); doTestMBeanServerNotification_UNREGISTRATION_NOTIFICATION(connection, false); } @Test public void testMBeanServerNotification_UNREGISTRATION_NOTIFICATIONUsingRemoteJMXConnector() throws Exception { setup(new MBeanInfoAdditionalInitialization(ProcessType.STANDALONE_SERVER, new SubystemWithSingleFixedChildExtension())); JMXConnectorServer connectorServer = startLocalConnectorServer(ManagementFactory.getPlatformMBeanServer()); JMXConnector connector = JMXConnectorFactory.connect(connectorServer.getAddress()); try { doTestMBeanServerNotification_UNREGISTRATION_NOTIFICATION(connector.getMBeanServerConnection(), true); } finally { connector.close(); connectorServer.stop(); } } private void doTestMBeanServerNotification_UNREGISTRATION_NOTIFICATION(MBeanServerConnection connection, boolean mustReceiveNotification) throws Exception { final ObjectName testObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test"); final ObjectName childObjectName = createObjectName(LEGACY_DOMAIN + ":subsystem=test,single=only"); final CountDownLatch notificationEmitted = new CountDownLatch(1); final AtomicReference<Notification> notification = new AtomicReference<>(); NotificationListener listener = new MbeanServerNotificationListener(notification, notificationEmitted, LEGACY_DOMAIN); NotificationFilterSupport filter = new NotificationFilterSupport(); filter.enableType(MBeanServerNotification.UNREGISTRATION_NOTIFICATION); connection.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, listener, filter, null); // add a management resource connection.invoke(testObjectName, "addSingleOnly", new Object[]{123}, new String[]{String.class.getName()}); // and remove it connection.invoke(childObjectName, "remove", new Object[0], new String[0]); if (mustReceiveNotification) { Assert.assertTrue("Did not receive expected notification", notificationEmitted.await(1, TimeUnit.SECONDS)); Notification notif = notification.get(); Assert.assertNotNull(notif); Assert.assertTrue(notif instanceof MBeanServerNotification); MBeanServerNotification mBeanServerNotification = (MBeanServerNotification) notif; Assert.assertEquals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION, mBeanServerNotification.getType()); Assert.assertEquals(childObjectName, mBeanServerNotification.getMBeanName()); } else { Assert.assertFalse("Did receive unexpected notification", notificationEmitted.await(500, TimeUnit.MILLISECONDS)); } connection.removeNotificationListener(MBeanServerDelegate.DELEGATE_NAME, listener, filter, null); } private OpenMBeanOperationInfo findOperation(MBeanOperationInfo[] ops, String name) { for (MBeanOperationInfo op : ops) { Assert.assertNotNull(op.getName()); if (op.getName().equals(name)) { return assertCast(OpenMBeanOperationInfo.class, op); } } Assert.fail("No op called " + name); return null; } private OpenMBeanAttributeInfo findAttribute(MBeanAttributeInfo[] attrs, String name) { for (MBeanAttributeInfo attr : attrs) { Assert.assertNotNull(attr.getName()); if (attr.getName().equals(name)) { return assertCast(OpenMBeanAttributeInfo.class, attr); } } Assert.fail("No attr called " + name); return null; } private void assertEqualByteArray(byte[] bytes, byte... expected) { Assert.assertEquals(expected.length, bytes.length); for (int i = 0; i < bytes.length; i++) { Assert.assertEquals(expected[i], bytes[i]); } } private CompositeData createComplexData(MBeanServerConnection connection, CompositeType type, int intValue, BigDecimal bigDecimalValue) throws Exception { Map<String, Object> items = new HashMap<String, Object>(); items.put("int-value", intValue); //items.put("bigint-value", bigIntegerValue); items.put("bigdecimal-value", bigDecimalValue); CompositeDataSupport data = new CompositeDataSupport(type, items); return data; } private CompositeData createComplexData(MBeanServerConnection connection, CompositeType type, String intValue, String bigDecimalValue) throws Exception { Map<String, Object> items = new HashMap<String, Object>(); items.put("int-value", intValue); //items.put("bigint-value", bigIntegerValue); items.put("bigdecimal-value", bigDecimalValue); CompositeDataSupport data = new CompositeDataSupport(type, items); return data; } private void assertMapType(OpenType<?> mapType, OpenType<?> keyType, OpenType<?> valueType) { TabularType type = assertCast(TabularType.class, mapType); Assert.assertEquals(1, type.getIndexNames().size()); Assert.assertEquals("key", type.getIndexNames().get(0)); Assert.assertEquals(2, type.getRowType().keySet().size()); Assert.assertTrue(type.getRowType().keySet().contains("key")); Assert.assertTrue(type.getRowType().keySet().contains("value")); Assert.assertEquals(keyType, type.getRowType().getType("key")); Assert.assertEquals(valueType, type.getRowType().getType("value")); } private void checkComplexTypeInfo(CompositeType composite, boolean expressions, String prefix) { Set<String> keys = composite.keySet(); Assert.assertEquals(2, keys.size()); assertCompositeType(composite, "int-value", expressions ? String.class.getName() : Integer.class.getName(), (prefix != null ? prefix : "") + "int-value"); assertCompositeType(composite, "bigdecimal-value", expressions ? String.class.getName() : BigDecimal.class.getName(), (prefix != null ? prefix : "") + "bigdecimal-value"); } private void assertAttributeDescription(MBeanAttributeInfo attribute, String name, String type, String description, boolean readable, boolean writable) { Assert.assertEquals(name, attribute.getName()); Assert.assertEquals(description, attribute.getDescription()); Assert.assertEquals(type, attribute.getType()); Assert.assertEquals(readable, attribute.isReadable()); Assert.assertEquals(writable, attribute.isWritable()); } private OpenType<?> assertCompositeType(CompositeType composite, String name, String type, String description) { return assertCompositeType(composite, name, type, description, true); } private OpenType<?> assertCompositeType(CompositeType composite, String name, String type, String description, boolean validateType) { Assert.assertTrue(composite.keySet().contains(name)); if (validateType) { Assert.assertEquals(type, composite.getType(name).getTypeName()); } Assert.assertEquals(description, composite.getDescription(name)); return composite.getType(name); } private static <T> T assertGetFromList(Class<T> clazz, AttributeList list, String name) { Object value = null; List<javax.management.Attribute> attrs = list.asList(); for (Attribute attr : attrs) { if (attr.getName().equals(name)) { value = attr.getValue(); break; } } Assert.assertNotNull(value); return assertCast(clazz, value); } private static <T> T assertCast(Class<T> clazz, Object value) { Assert.assertTrue("value " + value.getClass().getName() + " can not be changed to a " + clazz.getName(), clazz.isAssignableFrom(value.getClass())); return clazz.cast(value); } private void checkSameMBeans(Set<ObjectInstance> instances, Set<ObjectName> objectNames) { for (ObjectInstance instance : instances) { Assert.assertTrue("Does not contain " + instance.getObjectName(), objectNames.contains(instance.getObjectName())); } } private void assertContainsNames(Set<ObjectName> objectNames, ObjectName... expected) { for (ObjectName name : expected) { Assert.assertTrue("Does not contain " + name, objectNames.contains(name)); } } private static class MbeanServerNotificationListener implements NotificationListener { private final AtomicReference<Notification> notification; private final CountDownLatch latch; private final String domain; private MbeanServerNotificationListener(AtomicReference<Notification> notification, CountDownLatch latch, String domain) { this.notification = notification; this.latch = latch; this.domain = domain; } @Override public void handleNotification(Notification notification, Object handback) { if (notification instanceof MBeanServerNotification) { MBeanServerNotification notif = (MBeanServerNotification) notification; if (notif.getMBeanName().getDomain().equals(domain)) { this.notification.set(notification); this.latch.countDown(); } } } } /* * Start a local RMI Server (similar to what the Attach API does when connecting locally using jconsole) */ private static JMXConnectorServer startLocalConnectorServer(MBeanServer mBeanServer) throws IOException { JMXServiceURL serviceURL = new JMXServiceURL("service:jmx:rmi://localhost"); JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(serviceURL, new HashMap<String, String>(), mBeanServer); connectorServer.start(); return connectorServer; } private KernelServices setup(BaseAdditionalInitialization additionalInitialization) throws Exception { // Parse the subsystem xml and install into the controller String subsystemXml = "<subsystem xmlns=\"" + Namespace.CURRENT.getUriString() + "\">" + "<expose-resolved-model domain-name=\"jboss.resolved\"/>" + "<expose-expression-model/>" + "<remoting-connector/>" + "</subsystem>" + additionalInitialization.getExtraXml(); KernelServices kernelServices = createKernelServicesBuilder(additionalInitialization).setSubsystemXml(subsystemXml).build(); return kernelServices; } private MBeanServerConnection getRemoteConnection() throws Exception { Assert.assertNull(jmxConnector); // Make sure that we can connect to the MBean server String host = "localhost"; String urlString = System .getProperty("jmx.service.url", "service:jmx:remoting-jmx://" + host + ":" + JMX_PORT); JMXServiceURL serviceURL = new JMXServiceURL(urlString); // TODO this is horrible - for some reason after the first test the // second time we // start the JMX connector it takes time for it to appear long end = System.currentTimeMillis() + 10000; while (true) { try { JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceURL, null); this.jmxConnector = jmxConnector; return jmxConnector.getMBeanServerConnection(); } catch (Exception e) { if (System.currentTimeMillis() >= end) { throw new RuntimeException(e); } Thread.sleep(50); } } } private MBeanServerConnection setupAndGetConnection(BaseAdditionalInitialization additionalInitialization) throws Exception { setup(additionalInitialization); return getRemoteConnection(); } private static ObjectName createObjectName(String s) { try { return new ObjectName(s); } catch (MalformedObjectNameException e) { throw new RuntimeException(e); } } private static class BaseAdditionalInitialization extends AdditionalInitialization { final ProcessType processType; public BaseAdditionalInitialization(ProcessType processType) { assert processType.isServer(); this.processType = processType; } @Override protected void initializeExtraSubystemsAndModel(ExtensionRegistry extensionRegistry, Resource rootResource, ManagementResourceRegistration rootRegistration, RuntimeCapabilityRegistry capabilityRegistry) { rootRegistration.registerOperationHandler(ResolveExpressionHandler.DEFINITION, ResolveExpressionHandler.INSTANCE); AdditionalInitialization.registerCapabilities(capabilityRegistry, RemotingConnectorResource.REMOTING_CAPABILITY); } @Override protected void setupController(ControllerInitializer controllerInitializer) { controllerInitializer.addSocketBinding("server", JMX_PORT); controllerInitializer.addPath("jboss.controller.temp.dir", System.getProperty("java.io.tmpdir"), null); } @Override protected void addExtraServices(final ServiceTarget target) { ManagementRemotingServices.installRemotingManagementEndpoint(target, ManagementRemotingServices.MANAGEMENT_ENDPOINT, "localhost", EndpointService.EndpointType.MANAGEMENT); ServiceName tmpDirPath = ServiceName.JBOSS.append("server", "path", "jboss.controller.temp.dir"); RemotingServices.installConnectorServicesForSocketBinding(target, ManagementRemotingServices.MANAGEMENT_ENDPOINT, "server", SocketBinding.JBOSS_BINDING_NAME.append("server"), OptionMap.EMPTY, null, null, null); } @Override protected ProcessType getProcessType() { return processType; } String getExtraXml() { return ""; } } private static class MBeanInfoAdditionalInitialization extends BaseAdditionalInitialization { private final Extension extension; public MBeanInfoAdditionalInitialization(ProcessType processType, Extension extension) { super(processType); this.extension = extension; } @Override protected void addParsers(ExtensionRegistry extensionRegistry, XMLMapper xmlMapper) { extension.initializeParsers(extensionRegistry.getExtensionParsingContext("additional", xmlMapper)); } @Override protected void initializeExtraSubystemsAndModel(ExtensionRegistry extensionRegistry, Resource rootResource, ManagementResourceRegistration rootRegistration, RuntimeCapabilityRegistry capabilityRegistry) { super.initializeExtraSubystemsAndModel(extensionRegistry, rootResource, rootRegistration, capabilityRegistry); extension.initialize(extensionRegistry.getExtensionContext("additional", rootRegistration, ExtensionRegistryType.SLAVE)); } @Override String getExtraXml() { return "<subsystem xmlns=\"" + TestExtension.NAMESPACE + "\"/>"; } } static class SubystemWithSingleFixedChildExtension extends SubsystemWithChildrenExtension { @Override PathElement getChildElement() { return PathElement.pathElement("single", "only"); } } static class SubystemWithSiblingChildrenChildExtension extends SubsystemWithChildrenExtension { @Override PathElement getChildElement() { return PathElement.pathElement("siblings"); } } }