/*
* JBoss, Home of Professional Open Source
* Copyright 2015, Red Hat, Inc., and individual contributors as indicated
* by the @authors tag.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.as.controller;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.jboss.as.controller.capability.Capability;
import org.jboss.as.controller.capability.RuntimeCapability;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.operations.global.GlobalNotifications;
import org.jboss.as.controller.operations.global.GlobalOperationHandlers;
import org.jboss.as.controller.operations.validation.AbstractParameterValidator;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.test.AbstractControllerTestBase;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.jboss.msc.service.AbstractServiceListener;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.xnio.Pool;
import org.xnio.XnioWorker;
/**
* @author Tomaz Cerar (c) 2015 Red Hat Inc.
*/
public class CapabilityRegistryTestCase extends AbstractControllerTestBase {
private static final SimpleAttributeDefinition ad = new SimpleAttributeDefinitionBuilder("test", ModelType.STRING, true)
.build();
private static final SimpleAttributeDefinition other = new SimpleAttributeDefinitionBuilder("other", ModelType.STRING, true)
.setValidator(new AbstractParameterValidator() {
@Override
public void validateParameter(String parameterName, ModelNode value) throws OperationFailedException {
if (value.asString().equals("break")){
throw new OperationFailedException("we need to break");
}
}
})
.build();
private static final RuntimeCapability<Void> IO_WORKER_RUNTIME_CAPABILITY =
RuntimeCapability.Builder.of("org.wildfly.io.worker", false, XnioWorker.class).build();
private static final RuntimeCapability<Void> IO_POOL_RUNTIME_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.io.buffer-pool", false, Pool.class).build();
private static final RuntimeCapability<Void> TEST_CAPABILITY1 = RuntimeCapability.Builder.of("org.wildfly.test.capability1", true, Void.class).build();
private static final RuntimeCapability<Void> TEST_CAPABILITY2 = RuntimeCapability.Builder.of("org.wildfly.test.capability2", true, Void.class).build();
private static final RuntimeCapability<Void> TEST_CAPABILITY3 = RuntimeCapability.Builder.of("org.wildfly.test.capability3", true, Void.class).build();
private static final RuntimeCapability<Void> PROFILE_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.test.profile-capability", true, Void.class).build();
private static final RuntimeCapability<Void> HOST_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.test.host-capability", true, Void.class).build();
private static final RuntimeCapability<Void> RELOADED_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.test.reloaded-capability", true, Void.class).build();
private static final RuntimeCapability<Void> INDEPENDENT_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.test.independent-capability", true, Void.class).build();
private static final RuntimeCapability<Void> ROOT_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.test.root-capability", false, Void.class).build();
private static final RuntimeCapability<Void> DEPENDENT_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.test.dep-capability", false, Void.class)
.addRequirements(ROOT_CAPABILITY.getName()).build();
private static final RuntimeCapability<Void> TRANS_DEP_CAPABILITY = RuntimeCapability.Builder.of("org.wildfly.test.trans-dep-capability", false, Void.class)
.addRequirements(DEPENDENT_CAPABILITY.getName()).build();
private static PathAddress TEST_ADDRESS1 = PathAddress.pathAddress("subsystem", "test1");
private static PathAddress TEST_ADDRESS2 = PathAddress.pathAddress("subsystem", "test2");
private static PathAddress TEST_ADDRESS3 = PathAddress.pathAddress("sub", "resource");
private static PathAddress TEST_ADDRESS4 = PathAddress.pathAddress("subsystem", "test4");
private static PathAddress TEST_ADDRESS5 = PathAddress.pathAddress("subsystem", "broken");
private static PathElement RELOAD_ELEMENT = PathElement.pathElement("subsystem", "reload");
private static PathElement CHILD_ELEMENT = PathElement.pathElement("child", "test");
private static PathElement GRANDCHILD_ELEMENT = PathElement.pathElement("grandchild", "test");
private static PathElement INFANT_ELEMENT = PathElement.pathElement("infant", "test");
private static PathElement INDEPENDENT_ELEMENT = PathElement.pathElement("independent", "test");
private static PathElement UNINCORPORATED_ELEMENT = PathElement.pathElement("unincorporated", "test");
private static PathElement HOST_ELEMENT = PathElement.pathElement("host", "test");
private static PathElement PROFILE_ELEMENT = PathElement.pathElement("profile", "test");
private static PathElement NO_CAP_ELEMENT = PathElement.pathElement("subsystem", "no-cap");
private static PathElement DEP_CAP_ELEMENT = PathElement.pathElement("subsystem", "dep-cap");
private static ResourceDefinition TEST_RESOURCE1 = ResourceBuilder.Factory.create(TEST_ADDRESS1.getElement(0),
NonResolvingResourceDescriptionResolver.INSTANCE)
.setAddOperation(new AbstractAddStepHandler(new HashSet<>(Arrays.asList(IO_POOL_RUNTIME_CAPABILITY, IO_WORKER_RUNTIME_CAPABILITY)), ad, other))
.setRemoveOperation(new ReloadRequiredRemoveStepHandler(IO_POOL_RUNTIME_CAPABILITY, IO_WORKER_RUNTIME_CAPABILITY))
.addReadWriteAttribute(ad, null, new ReloadRequiredWriteAttributeHandler(ad))
.addReadWriteAttribute(other, null, new ReloadRequiredWriteAttributeHandler(other))
.addOperation(new SimpleOperationDefinition("add-cap",
new NonResolvingResourceDescriptionResolver()),
(context, operation) -> {
ManagementResourceRegistration mrr = context.getResourceRegistrationForUpdate();
mrr.registerCapability(TEST_CAPABILITY1);
Assert.assertEquals(3, mrr.getCapabilities().size());
})
.addCapability(IO_POOL_RUNTIME_CAPABILITY)
.addCapability(IO_WORKER_RUNTIME_CAPABILITY)
.build();
private static final ResourceDefinition SUB_RESOURCE = ResourceBuilder.Factory.create(TEST_ADDRESS3.getElement(0),
NonResolvingResourceDescriptionResolver.INSTANCE)
.setAddOperation(new AbstractAddStepHandler(Collections.singleton(TEST_CAPABILITY3)))
.setRemoveOperation(new ReloadRequiredRemoveStepHandler(TEST_CAPABILITY3))
.addReadWriteAttribute(ad, null, new ReloadRequiredWriteAttributeHandler(ad))
.addCapability(TEST_CAPABILITY3)
.build();
private static ResourceDefinition TEST_RESOURCE2 = ResourceBuilder.Factory.create(TEST_ADDRESS2.getElement(0),
NonResolvingResourceDescriptionResolver.INSTANCE)
.setAddOperation(new AbstractAddStepHandler(Collections.singleton(TEST_CAPABILITY2), ad, other))
.setRemoveOperation(new ReloadRequiredRemoveStepHandler(TEST_CAPABILITY2))
.addReadWriteAttribute(ad, null, new ReloadRequiredWriteAttributeHandler(ad))
.addReadWriteAttribute(other, null, new ReloadRequiredWriteAttributeHandler(other))
.addCapability(TEST_CAPABILITY2)
.addOperation(new SimpleOperationDefinition("add-sub-resource",
new NonResolvingResourceDescriptionResolver()),
(context, operation) -> {
ManagementResourceRegistration mrr = context.getResourceRegistrationForUpdate();
mrr.registerSubModel(SUB_RESOURCE);
})
.addOperation(new SimpleOperationDefinition("remove-sub-resource",
new NonResolvingResourceDescriptionResolver()),
(context, operation) -> {
ManagementResourceRegistration mrr = context.getResourceRegistrationForUpdate();
mrr.unregisterSubModel(SUB_RESOURCE.getPathElement());
})
.build();
private static ResourceDefinition TEST_RESOURCE4 = ResourceBuilder.Factory.create(TEST_ADDRESS4.getElement(0),
NonResolvingResourceDescriptionResolver.INSTANCE)
.setAddOperation(new AbstractAddStepHandler(new HashSet<>(Arrays.asList(IO_POOL_RUNTIME_CAPABILITY, IO_WORKER_RUNTIME_CAPABILITY)), ad, other) {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
if(operation.hasDefined("fail")) {
throw new OperationFailedException("Let's rollback");
}
}
@Override
protected boolean requiresRuntime(OperationContext context) {
return true;
}
})
.setRemoveOperation(new ReloadRequiredRemoveStepHandler(IO_POOL_RUNTIME_CAPABILITY, IO_WORKER_RUNTIME_CAPABILITY))
.addReadWriteAttribute(ad, null, new ReloadRequiredWriteAttributeHandler(ad))
.addReadWriteAttribute(other, null, new ReloadRequiredWriteAttributeHandler(other))
.addCapability(IO_POOL_RUNTIME_CAPABILITY)
.addCapability(IO_WORKER_RUNTIME_CAPABILITY)
.build();
private static final OperationStepHandler RELOAD_HANDLER = new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
context.reloadRequired();
context.completeStep(OperationContext.RollbackHandler.REVERT_RELOAD_REQUIRED_ROLLBACK_HANDLER);
}
};
private static final OperationDefinition RELOAD_DEFINITION = new SimpleOperationDefinition("reload", NonResolvingResourceDescriptionResolver.INSTANCE);
private static final OperationStepHandler RUNTIME_MOD_HANDLER = new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
context.addStep(new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
// If we execute, the result is now defined
context.getResult().set(true);
}
}, OperationContext.Stage.RUNTIME);
}
};
private static final OperationDefinition RUNTIME_MOD_DEFINITION = new SimpleOperationDefinition("runtime-mod", NonResolvingResourceDescriptionResolver.INSTANCE);
private static final OperationDefinition RUNTIME_ONLY_DEFINITION = new SimpleOperationDefinitionBuilder("runtime-only", NonResolvingResourceDescriptionResolver.INSTANCE)
.setRuntimeOnly()
.build();
private static ResourceBuilder createReloadTestResourceBuilder(PathElement address, RuntimeCapability... caps) {
return addReloadTestOps(ResourceBuilder.Factory.create(address, NonResolvingResourceDescriptionResolver.INSTANCE), caps);
}
private static ResourceBuilder addReloadTestOps(ResourceBuilder builder, RuntimeCapability... caps) {
ResourceBuilder result = builder
.addOperation(RELOAD_DEFINITION, RELOAD_HANDLER)
.addOperation(RUNTIME_MOD_DEFINITION, RUNTIME_MOD_HANDLER)
.addOperation(RUNTIME_ONLY_DEFINITION, RUNTIME_MOD_HANDLER);
if (caps != null && caps.length > 0) {
result = result.addCapabilities((Capability[]) caps)
.setAddOperation(new AbstractAddStepHandler(new HashSet<RuntimeCapability>(Arrays.asList(caps))))
.setRemoveOperation(new ModelOnlyRemoveStepHandler());
}
return result;
}
private static ResourceDefinition RELOAD_PARENT = createReloadTestResourceBuilder(RELOAD_ELEMENT, RELOADED_CAPABILITY)
.pushChild(createReloadTestResourceBuilder(INDEPENDENT_ELEMENT, INDEPENDENT_CAPABILITY))
.pushChild(createReloadTestResourceBuilder(CHILD_ELEMENT)).pop()
.pop()
.pushChild(createReloadTestResourceBuilder(UNINCORPORATED_ELEMENT))
.setIncorporatingCapabilities(Collections.emptySet())
.pushChild(createReloadTestResourceBuilder(CHILD_ELEMENT)).pop()
.pop()
.pushChild(createReloadTestResourceBuilder(CHILD_ELEMENT))
.pushChild(createReloadTestResourceBuilder(GRANDCHILD_ELEMENT))
.pushChild(createReloadTestResourceBuilder(INFANT_ELEMENT))
.pop()
.pop()
.pushChild(createReloadTestResourceBuilder(UNINCORPORATED_ELEMENT))
.setIncorporatingCapabilities(Collections.emptySet())
.pushChild(createReloadTestResourceBuilder(INFANT_ELEMENT)).pop()
.pop()
.pop()
.build();
private static ResourceDefinition HOST_RESOURCE = createReloadTestResourceBuilder(HOST_ELEMENT, HOST_CAPABILITY)
.pushChild(createReloadTestResourceBuilder(CHILD_ELEMENT)).pop()
.build();
private static ResourceDefinition PROFILE_RESOURCE = createReloadTestResourceBuilder(PROFILE_ELEMENT, PROFILE_CAPABILITY)
.pushChild(createReloadTestResourceBuilder(NO_CAP_ELEMENT))
.pushChild(createReloadTestResourceBuilder(CHILD_ELEMENT)).pop()
.pop()
.build();
private static ResourceDefinition NO_CAP_RESOURCE = createReloadTestResourceBuilder(CHILD_ELEMENT)
.build();
private static ResourceDefinition DEP_CAP_RESOURCE = createReloadTestResourceBuilder(DEP_CAP_ELEMENT, DEPENDENT_CAPABILITY)
.pushChild(createReloadTestResourceBuilder(CHILD_ELEMENT, TRANS_DEP_CAPABILITY)).pop()
.build();
private static final AtomicReference<CountDownLatch> readLatchHolder = new AtomicReference<>();
private static final AtomicReference<CountDownLatch> failLatchHolder = new AtomicReference<>();
private static final ResourceDefinition BAD_RESOURCE = ResourceBuilder.Factory.create(TEST_ADDRESS5.getElement(0),
NonResolvingResourceDescriptionResolver.INSTANCE)
.setAddOperation(new AbstractAddStepHandler(Collections.singleton(TEST_CAPABILITY1)) {
@Override
protected void performRuntime(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
try {
CountDownLatch latch = new CountDownLatch(1);
failLatchHolder.set(latch);
// Notify the test we are done with model stage
readLatchHolder.get().countDown();
// Wait for test to unblock us
latch.await();
} catch (InterruptedException e) {
// ignore
}
context.setRollbackOnly();
context.getFailureDescription().set("failed per design");
}
})
.setRemoveOperation(new ReloadRequiredRemoveStepHandler(TEST_CAPABILITY3))
.addOperation(new SimpleOperationDefinition("read", NonResolvingResourceDescriptionResolver.INSTANCE),
((context, operation) -> context.getResult().set(true)))
.build();
private static final int RELOAD_CAP_COUNT = 6;
private ManagementModel managementModel;
@Override
protected void initModel(ManagementModel managementModel) {
this.managementModel = managementModel;
ManagementResourceRegistration rootRegistration = managementModel.getRootResourceRegistration();
// register the global operations to be able to call :read-attribute and :write-attribute
GlobalOperationHandlers.registerGlobalOperations(rootRegistration, processType);
// register the global notifications so there is no warning that emitted notifications are not described by the resource.
GlobalNotifications.registerGlobalNotifications(rootRegistration, processType);
rootRegistration.registerOperationHandler(CompositeOperationHandler.DEFINITION, CompositeOperationHandler.INSTANCE);
rootRegistration.registerOperationHandler(new SimpleOperationDefinition("clean", NonResolvingResourceDescriptionResolver.INSTANCE), (context, operation) -> {
ManagementResourceRegistration mrr = context.getResourceRegistrationForUpdate();
mrr.unregisterSubModel(TEST_RESOURCE1.getPathElement());
mrr.unregisterSubModel(TEST_RESOURCE2.getPathElement());
mrr.unregisterSubModel(TEST_RESOURCE4.getPathElement());
mrr.unregisterSubModel(BAD_RESOURCE.getPathElement());
mrr.unregisterSubModel(RELOAD_PARENT.getPathElement());
mrr.unregisterSubModel(HOST_RESOURCE.getPathElement());
mrr.unregisterSubModel(PROFILE_RESOURCE.getPathElement());
mrr.unregisterSubModel(NO_CAP_RESOURCE.getPathElement());
mrr.unregisterSubModel(DEP_CAP_RESOURCE.getPathElement());
});
rootRegistration.registerOperationHandler(new SimpleOperationDefinition("create", NonResolvingResourceDescriptionResolver.INSTANCE), (context, operation) -> {
ManagementResourceRegistration mrr = context.getResourceRegistrationForUpdate();
mrr.registerSubModel(TEST_RESOURCE1);
mrr.registerSubModel(TEST_RESOURCE2);
mrr.registerSubModel(TEST_RESOURCE4);
mrr.registerSubModel(BAD_RESOURCE);
mrr.registerSubModel(RELOAD_PARENT);
mrr.registerSubModel(HOST_RESOURCE);
mrr.registerSubModel(PROFILE_RESOURCE);
mrr.registerSubModel(NO_CAP_RESOURCE);
mrr.registerSubModel(DEP_CAP_RESOURCE);
});
rootRegistration.registerOperationHandler(new SimpleOperationDefinition("root-cap", NonResolvingResourceDescriptionResolver.INSTANCE),
new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
context.registerCapability(ROOT_CAPABILITY);
}
});
rootRegistration.registerOperationHandler(new SimpleOperationDefinition("no-root-cap", NonResolvingResourceDescriptionResolver.INSTANCE),
new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
context.deregisterCapability(ROOT_CAPABILITY.getName());
}
});
rootRegistration.registerOperationHandler(RELOAD_DEFINITION, RELOAD_HANDLER);
rootRegistration.registerOperationHandler(RUNTIME_MOD_DEFINITION, RUNTIME_MOD_HANDLER);
rootRegistration.registerOperationHandler(RUNTIME_ONLY_DEFINITION, RUNTIME_MOD_HANDLER);
}
@Before
public void createModel() throws OperationFailedException {
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
executeCheckNoFailure(createOperation("create", PathAddress.EMPTY_ADDRESS));
}
@After
public void checkEmptyRegistry() throws Exception {
//remove model registration
executeCheckNoFailure(createOperation("clean", PathAddress.EMPTY_ADDRESS));
//we check that each test cleaned up after itself
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
Assert.assertEquals(0, capabilityRegistry.getPossibleCapabilities().size());
}
@Test
public void testCapabilityRegistration() throws OperationFailedException {
ManagementResourceRegistration registration = managementModel.getRootResourceRegistration().getSubModel(TEST_ADDRESS1);
Assert.assertEquals(2, registration.getCapabilities().size());
Assert.assertEquals(RELOAD_CAP_COUNT + 3, capabilityRegistry.getPossibleCapabilities().size()); //resource1 has 2 + 1 from resource 2
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
ModelNode addOp = createOperation(ADD, TEST_ADDRESS1);
addOp.get("test").set("some test value");
addOp.get("other").set("other value");
executeCheckNoFailure(addOp);
Assert.assertEquals(2, capabilityRegistry.getCapabilities().size());
//post boot registration
executeCheckNoFailure(createOperation("add-cap", TEST_ADDRESS1));
Assert.assertEquals(3, registration.getCapabilities().size());
Assert.assertEquals(RELOAD_CAP_COUNT + 4, capabilityRegistry.getPossibleCapabilities().size());
executeCheckNoFailure(createOperation(REMOVE, TEST_ADDRESS1));
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
ModelNode add2Op = createOperation(ADD, TEST_ADDRESS2);
add2Op.get("test").set("some test value");
executeCheckNoFailure(add2Op);
Assert.assertEquals(1, capabilityRegistry.getCapabilities().size());
Assert.assertEquals(RELOAD_CAP_COUNT + 4, capabilityRegistry.getPossibleCapabilities().size());
executeCheckNoFailure(createOperation("add-sub-resource", TEST_ADDRESS2));
Assert.assertEquals(1, capabilityRegistry.getCapabilities().size());
Assert.assertEquals(RELOAD_CAP_COUNT + 5, capabilityRegistry.getPossibleCapabilities().size());
executeCheckNoFailure(createOperation("remove-sub-resource", TEST_ADDRESS2));
Assert.assertEquals(1, capabilityRegistry.getCapabilities().size());
//this is now 3 as remove resource also unregisteres sub resource from mgmt tree
Assert.assertEquals(RELOAD_CAP_COUNT + 4, capabilityRegistry.getPossibleCapabilities().size());
//remove test2 resource so capabilites are moved
executeCheckNoFailure(createOperation(REMOVE, TEST_ADDRESS2));
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
}
@Test
public void testRollBack() throws OperationFailedException {
ModelNode addOp1 = createOperation(ADD, TEST_ADDRESS1);
addOp1.get("test").set("some test value");
addOp1.get("other").set("break"); //this value will throw exception and rollback should happen
executeCheckForFailure(addOp1);
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
ModelNode addOp2 = createOperation(ADD, TEST_ADDRESS2);
addOp1.get("test").set("some test value");
ModelNode composite = createOperation(ModelDescriptionConstants.COMPOSITE);
composite.get(STEPS).add(addOp2);//adds one capability,
composite.get(STEPS).add(addOp1); //breaks which causes operation to be rollbacked, so previously added capability shouldn't be represent.
executeCheckForFailure(composite);
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
}
@Test
public void testRollBackAfterPublish() throws OperationFailedException {
ModelNode addOp1 = createOperation(ADD, TEST_ADDRESS1);
addOp1.get("test").set("some test value");
addOp1.get("other").set("other value");
executeCheckNoFailure(addOp1);
Assert.assertEquals(2, capabilityRegistry.getCapabilities().size());
ModelNode addOp4 = createOperation(ADD, TEST_ADDRESS4);
addOp4.get("test").set("some test value");
addOp4.get("other").set("other value");
addOp4.get("fail").set("true");
executeCheckForFailure(addOp4); //Rollbacking
Assert.assertEquals(2, capabilityRegistry.getCapabilities().size()); //Should remove the new RegistrationPoints
addOp4 = createOperation(ADD, TEST_ADDRESS4);
addOp4.get("test").set("some test value");
addOp4.get("other").set("other value");
executeCheckNoFailure(addOp4); //Will fail if rollback didn't work as epxected
Assert.assertEquals(2, capabilityRegistry.getCapabilities().size());
executeCheckNoFailure(createOperation(REMOVE, TEST_ADDRESS4));
Assert.assertEquals(2, capabilityRegistry.getCapabilities().size());
executeCheckNoFailure(createOperation(REMOVE, TEST_ADDRESS1));
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
}
// Check that subsystem=reload requiring reload prevents runtime execution of
// subsystem=reload/child=test, since it is incorporated by the parent resource cap
@Test
public void testChildReload() throws OperationFailedException {
add(RELOAD_ELEMENT);
try {
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, CHILD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
}
// Check that subsystem=reload requiring reload prevents runtime execution of
// subsystem=reload/child=test/grandchild=test, since it is incorporated by the parent resource cap
@Test
public void testGrandChildReload() throws OperationFailedException {
add(RELOAD_ELEMENT);
try {
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT, CHILD_ELEMENT, GRANDCHILD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, CHILD_ELEMENT, GRANDCHILD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
}
// Check that subsystem=reload requiring reload prevents runtime execution of
// subsystem=reload/child=test/grandchild=test/infant=test, since it is incorporated by the parent resource cap
// This one is getting a bit extreme. :)
@Test
public void testInfantReload() throws OperationFailedException {
add(RELOAD_ELEMENT);
try {
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT, CHILD_ELEMENT, GRANDCHILD_ELEMENT, INFANT_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, CHILD_ELEMENT, GRANDCHILD_ELEMENT, INFANT_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
}
// Check that subsystem=reload requiring reload doesn't prevent runtime execution of
// subsystem=reload/independent=test, since it has its own capability
@Test
public void testIndependentCapabilityReload() throws OperationFailedException {
add(RELOAD_ELEMENT);
try {
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
// First, check what happens when the independent-capability isn't even registered yet
// This is kind of an odd case, but good to verify
runtimeCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
runtimeCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT, CHILD_ELEMENT);
// Add the independent element child so its independent-capability is registered
add(RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
try {
runtimeCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
runtimeCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT, CHILD_ELEMENT);
// Make sure triggering reload of independent element child affects things
requireReload(RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT, INDEPENDENT_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, INDEPENDENT_ELEMENT, CHILD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT, INDEPENDENT_ELEMENT);
}
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
}
// Check that subsystem=reload requiring reload doesn't prevent runtime execution of
// subsystem=reload/unincorporated=test, since it is not incorporated by the parent resource cap
@Test
public void testUnincorporatedChildResourceReload() throws OperationFailedException {
add(RELOAD_ELEMENT);
try {
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
runtimeCheck(true, RELOAD_ELEMENT, UNINCORPORATED_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, UNINCORPORATED_ELEMENT);
runtimeCheck(true, RELOAD_ELEMENT, UNINCORPORATED_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, UNINCORPORATED_ELEMENT, CHILD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
}
// Check that subsystem=reload requiring reload doesn't prevent runtime execution of
// subsystem=reload/child=test/unincorporated=test, since it is not incorporated by
// the grandparent resource cap. Also test subsystem=reload/child=test/unincorporated=test/infant=test
// to verify that its unincorporated parent stops checks further up the chain
@Test
public void testUnincorporatedGranchildResourceReload() throws OperationFailedException {
add(RELOAD_ELEMENT);
try {
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
runtimeCheck(true, RELOAD_ELEMENT, CHILD_ELEMENT, UNINCORPORATED_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, CHILD_ELEMENT, UNINCORPORATED_ELEMENT);
runtimeCheck(true, RELOAD_ELEMENT, CHILD_ELEMENT, UNINCORPORATED_ELEMENT, INFANT_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT, CHILD_ELEMENT, UNINCORPORATED_ELEMENT, INFANT_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
}
// Check that root resource requiring reload doesn't prevent runtime execution of
// /child=test, since root resource capabilities don't block children
@Test
public void testRootExclusion() throws OperationFailedException {
executeCheckNoFailure(Util.createEmptyOperation("root-cap", PathAddress.EMPTY_ADDRESS));
try {
requireReload();
runtimeCheck(false);
runtimeOnlyCheck(true);
runtimeCheck(true, CHILD_ELEMENT);
runtimeOnlyCheck(true, CHILD_ELEMENT);
runtimeCheck(true, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
// Now add the reload subsystem so its capability is registered
add(RELOAD_ELEMENT);
try {
runtimeCheck(true, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
// Now trigger reload-required from the reload subsystem
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
} finally {
//noinspection ThrowFromFinallyBlock
executeCheckNoFailure(Util.createEmptyOperation("no-root-cap", PathAddress.EMPTY_ADDRESS));
}
}
// Check that /host=test resource requiring reload doesn't prevent runtime execution of
// /host=test/child=test, since host root resource capabilities don't block children
@Test
public void testHostRootExclusion() throws OperationFailedException {
add(HOST_ELEMENT);
try {
requireReload(HOST_ELEMENT);
runtimeCheck(false, HOST_ELEMENT);
runtimeOnlyCheck(true, HOST_ELEMENT);
runtimeCheck(true, HOST_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, HOST_ELEMENT, CHILD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(HOST_ELEMENT);
}
}
// Check that /profile=test resource requiring reload doesn't prevent runtime execution of
// /profile=test/subsystem=no-cap, since kernel resource capabilities don't block subsystems
@Test
public void testProfileExclusion() throws OperationFailedException {
add(PROFILE_ELEMENT);
try {
requireReload(PROFILE_ELEMENT);
runtimeCheck(false, PROFILE_ELEMENT);
runtimeOnlyCheck(true, PROFILE_ELEMENT);
runtimeCheck(true, PROFILE_ELEMENT, NO_CAP_ELEMENT);
runtimeOnlyCheck(true, PROFILE_ELEMENT, NO_CAP_ELEMENT);
runtimeCheck(true, PROFILE_ELEMENT, NO_CAP_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, PROFILE_ELEMENT, NO_CAP_ELEMENT, CHILD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(PROFILE_ELEMENT);
}
}
// Transitive dependency check
// Check that subsystem=dep-cap requires reload once the root resource "root-cap"
// does, because the subsystem=dep-cap's capability requires root-cap.
@Test
public void testDependentCapabilities() throws OperationFailedException {
executeCheckNoFailure(Util.createEmptyOperation("root-cap", PathAddress.EMPTY_ADDRESS));
try {
add(DEP_CAP_ELEMENT);
try {
add(DEP_CAP_ELEMENT, CHILD_ELEMENT);
try {
runtimeCheck(true, DEP_CAP_ELEMENT);
runtimeOnlyCheck(true, DEP_CAP_ELEMENT);
runtimeCheck(true, DEP_CAP_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, DEP_CAP_ELEMENT, CHILD_ELEMENT);
requireReload();
runtimeCheck(false, DEP_CAP_ELEMENT);
runtimeOnlyCheck(true, DEP_CAP_ELEMENT);
runtimeCheck(false, DEP_CAP_ELEMENT, CHILD_ELEMENT);
runtimeOnlyCheck(true, DEP_CAP_ELEMENT, CHILD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(DEP_CAP_ELEMENT, CHILD_ELEMENT);
}
} finally {
//noinspection ThrowFromFinallyBlock
remove(DEP_CAP_ELEMENT);
}
} finally {
//noinspection ThrowFromFinallyBlock
executeCheckNoFailure(Util.createEmptyOperation("no-root-cap", PathAddress.EMPTY_ADDRESS));
}
}
@Test
public void testClear() throws OperationFailedException, InterruptedException {
add(RELOAD_ELEMENT);
try {
requireReload(RELOAD_ELEMENT);
runtimeCheck(false, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
// Do a mock reload
CountDownLatch latch = new CountDownLatch(1);
ServiceController<?> svc = container.getRequiredService(ServiceName.of("ModelController"));
svc.addListener(new AbstractServiceListener<Object>(){
@Override
public void listenerAdded(ServiceController<?> controller) {
controller.setMode(ServiceController.Mode.NEVER);
}
@Override
public void transition(ServiceController<?> controller, ServiceController.Transition transition) {
if (transition == ServiceController.Transition.STOPPING_to_DOWN) {
controller.setMode(ServiceController.Mode.ACTIVE);
} else if (transition == ServiceController.Transition.STARTING_to_UP) {
controller.removeListener(this);
latch.countDown();
}
}
});
Assert.assertTrue("Failed to reload", latch.await(30, TimeUnit.SECONDS));
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
Assert.assertEquals(0, capabilityRegistry.getPossibleCapabilities().size());
runtimeCheck(true, RELOAD_ELEMENT);
runtimeOnlyCheck(true, RELOAD_ELEMENT);
} finally {
//noinspection ThrowFromFinallyBlock
remove(RELOAD_ELEMENT);
}
}
@Test
public void testConcurrentRead() throws OperationFailedException, InterruptedException {
readLatchHolder.set(new CountDownLatch(1));
ModelNode addOp = Util.createAddOperation(TEST_ADDRESS5);
Thread t = new Thread(() -> executeForFailure(addOp));
t.setDaemon(true);
t.start();
try {
// Wait for the add op to reach Stage.RUNTIME, so the cap is registered
readLatchHolder.get().await();
CountDownLatch failLatch = failLatchHolder.get();
Assert.assertNotNull(failLatch);
// add op's registry change should not be visible
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
// Execute a concurrent read
executeCheckNoFailure(Util.createEmptyOperation("read", TEST_ADDRESS5));
// Confirm add op is still blocking
Assert.assertTrue(t.isAlive());
// add op's registry change should not be visible
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
// Let the add op release and fail
failLatch.countDown();
// Wait for add op to finish and then check the registry
t.join(30000);
Assert.assertFalse(t.isAlive());
Assert.assertEquals(0, capabilityRegistry.getCapabilities().size());
} finally {
if (t.isAlive()) {
t.interrupt();
}
}
}
private void add(PathElement... address) throws OperationFailedException {
executeCheckNoFailure(Util.createEmptyOperation("add", PathAddress.pathAddress(address)));
}
private void remove(PathElement... address) throws OperationFailedException {
executeCheckNoFailure(Util.createEmptyOperation("remove", PathAddress.pathAddress(address)));
}
private void requireReload(PathElement... elements) throws OperationFailedException {
PathAddress address = PathAddress.pathAddress(elements);
executeCheckNoFailure(Util.createEmptyOperation(RELOAD_DEFINITION.getName(), address));
}
private void runtimeCheck(boolean expectResult, PathElement... elements) throws OperationFailedException {
runtimeCheck(false, expectResult, elements);
}
private void runtimeOnlyCheck(boolean expectResult, PathElement... elements) throws OperationFailedException {
runtimeCheck(true, expectResult, elements);
}
private void runtimeCheck(boolean runtimeOnly, boolean expectResult, PathElement... elements) throws OperationFailedException {
OperationDefinition opDef = runtimeOnly ? RUNTIME_ONLY_DEFINITION : RUNTIME_MOD_DEFINITION;
PathAddress address = PathAddress.pathAddress(elements);
ModelNode op = Util.createEmptyOperation(opDef.getName(), address);
ModelNode result = executeForResult(op);
if (expectResult) {
Assert.assertTrue(result.toString(), result.isDefined());
Assert.assertTrue(result.toString(), result.asBoolean());
} else {
Assert.assertFalse(result.toString(), result.isDefined());
}
}
}