From 370cab15d79f23d56b9fdb83ae46fb3e773c1305 Mon Sep 17 00:00:00 2001 From: Brian Stansberry Date: Thu, 25 Apr 2024 17:28:09 -0500 Subject: [PATCH] [WFLY-19218] Ensure that service mbeans are registered with the mbean server through the whole Service lifecycle of their dependents --- .../org/jboss/as/service/MBeanServices.java | 2 +- .../sar/order/CustomLifecycleEmitter.java | 110 ++++++++++ .../order/CustomLifecycleEmitterMBean.java | 9 + .../sar/order/LifecycleEmitter.java | 78 +++++-- .../sar/order/LifecycleEmitterMBean.java | 41 ++++ .../sar/order/LifecycleListener.java | 40 +--- .../sar/order/LifecycleListenerMBean.java | 39 +++- .../order/ServiceMBeanOrderingTestCase.java | 199 ++++++++++++++++++ .../ServiceMBeanSupportOrderingTestCase.java | 108 ---------- .../integration/sar/order/jboss-service.xml | 14 ++ 10 files changed, 476 insertions(+), 164 deletions(-) create mode 100644 testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitter.java create mode 100644 testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitterMBean.java create mode 100644 testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanOrderingTestCase.java delete mode 100644 testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanSupportOrderingTestCase.java diff --git a/sar/src/main/java/org/jboss/as/service/MBeanServices.java b/sar/src/main/java/org/jboss/as/service/MBeanServices.java index cd384cc7363d..abd2e6ca4fad 100644 --- a/sar/src/main/java/org/jboss/as/service/MBeanServices.java +++ b/sar/src/main/java/org/jboss/as/service/MBeanServices.java @@ -100,7 +100,7 @@ void addDependency(final String dependencyMBeanName) { final ServiceName injectedMBeanStartStopServiceName = ServiceNameFactory.newStartStop(dependencyMBeanName); startStopServiceBuilder.requires(injectedMBeanStartStopServiceName); final ServiceName injectedMBeanRegisterUnregisterServiceName = ServiceNameFactory.newRegisterUnregister(dependencyMBeanName); - registerUnregisterServiceBuilder.requires(injectedMBeanRegisterUnregisterServiceName); + createDestroyServiceBuilder.requires(injectedMBeanRegisterUnregisterServiceName); } void addAttribute(final String attributeMBeanName, final Method setter, final DelegatingSupplier propertySupplier) { diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitter.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitter.java new file mode 100644 index 000000000000..e4ff46a7f46e --- /dev/null +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitter.java @@ -0,0 +1,110 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.jboss.as.test.integration.sar.order; + +import static org.jboss.as.test.integration.sar.order.LifecycleEmitterMBean.invokeListener; +import static org.jboss.as.test.integration.sar.order.LifecycleEmitterMBean.safeInvokeListener; + +import java.util.ArrayList; +import java.util.List; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.jboss.system.Service; + +public class CustomLifecycleEmitter implements CustomLifecycleEmitterMBean, Service, MBeanRegistration { + + private volatile String id; + private volatile ObjectName listener; + private volatile ObjectName dependency; + private volatile MBeanServer server; + private final List unrecordedEvents = new ArrayList<>(); + + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public ObjectName getDependency() { + return dependency; + } + + @Override + public void setDependency(ObjectName dependency) { + this.dependency = dependency; + } + + @Override + public ObjectName getLifecycleListener() { + return listener; + } + + @Override + public void setLifecycleListener(ObjectName lifecycleListener) { + this.listener = lifecycleListener; + } + + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + this.server = server; + for (String unrecordedEvent : unrecordedEvents) { + invokeListener(id, unrecordedEvent, server); + } + invokeListener(id, MBEAN_PRE_REGISTERED, server); + return name; + } + + @Override + public void postRegister(Boolean registrationDone) { + safeInvokeListener(id, MBEAN_POST_REGISTERED, server); + } + + @Override + public void preDeregister() throws Exception { + invokeListener(id, MBEAN_PRE_DEREGISTERED, server); + } + + @Override + public void postDeregister() { + safeInvokeListener(id, MBEAN_POST_DEREGISTERED, server); + } + + @Override + public void create() throws Exception { + if (server != null) { + invokeListener(id, MBEAN_CREATED, server); + } else { + unrecordedEvents.add(MBEAN_CREATED); + } + } + + @Override + public void start() throws Exception { + if (server != null) { + invokeListener(id, MBEAN_STARTED, server); + } else { + unrecordedEvents.add(MBEAN_STARTED); + } + } + + @Override + public void stop() { + safeInvokeListener(id, MBEAN_STOPPED, server); + } + + @Override + public void destroy() { + safeInvokeListener(id, MBEAN_DESTROYED, server); + } +} diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitterMBean.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitterMBean.java new file mode 100644 index 000000000000..261dc7c0cdcf --- /dev/null +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/CustomLifecycleEmitterMBean.java @@ -0,0 +1,9 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.jboss.as.test.integration.sar.order; + +public interface CustomLifecycleEmitterMBean extends LifecycleEmitterMBean { +} diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitter.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitter.java index 9a4bdae18bb8..240fd3e32111 100644 --- a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitter.java +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitter.java @@ -7,6 +7,7 @@ import javax.management.InstanceNotFoundException; import javax.management.MBeanException; +import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; @@ -15,11 +16,12 @@ public class LifecycleEmitter extends ServiceMBeanSupport implements LifecycleEmitterMBean { - private static final String[] LISTENER_SIG = { "java.lang.String" }; + private static final String[] LISTENER_SIG = { "java.lang.String", "java.lang.String" }; private static volatile ObjectName LISTENER; private volatile String id; private volatile ObjectName listener; + private volatile ObjectName dependency; @Override public String getId() { @@ -31,6 +33,16 @@ public void setId(String id) { this.id = id; } + @Override + public ObjectName getDependency() { + return dependency; + } + + @Override + public void setDependency(ObjectName dependency) { + this.dependency = dependency; + } + @Override public ObjectName getLifecycleListener() { return listener; @@ -43,34 +55,68 @@ public void setLifecycleListener(ObjectName listener) { @Override protected void createService() throws Exception { - invokeListener("mbeanCreated"); + invokeListener(MBEAN_CREATED); } @Override protected void startService() throws Exception { - invokeListener("mbeanStarted"); + invokeListener(MBEAN_STARTED); + } + + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + ObjectName result = super.preRegister(server, name); + invokeListener(MBEAN_PRE_REGISTERED); + return result; + } + + @Override + public void postRegister(Boolean registrationDone) { + super.postRegister(registrationDone); + safeInvokeListener(MBEAN_POST_REGISTERED); + } + + @Override + public void preDeregister() throws Exception { + super.preDeregister(); + invokeListener(MBEAN_PRE_DEREGISTERED); + } + + @Override + public void postDeregister() { + super.postDeregister(); + safeInvokeListener(MBEAN_POST_DEREGISTERED); } @Override protected void stopService() throws Exception { - invokeListener("mbeanStopped"); + invokeListener(MBEAN_STOPPED); } @Override protected void destroyService() throws Exception { - invokeListener("mbeanDestroyed"); + invokeListener(MBEAN_DESTROYED); } - private void invokeListener(String methodName) throws MalformedObjectNameException, ReflectionException, InstanceNotFoundException, MBeanException { + private void safeInvokeListener(String methodName) { + try { + invokeListener(methodName); + } catch (MalformedObjectNameException | ReflectionException | InstanceNotFoundException | MBeanException e) { + throw new RuntimeException(e); + } + } + + private void invokeListener(String eventName) throws MalformedObjectNameException, ReflectionException, InstanceNotFoundException, MBeanException { if ("A".equals(id)) { - // Add a delay to give the other mbeans a chance to (incorrectly) move ahead - try { - Thread.sleep(100); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + if (!MBEAN_PRE_DEREGISTERED.equals(eventName) && !MBEAN_POST_DEREGISTERED.equals(eventName)) { + // Add a delay to give the other mbeans a chance to (incorrectly) move ahead + sleep(100); } + } else if (MBEAN_STOPPED.equals(eventName) || MBEAN_DESTROYED.equals(eventName)) { + // Add a delay to give A chance to (incorrectly) deregister + sleep(50); } - getServer().invoke(getListenerObjectName(), methodName, new Object[]{ id }, LISTENER_SIG); + getServer().invoke(getListenerObjectName(), "mbeanEvent", new Object[]{ id, eventName }, LISTENER_SIG); } private static ObjectName getListenerObjectName() throws MalformedObjectNameException { @@ -79,4 +125,12 @@ private static ObjectName getListenerObjectName() throws MalformedObjectNameExce } return LISTENER; } + + private static void sleep(long ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } } diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitterMBean.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitterMBean.java index 8d98313646f8..2f54031eab87 100644 --- a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitterMBean.java +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleEmitterMBean.java @@ -5,15 +5,56 @@ package org.jboss.as.test.integration.sar.order; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import javax.management.ReflectionException; public interface LifecycleEmitterMBean { + String MBEAN_CREATED = "mbeanCreated"; + String MBEAN_STARTED = "mbeanStarted"; + String MBEAN_STOPPED = "mbeanStopped"; + String MBEAN_DESTROYED = "mbeanDestroyed"; + String MBEAN_PRE_REGISTERED = "mbeanPreRegistered"; + String MBEAN_POST_REGISTERED = "mbeanPostRegistered"; + String MBEAN_PRE_DEREGISTERED = "mbeanPreDeregistered"; + String MBEAN_POST_DEREGISTERED = "mbeanPostDeregistered"; + + String[] LISTENER_SIG = { "java.lang.String", "java.lang.String" }; + + String getId(); void setId(String id); + ObjectName getDependency(); + + void setDependency(ObjectName dependency); + ObjectName getLifecycleListener(); void setLifecycleListener(ObjectName lifecycleListener); + + static void safeInvokeListener(String id, String methodName, MBeanServer mBeanServer) { + try { + invokeListener(id, methodName, mBeanServer); + } catch (MalformedObjectNameException | ReflectionException | InstanceNotFoundException | MBeanException e) { + throw new RuntimeException(e); + } + } + + static void invokeListener(String id, String eventName, MBeanServer mBeanServer) throws MalformedObjectNameException, ReflectionException, InstanceNotFoundException, MBeanException { + if ("A".equals(id)) { + // Add a delay to give the other mbeans a chance to (incorrectly) move ahead + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + mBeanServer.invoke(ObjectName.getInstance("jboss:name=OrderListener"), "mbeanEvent", new Object[]{ id, eventName }, LISTENER_SIG); + } } diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListener.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListener.java index 7441fecabc30..aa9c8c298c47 100644 --- a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListener.java +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListener.java @@ -13,48 +13,18 @@ public class LifecycleListener extends ServiceMBeanSupport implements LifecycleListenerMBean { - private final List creates = Collections.synchronizedList(new ArrayList<>()); - private final List starts = Collections.synchronizedList(new ArrayList<>()); - private final List stops = Collections.synchronizedList(new ArrayList<>()); - private final List destroys = Collections.synchronizedList(new ArrayList<>()); + private final List allEvents = Collections.synchronizedList(new ArrayList<>()); - @Override - public void mbeanCreated(String id) { - creates.add(id); - } - - @Override - public synchronized void mbeanStarted(String id) { - starts.add(id); - } @Override - public synchronized void mbeanStopped(String id) { - stops.add(id); + public void mbeanEvent(String id, String event) { + allEvents.add(new Tuple(id, event)); } - @Override - public void mbeanDestroyed(String id) { - destroys.add(id); - } @Override - public List getCreates() { - return creates; + public List getAllEvents() { + return allEvents; } - @Override - public List getStarts() { - return starts; - } - - @Override - public List getStops() { - return stops; - } - - @Override - public List getDestroys() { - return destroys; - } } diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListenerMBean.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListenerMBean.java index ac34091871d9..4bac36d64d6d 100644 --- a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListenerMBean.java +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/LifecycleListenerMBean.java @@ -5,24 +5,47 @@ package org.jboss.as.test.integration.sar.order; +import java.io.Serializable; import java.util.List; +import java.util.Objects; public interface LifecycleListenerMBean { - void mbeanCreated(String id); + void mbeanEvent(String id, String event); - void mbeanStarted(String id); + List getAllEvents(); - void mbeanStopped(String id); + final class Tuple implements Serializable { - void mbeanDestroyed(String id); + private static final long serialVersionUID = 1L; - List getCreates(); + final String id; + final String method; - List getStarts(); + public Tuple(String id, String method) { + this.id = id; + this.method = method; + } - List getStops(); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tuple tuple = (Tuple) o; + return Objects.equals(id, tuple.id) && Objects.equals(method, tuple.method); + } - List getDestroys(); + @Override + public int hashCode() { + return Objects.hash(id, method); + } + @Override + public String toString() { + return "Tuple{" + + "id='" + id + '\'' + + ", method='" + method + '\'' + + '}'; + } + } } diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanOrderingTestCase.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanOrderingTestCase.java new file mode 100644 index 000000000000..2b99cd03fba3 --- /dev/null +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanOrderingTestCase.java @@ -0,0 +1,199 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.jboss.as.test.integration.sar.order; + +import static org.junit.Assert.assertEquals; +import static org.jboss.as.test.shared.PermissionUtils.createPermissionsXmlAsset; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanPermission; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; + +import org.jboss.arquillian.container.test.api.Deployer; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.as.arquillian.api.ContainerResource; +import org.jboss.as.arquillian.container.ManagementClient; +import org.jboss.as.test.integration.common.DefaultConfiguration; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test that Service MBeans that depend on other such mbeans have their lifecycle methods called in correct dependency order. + * + * @author Brian Stansberry + */ +@RunWith(Arquillian.class) +@RunAsClient +public class ServiceMBeanOrderingTestCase { + + private static final String MBEAN_CREATED = LifecycleEmitter.MBEAN_CREATED; + private static final String MBEAN_STARTED = LifecycleEmitter.MBEAN_STARTED; + private static final String MBEAN_STOPPED = LifecycleEmitter.MBEAN_STOPPED; + private static final String MBEAN_DESTROYED = LifecycleEmitter.MBEAN_DESTROYED; + private static final String MBEAN_PRE_REGISTERED = LifecycleEmitter.MBEAN_PRE_REGISTERED; + private static final String MBEAN_POST_REGISTERED = LifecycleEmitter.MBEAN_POST_REGISTERED; + private static final String MBEAN_PRE_DEREGISTERED = LifecycleEmitter.MBEAN_PRE_DEREGISTERED; + private static final String MBEAN_POST_DEREGISTERED = LifecycleEmitter.MBEAN_POST_DEREGISTERED; + + private static final String UNMANAGED_SAR_DEPLOYMENT_NAME = "service-mbean-order-test"; + private static final List FORWARD_ORDER = Arrays.asList("A", "B", "C"); + private static final List REVERSE_ORDER = Arrays.asList("C", "B", "A"); + + private static final List A_EVENTS = Arrays.asList(MBEAN_PRE_REGISTERED, MBEAN_CREATED, MBEAN_STARTED, + MBEAN_POST_REGISTERED, MBEAN_PRE_DEREGISTERED, MBEAN_POST_DEREGISTERED, + MBEAN_STOPPED, MBEAN_DESTROYED); + private static final List D_EVENTS = Arrays.asList(MBEAN_CREATED, MBEAN_STARTED, + MBEAN_PRE_REGISTERED, MBEAN_POST_REGISTERED, MBEAN_PRE_DEREGISTERED, MBEAN_POST_DEREGISTERED, + MBEAN_STOPPED, MBEAN_DESTROYED); + + @ContainerResource + private ManagementClient managementClient; + + @ArquillianResource + private Deployer deployer; + + @Deployment(name = ServiceMBeanOrderingTestCase.UNMANAGED_SAR_DEPLOYMENT_NAME, managed = false) + public static JavaArchive geTestMBeanSar() { + final JavaArchive sar = ShrinkWrap.create(JavaArchive.class, "service-mbean-order-test.sar"); + sar.addClasses(LifecycleEmitterMBean.class, LifecycleEmitter.class, CustomLifecycleEmitterMBean.class, CustomLifecycleEmitter.class); + sar.addAsManifestResource(ServiceMBeanOrderingTestCase.class.getPackage(), "jboss-service.xml", "jboss-service.xml"); + sar.addAsManifestResource(createPermissionsXmlAsset( + new MBeanPermission(ServiceMBeanOrderingTestCase.class.getPackage().getName() + ".*", "*")), + "permissions.xml"); + + + return sar; + } + + @Deployment + public static JavaArchive getTestResultMBeanSar() { + final JavaArchive sar = ShrinkWrap.create(JavaArchive.class, "service-mbean-order-test-result.sar"); + sar.addClasses(LifecycleListenerMBean.class, LifecycleListener.class); + sar.addAsManifestResource(ServiceMBeanOrderingTestCase.class.getPackage(), "result-jboss-service.xml", + "jboss-service.xml"); + return sar; + } + + /** + * Tests that invocation on a service deployed within a .sar, inside a .ear without an application.xml, is successful. + * + * @throws Exception if one occurs when connecting the mbean server and reading the event history + */ + @Test + public void testServiceMBeanLifecycleOrder() throws Exception { + // get mbean server + try (JMXConnector connector = JMXConnectorFactory.connect(managementClient.getRemoteJMXURL(), DefaultConfiguration.credentials())) { + // deploy the unmanaged sar + deployer.deploy(ServiceMBeanOrderingTestCase.UNMANAGED_SAR_DEPLOYMENT_NAME); + // undeploy it + deployer.undeploy(ServiceMBeanOrderingTestCase.UNMANAGED_SAR_DEPLOYMENT_NAME); + + Events events = new Events(connector.getMBeanServerConnection()); + + // Validate that A's events happen in the expected order for a ServiceMBeanSupport subclass + assertEquals(A_EVENTS, events.aEvents); + // Validate that D's events happen in the expected order for a non-ServiceMBeanSupport subclass + assertEquals(D_EVENTS, events.dEvents); + + // C depends on B depends on A. Validate create/start/stop/destroy reflect that + assertEquals(FORWARD_ORDER, events.creates); + assertEquals(FORWARD_ORDER, events.starts); + assertEquals(REVERSE_ORDER, events.stops); + assertEquals(REVERSE_ORDER, events.destroys); + + // Confirm A is registered before any dependent is created + for (Map.Entry entry : events.createsIndexes.entrySet()) { + assertTrue(entry.getKey() + " was created before A was registered", entry.getValue() > events.aRegistered); + } + + // Confirm A is deregistered after any dependent is destroyed + for (Map.Entry entry : events.destroysIndexes.entrySet()) { + assertTrue(entry.getKey() + " was destroyed after A was deregistered", entry.getValue() < events.aDeregistered); + } + } + + } + + private static class Events { + int aRegistered; + int aStarted; + int aDeregistered; + final Map createsIndexes = new HashMap<>(); + final Map destroysIndexes = new HashMap<>(); + final List creates = new ArrayList<>(); + final List starts = new ArrayList<>(); + final List stops = new ArrayList<>(); + final List destroys = new ArrayList<>(); + final List aEvents = new ArrayList<>(); + final List dEvents = new ArrayList<>(); + + private Events(MBeanServerConnection mBeanServerConnection) throws MalformedObjectNameException, ReflectionException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, IOException { + + ObjectName on = new ObjectName("jboss:name=OrderListener"); + @SuppressWarnings("unchecked") + List order = + (List) mBeanServerConnection.getAttribute(on, "AllEvents"); + + int i = 0; + for (LifecycleListenerMBean.Tuple tuple : order) { + if ("A".equals(tuple.id)) { + if (MBEAN_POST_REGISTERED.equals(tuple.method)) { + aRegistered = i; + } else if (MBEAN_STARTED.equals(tuple.method)) { + aStarted = i; + } else if (MBEAN_PRE_DEREGISTERED.equals(tuple.method)) { + aDeregistered = i; + } + aEvents.add(tuple.method); + } else if (MBEAN_CREATED.equals(tuple.method)) { + createsIndexes.put(tuple.id, i); + } else if (MBEAN_DESTROYED.equals(tuple.method)) { + destroysIndexes.put(tuple.id, i); + } + + if ("D".equals(tuple.id)) { + dEvents.add(tuple.method); + } else if (!"E".equals(tuple.id)) { // ignore D and E in this block as they have no ordering relationship with B and C + switch (tuple.method) { + case MBEAN_CREATED: + creates.add(tuple.id); + break; + case MBEAN_STARTED: + starts.add(tuple.id); + break; + case MBEAN_STOPPED: + stops.add(tuple.id); + break; + case MBEAN_DESTROYED: + destroys.add(tuple.id); + break; + } + } + + i++; + } + } + } +} \ No newline at end of file diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanSupportOrderingTestCase.java b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanSupportOrderingTestCase.java deleted file mode 100644 index 5ec7b5cd3d43..000000000000 --- a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/ServiceMBeanSupportOrderingTestCase.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright The WildFly Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.jboss.as.test.integration.sar.order; - -import static org.jboss.as.test.shared.PermissionUtils.createPermissionsXmlAsset; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import javax.management.AttributeNotFoundException; -import javax.management.InstanceNotFoundException; -import javax.management.MBeanException; -import javax.management.MBeanPermission; -import javax.management.MBeanServerConnection; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import javax.management.ReflectionException; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; - -import org.jboss.arquillian.container.test.api.Deployer; -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.arquillian.container.test.api.RunAsClient; -import org.jboss.arquillian.junit.Arquillian; -import org.jboss.arquillian.test.api.ArquillianResource; -import org.jboss.as.arquillian.api.ContainerResource; -import org.jboss.as.arquillian.container.ManagementClient; -import org.jboss.as.test.integration.common.DefaultConfiguration; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.jboss.system.ServiceMBeanSupport; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test that MBeans which extend {@link ServiceMBeanSupport} and depend on other such mbeans have their create/start/stop/destroyService methods called in correct dependency order. - * - * @author Brian Stansberry - */ -@RunWith(Arquillian.class) -@RunAsClient -public class ServiceMBeanSupportOrderingTestCase { - - private static final String UNMANAGED_SAR_DEPLOYMENT_NAME = "service-mbean-order-test"; - private static final List FORWARD_ORDER = Arrays.asList("A", "B", "C"); - private static final List REVERSE_ORDER = Arrays.asList("C", "B", "A"); - - @ContainerResource - private ManagementClient managementClient; - - @ArquillianResource - private Deployer deployer; - - @Deployment(name = ServiceMBeanSupportOrderingTestCase.UNMANAGED_SAR_DEPLOYMENT_NAME, managed = false) - public static JavaArchive geTestMBeanSar() { - final JavaArchive sar = ShrinkWrap.create(JavaArchive.class, "service-mbean-order-test.sar"); - sar.addClasses(LifecycleEmitterMBean.class, LifecycleEmitter.class); - sar.addAsManifestResource(ServiceMBeanSupportOrderingTestCase.class.getPackage(), "jboss-service.xml", "jboss-service.xml"); - sar.addAsManifestResource(createPermissionsXmlAsset( - new MBeanPermission(ServiceMBeanSupportOrderingTestCase.class.getPackage().getName() + ".*", "*")), - "permissions.xml"); - - - return sar; - } - - @Deployment - public static JavaArchive getTestResultMBeanSar() { - final JavaArchive sar = ShrinkWrap.create(JavaArchive.class, "service-mbean-order-test-result.sar"); - sar.addClasses(LifecycleListenerMBean.class, LifecycleListener.class); - sar.addAsManifestResource(ServiceMBeanSupportOrderingTestCase.class.getPackage(), "result-jboss-service.xml", - "jboss-service.xml"); - return sar; - } - - /** - * Tests that invocation on a service deployed within a .sar, inside a .ear without an application.xml, is successful. - * - * @throws Exception - */ - @Test - public void testServiceMBeanSupportLifecycleOrder() throws Exception { - // get mbean server - final JMXConnector connector = JMXConnectorFactory.connect(managementClient.getRemoteJMXURL(), DefaultConfiguration.credentials()); - final MBeanServerConnection mBeanServerConnection = connector.getMBeanServerConnection(); - - // deploy the unmanaged sar - deployer.deploy(ServiceMBeanSupportOrderingTestCase.UNMANAGED_SAR_DEPLOYMENT_NAME); - // undeploy it - deployer.undeploy(ServiceMBeanSupportOrderingTestCase.UNMANAGED_SAR_DEPLOYMENT_NAME); - - // Check the order of lifecycle events - checkOrder(mBeanServerConnection, "Starts", FORWARD_ORDER); - checkOrder(mBeanServerConnection, "Stops", REVERSE_ORDER); - checkOrder(mBeanServerConnection, "Creates", FORWARD_ORDER); - checkOrder(mBeanServerConnection, "Destroys", REVERSE_ORDER); - } - - private static void checkOrder(MBeanServerConnection mBeanServerConnection, String lifecycleStage, List expected) throws MalformedObjectNameException, ReflectionException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, IOException { - List order = (List) mBeanServerConnection.getAttribute(new ObjectName("jboss:name=OrderListener"), - lifecycleStage); - Assert.assertEquals("Unexpected order for " + lifecycleStage, expected, order); - } -} \ No newline at end of file diff --git a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/jboss-service.xml b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/jboss-service.xml index dac6b1e91420..28021d0a3d75 100644 --- a/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/jboss-service.xml +++ b/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/sar/order/jboss-service.xml @@ -8,6 +8,20 @@ xsi:schemaLocation="urn:jboss:service:7.0 jboss-service_7_0.xsd"> + + + + E + jboss:name=A + jboss:name=OrderListener + + + D + jboss:name=A + jboss:name=OrderListener + + + C jboss:name=B