/* * JBoss, Home of Professional Open Source. * Copyright 2015, Red Hat, Inc., 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.controller.test; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILD_TYPE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_RUNTIME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_GROUP_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CHILDREN_RESOURCES_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RECURSIVE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.ManagementModel; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.SimpleResourceDefinition; import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver; import org.jboss.as.controller.logging.ControllerLogger; 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.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.PlaceholderResource; import org.jboss.as.controller.registry.Resource; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.junit.Before; import org.junit.Test; /** * WFCORE-573 -- tests of handling a resource that disappears in the middle of executing a read. * * @author Brian Stansberry (c) 2015 Red Hat Inc. */ public class DisappearingResourceTestCase extends AbstractControllerTestBase { private static final PathElement SUBSYSTEM_ELEMENT = PathElement.pathElement(SUBSYSTEM, "mysubsystem"); private static final PathAddress SUBSYSTEM_ADDRESS = PathAddress.pathAddress(SUBSYSTEM_ELEMENT); private static final String PARENT = "parent"; private static final PathElement PARENT_ELEMENT = PathElement.pathElement(PARENT, "A"); private static final PathAddress PARENT_ADDRESS = PathAddress.pathAddress(SUBSYSTEM_ELEMENT, PARENT_ELEMENT); private static final String CHILD = "child"; private static final PathElement CHILD_B_ELEMENT = PathElement.pathElement(CHILD, "B"); private static final PathElement CHILD_C_ELEMENT = PathElement.pathElement(CHILD, "C"); private static final PathElement CHILD_WILDCARD_ELEMENT = PathElement.pathElement(CHILD); private static final PathAddress CHILD_B_ADDRESS = PathAddress.pathAddress(PARENT_ADDRESS, CHILD_B_ELEMENT); private static final PathAddress CHILD_C_ADDRESS = PathAddress.pathAddress(PARENT_ADDRESS, CHILD_C_ELEMENT); private static final PathAddress CHILD_WILDCARD_ADDRESS = PathAddress.pathAddress(PARENT_ADDRESS, CHILD_WILDCARD_ELEMENT); private static final String ATTR = "attr"; private static final String GROUP = "group"; private CountDownLatch attributeInLatch; private CountDownLatch attributeOutLatch; private boolean discardC; private boolean discardParent; @Before public void setup() { attributeInLatch = new CountDownLatch(1); attributeOutLatch = new CountDownLatch(1); discardC = false; discardParent = false; } @Test public void testDisappearingTopLevelAttribute() throws Exception { attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_RESOURCE_OPERATION, CHILD_B_ADDRESS); ModelNode rsp = executeCheckForFailure(op); //noinspection ThrowableResultOfMethodCallIgnored assertTrue(rsp.toString(), rsp.get(FAILURE_DESCRIPTION).asString().contains(ControllerLogger.MGMT_OP_LOGGER.managementResourceNotFound(CHILD_B_ADDRESS).getMessage())); } @Test public void testDisappearingChildResourceAttribute() throws Exception { attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_RESOURCE_OPERATION, PARENT_ADDRESS); op.get(RECURSIVE).set(true); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertTrue(result.toString(), result.hasDefined(CHILD, "C", ATTR)); assertTrue(result.toString(), result.get(CHILD, "C", ATTR).asInt() == 1); assertFalse(result.toString(), result.get(CHILD).has("B")); } @Test public void testDisappearingChildrenResourcesAttribute() throws Exception { discardC = true; attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_RESOURCE_OPERATION, PARENT_ADDRESS); op.get(RECURSIVE).set(true); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertFalse(result.toString(), result.get(CHILD).has("B")); assertFalse(result.toString(), result.get(CHILD).has("C")); } @Test public void testDisappearingWildcardChildrenResourcesAttribute() throws Exception { discardC = true; attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_RESOURCE_OPERATION, CHILD_WILDCARD_ADDRESS); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.LIST, result.getType()); assertEquals(result.toString(), 0, result.asInt()); } @Test public void testDisappearingWildcardResourceAttribute() throws Exception { attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_RESOURCE_OPERATION, CHILD_WILDCARD_ADDRESS); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.LIST, result.getType()); assertEquals(result.toString(), 1, result.asInt()); ModelNode cResult = result.get(0); assertEquals(result.toString(), CHILD_C_ADDRESS, PathAddress.pathAddress(cResult.get(OP_ADDR))); assertTrue(result.toString(), cResult.hasDefined(RESULT, ATTR)); assertEquals(result.toString(), 1, cResult.get(RESULT, ATTR).asInt()); } @Test public void testResourceDisappearingInAssembly() throws Exception { ModelNode op = Util.createOperation(READ_RESOURCE_OPERATION, PARENT_ADDRESS); op.get(RECURSIVE).set(true); op.get(INCLUDE_RUNTIME).set(true); Runnable r = new Runnable() { @Override public void run() { try { attributeInLatch.await(300, TimeUnit.SECONDS); discardParent = true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } finally { attributeOutLatch.countDown(); } } }; Thread t = new Thread(r); t.start(); ModelNode rsp = executeCheckForFailure(op); //noinspection ThrowableResultOfMethodCallIgnored assertTrue(rsp.toString(), rsp.get(FAILURE_DESCRIPTION).asString().contains(ControllerLogger.MGMT_OP_LOGGER.managementResourceNotFound(PARENT_ADDRESS).getMessage())); } @Test public void testReadChildrenResourcesSingleDisappearing() throws Exception { attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_CHILDREN_RESOURCES_OPERATION, PARENT_ADDRESS); op.get(CHILD_TYPE).set(CHILD); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.OBJECT, result.getType()); assertEquals(result.toString(), 1, result.asInt()); assertTrue(result.toString(), result.hasDefined(CHILD_C_ELEMENT.getValue(), ATTR)); assertEquals(result.toString(), 1, result.get(CHILD_C_ELEMENT.getValue(), ATTR).asInt()); } @Test public void testReadChildrenResourcesAllDisappearing() throws Exception { discardC = true; attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_CHILDREN_RESOURCES_OPERATION, PARENT_ADDRESS); op.get(CHILD_TYPE).set(CHILD); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.OBJECT, result.getType()); assertEquals(result.toString(), 0, result.asInt()); } @Test public void testReadChildrenResourcesRecursive() throws Exception { attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_CHILDREN_RESOURCES_OPERATION, SUBSYSTEM_ADDRESS); op.get(CHILD_TYPE).set(PARENT); op.get(INCLUDE_RUNTIME).set(true); op.get(RECURSIVE).set(true); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.OBJECT, result.getType()); assertEquals(result.toString(), 1, result.asInt()); ModelNode parentResult = result.get(PARENT_ELEMENT.getValue()); assertTrue(result.toString(), parentResult.hasDefined(CHILD, "C", ATTR)); assertTrue(result.toString(), parentResult.get(CHILD, "C", ATTR).asInt() == 1); assertFalse(result.toString(), parentResult.get(CHILD).has("B")); } @Test public void testReadChildrenResourcesDisappearingInAssembly() throws Exception { ModelNode op = Util.createOperation(READ_CHILDREN_RESOURCES_OPERATION, SUBSYSTEM_ADDRESS); op.get(CHILD_TYPE).set(PARENT); op.get(RECURSIVE).set(true); op.get(INCLUDE_RUNTIME).set(true); Runnable r = new Runnable() { @Override public void run() { try { attributeInLatch.await(300, TimeUnit.SECONDS); discardParent = true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } finally { attributeOutLatch.countDown(); } } }; Thread t = new Thread(r); t.start(); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.OBJECT, result.getType()); assertEquals(result.toString(), 0, result.asInt()); } @Test public void testReadAttributeGroup() throws Exception { attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_ATTRIBUTE_GROUP_OPERATION, CHILD_B_ADDRESS); op.get(NAME).set(GROUP); ModelNode rsp = executeCheckForFailure(op); //noinspection ThrowableResultOfMethodCallIgnored assertTrue(rsp.toString(), rsp.get(FAILURE_DESCRIPTION).asString().contains(ControllerLogger.MGMT_OP_LOGGER.managementResourceNotFound(CHILD_B_ADDRESS).getMessage())); } @Test public void testReadAttributeGroupWildcardsAllDisappearing() throws Exception { discardC = true; attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_ATTRIBUTE_GROUP_OPERATION, CHILD_WILDCARD_ADDRESS); op.get(NAME).set(GROUP); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.LIST, result.getType()); assertEquals(result.toString(), 0, result.asInt()); } @Test public void testReadAttributeGroupWildcardsOneDisappearing() throws Exception { attributeOutLatch.countDown(); ModelNode op = Util.createOperation(READ_ATTRIBUTE_GROUP_OPERATION, CHILD_WILDCARD_ADDRESS); op.get(NAME).set(GROUP); op.get(INCLUDE_RUNTIME).set(true); ModelNode result = executeForResult(op); assertEquals(result.toString(), ModelType.LIST, result.getType()); assertEquals(result.toString(), 1, result.asInt()); ModelNode cResult = result.get(0); assertEquals(result.toString(), CHILD_C_ADDRESS, PathAddress.pathAddress(cResult.get(OP_ADDR))); assertTrue(result.toString(), cResult.hasDefined(RESULT, ATTR)); assertEquals(result.toString(), 1, cResult.get(RESULT, ATTR).asInt()); } @Override protected void initModel(ManagementModel managementModel) { ManagementResourceRegistration registration = managementModel.getRootResourceRegistration(); GlobalOperationHandlers.registerGlobalOperations(registration, processType); GlobalNotifications.registerGlobalNotifications(registration, processType); ManagementResourceRegistration subsystemRegistration = registration.registerSubModel( new SimpleResourceDefinition(SUBSYSTEM_ELEMENT, new NonResolvingResourceDescriptionResolver())); ManagementResourceRegistration parentReg = subsystemRegistration.registerSubModel( new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(PARENT_ELEMENT, new NonResolvingResourceDescriptionResolver()).setRuntime())); ManagementResourceRegistration runtimeResource = parentReg.registerSubModel( new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(CHILD_WILDCARD_ELEMENT, new NonResolvingResourceDescriptionResolver()).setRuntime())); AttributeDefinition runtimeAttr = TestUtils.createAttribute(ATTR, ModelType.LONG, GROUP); runtimeResource.registerReadOnlyAttribute(runtimeAttr, new OperationStepHandler() { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { if (!discardC && "C".equals(context.getCurrentAddressValue())) { context.getResult().set(1); } else { attributeInLatch.countDown(); try { attributeOutLatch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } throw new Resource.NoSuchResourceException(context.getCurrentAddress().getLastElement()); } } }); registration.registerOperationHandler(TestUtils.SETUP_OPERATION_DEF, new OperationStepHandler() { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { // no-op; just satisfy the test infrastructure's desire for an op } }); managementModel.getRootResource().registerChild(SUBSYSTEM_ELEMENT, new SubsystemResource()); } private class SubsystemResource extends PlaceholderResource.PlaceholderResourceEntry { private SubsystemResource() { super(PARENT_ELEMENT); } @Override public boolean hasChild(PathElement element) { return getChild(element) != null; } @Override public Resource getChild(PathElement element) { if (!discardParent && PARENT_ELEMENT.equals(element)) { return new ParentResource(); } return null; } @Override public Resource requireChild(PathElement element) { Resource child = getChild(element); if (child == null) { throw new NoSuchResourceException(element); } return child; } @Override public boolean hasChildren(String childType) { return getChildren(childType).size() > 0; } @Override public Set<String> getChildTypes() { return Collections.singleton(PARENT); } @Override public Set<String> getChildrenNames(String childType) { if (!discardParent && PARENT.equals(childType)) { return Collections.singleton(PARENT_ELEMENT.getValue()); } return Collections.emptySet(); } @Override public Set<ResourceEntry> getChildren(String childType) { if (!discardParent && PARENT.equals(childType)) { ResourceEntry entry = new ParentResource(); return Collections.singleton(entry); } return Collections.emptySet(); } } private class ParentResource extends PlaceholderResource.PlaceholderResourceEntry { private ParentResource() { super(PARENT_ELEMENT); } @Override public boolean hasChild(PathElement element) { return getChild(element) != null; } @Override public Resource getChild(PathElement element) { if (CHILD_B_ELEMENT.equals(element)) { return new PlaceholderResourceEntry(CHILD_B_ELEMENT); } else if (CHILD_C_ELEMENT.equals(element)) { return new PlaceholderResourceEntry(CHILD_C_ELEMENT); } return null; } @Override public Resource requireChild(PathElement element) { Resource child = getChild(element); if (child == null) { throw new NoSuchResourceException(element); } return child; } @Override public boolean hasChildren(String childType) { return getChildren(childType).size() > 0; } @Override public Set<String> getChildTypes() { return Collections.singleton(CHILD); } @Override public Set<String> getChildrenNames(String childType) { if (CHILD.equals(childType)) { return new HashSet<>(Arrays.asList("B", "C")); } return Collections.emptySet(); } @Override public Set<ResourceEntry> getChildren(String childType) { if (CHILD.equals(childType)) { ResourceEntry entryB = new PlaceholderResourceEntry(CHILD_B_ELEMENT); ResourceEntry entryC = new PlaceholderResourceEntry(CHILD_C_ELEMENT); return new HashSet<>(Arrays.asList(entryB, entryC)); } return Collections.emptySet(); } } }