/* * JBoss, Home of Professional Open Source. * Copyright 2014, 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.test.integration.domain.suites; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.COMPOSITE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DIRECTORY_GROUPING; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INTERFACE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MANAGEMENT_MAJOR_VERSION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_GROUP_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_OPERATION_DESCRIPTION_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_CONFIG; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_GROUPS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SYSTEM_PROPERTY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.operations.common.Util; import org.jboss.as.test.integration.domain.management.util.DomainLifecycleUtil; import org.jboss.as.test.integration.domain.management.util.DomainTestSupport; import org.jboss.as.test.integration.domain.management.util.DomainTestUtils; import org.jboss.as.test.integration.management.util.MgmtOperationException; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.dmr.Property; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * @author Emanuel Muckenhuber */ public class WildcardOperationsTestCase { private static final String WILDCARD = "*"; private static final PathElement SYS_PROP_ELEMENT = PathElement.pathElement(SYSTEM_PROPERTY, "wildcard-composite-op"); private static DomainTestSupport testSupport; private static DomainLifecycleUtil domainMasterLifecycleUtil; @BeforeClass public static void setupDomain() throws Exception { testSupport = DomainTestSuite.createSupport(WildcardOperationsTestCase.class.getSimpleName()); domainMasterLifecycleUtil = testSupport.getDomainMasterLifecycleUtil(); } @AfterClass public static void tearDownDomain() throws Exception { testSupport = null; domainMasterLifecycleUtil = null; DomainTestSuite.stopSupport(); } @After public void cleanup() throws IOException { ModelNode op = Util.createRemoveOperation(PathAddress.pathAddress(SYS_PROP_ELEMENT)); domainMasterLifecycleUtil.getDomainClient().execute(op); } @Test public void testBasicWildcardOperations() throws IOException, MgmtOperationException { // host=* PathAddress hosts = PathAddress.pathAddress(HOST, WILDCARD); executeReadResource(hosts.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); executeReadResourceDescription(hosts.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); // host=*,server=* PathAddress servers = hosts.append(RUNNING_SERVER, WILDCARD); executeReadResource(servers.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); ServerWildcardChecker serverChecker = new ServerWildcardChecker(servers); executeReadResourceDescription(servers.toModelNode(), domainMasterLifecycleUtil.getDomainClient(), serverChecker); Assert.assertTrue(serverChecker.seenWildcardServer); Assert.assertTrue(1 < serverChecker.nonWildcardServers); // host=*,server=*,subsystem=* PathAddress subsystems = servers.append(SUBSYSTEM, WILDCARD); executeReadResource(subsystems.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); executeReadResourceDescription(subsystems.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); // host=*,server=*,interface=* (the resource definition here is actually against interface=* unlike the above) PathAddress interfaces = servers.append(INTERFACE, WILDCARD); executeReadResource(interfaces.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); WildcardRegistrationChecker interfaceChecker = new WildcardRegistrationChecker(INTERFACE); executeReadResourceDescription(interfaces.toModelNode(), domainMasterLifecycleUtil.getDomainClient(), interfaceChecker); } @Test public void testSpecificResourceOperationsUnderServer() throws IOException, MgmtOperationException { PathAddress servers = PathAddress.pathAddress(HOST, WILDCARD).append(RUNNING_SERVER, WILDCARD); PathAddress publicInterface = servers.append(INTERFACE, "public"); executeReadResource(publicInterface.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); executeReadResourceDescription(publicInterface.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); PathAddress jmxSubsystem = servers.append(SUBSYSTEM, "jmx"); executeReadResource(jmxSubsystem.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); executeReadResourceDescription(jmxSubsystem.toModelNode(), domainMasterLifecycleUtil.getDomainClient()); } @Test public void testReadOnlyCompositeOperationWithWildCards() throws IOException, MgmtOperationException { testCompositeOperationWithWildCards(true); } @Test public void testReadWriteCompositeOperationWithWildCards() throws IOException, MgmtOperationException { testCompositeOperationWithWildCards(false); } void testCompositeOperationWithWildCards(boolean readonly) throws IOException, MgmtOperationException { final ModelNode composite = new ModelNode(); composite.get(OP).set(COMPOSITE); final ModelNode steps = composite.get(STEPS); if (!readonly) { ModelNode syspropAdd = Util.createAddOperation(PathAddress.pathAddress(SYS_PROP_ELEMENT)); syspropAdd.get(VALUE).set("a"); steps.add().set(syspropAdd); } final ModelNode address = new ModelNode(); address.setEmptyList(); // Mix in a non-widlcard remote slave op address.add(HOST, "slave"); steps.add().set(createReadResourceOperation(address)); address.setEmptyList(); // host=* address.add(HOST, WILDCARD); steps.add().set(createReadResourceOperation(address)); steps.add().set(createReadResourceDescriptionOperation(address)); // host=*,server=* address.add(RUNNING_SERVER, WILDCARD); steps.add().set(createReadResourceOperation(address)); steps.add().set(createReadResourceDescriptionOperation(address)); // host=*,server=*,subsystem=* address.add(SUBSYSTEM, WILDCARD); steps.add().set(createReadResourceOperation(address)); steps.add().set(createReadResourceDescriptionOperation(address)); address.setEmptyList(); // Mix in a non-wildcard remote server op address.add(HOST, "master").add(RUNNING_SERVER, "main-one"); steps.add().set(createReadResourceOperation(address)); // Now repeat the whole thing, but nested final ModelNode nested = steps.add(); nested.get(OP).set(COMPOSITE); final ModelNode nestedSteps = nested.get(STEPS); if (!readonly) { ModelNode syspropUpdated = Util.getWriteAttributeOperation(PathAddress.pathAddress(SYS_PROP_ELEMENT), VALUE, "b"); nestedSteps.add().set(syspropUpdated); } address.setEmptyList(); // Mix in a non-wildcard remote slave op address.add(HOST, "slave"); nestedSteps.add().set(createReadResourceOperation(address)); address.setEmptyList(); // host=* address.add(HOST, WILDCARD); nestedSteps.add().set(createReadResourceOperation(address)); nestedSteps.add().set(createReadResourceDescriptionOperation(address)); // host=*,server=* address.add(RUNNING_SERVER, WILDCARD); nestedSteps.add().set(createReadResourceOperation(address)); nestedSteps.add().set(createReadResourceDescriptionOperation(address)); // host=*,server=*,subsystem=* address.add(SUBSYSTEM, WILDCARD); nestedSteps.add().set(createReadResourceOperation(address)); nestedSteps.add().set(createReadResourceDescriptionOperation(address)); address.setEmptyList(); // Mix in a non-wildcard remote server op address.add(HOST, "master").add(RUNNING_SERVER, "main-one"); nestedSteps.add().set(createReadResourceOperation(address)); final ModelNode response = domainMasterLifecycleUtil.getDomainClient().execute(composite); assertEquals(response.toString(), SUCCESS, response.get(OUTCOME).asString()); assertTrue(response.toString(), response.hasDefined(RESULT)); if (readonly) { assertFalse(response.toString(), response.has(SERVER_GROUPS)); } else { assertTrue(response.toString(), response.has(SERVER_GROUPS)); } validateWildCardCompositeResponse(response, readonly, true); } private void validateWildCardCompositeResponse(ModelNode response, boolean readOnly, boolean allowNested) { int i = 0; for (Property property : response.get(RESULT).asPropertyList()) { assertEquals(property.getName() + " from " + response, "step-" + (++i), property.getName()); ModelNode item = property.getValue(); assertEquals(property.getName() + " from " + response, ModelType.OBJECT, item.getType()); assertEquals(property.getName() + " from " + response, SUCCESS, item.get(OUTCOME).asString()); if (!readOnly && i == 1) { // skip remaining validation of this one continue; } assertTrue(property.getName() + " from " + response, item.hasDefined(RESULT)); ModelNode itemResult = item.get(RESULT); PathAddress pa = null; int validationIndex = readOnly ? i : i - 1; switch (validationIndex) { case 1: assertEquals(property.getName() + " result " + itemResult, ModelType.OBJECT, itemResult.getType()); assertTrue(property.getName() + " result " + itemResult, itemResult.hasDefined(MANAGEMENT_MAJOR_VERSION)); assertTrue(property.getName() + " result " + itemResult, itemResult.hasDefined(RUNNING_SERVER)); assertTrue(property.getName() + " result " + itemResult, itemResult.hasDefined(DIRECTORY_GROUPING)); assertEquals(property.getName() + " result " + itemResult, "slave", itemResult.get(NAME).asString()); break; case 2: case 3: pa = PathAddress.pathAddress(PathElement.pathElement(HOST)); case 4: case 5: if (pa == null) { pa = PathAddress.pathAddress(PathElement.pathElement(HOST), PathElement.pathElement(RUNNING_SERVER)); } case 6: case 7: if (pa == null) { pa = PathAddress.pathAddress(PathElement.pathElement(HOST), PathElement.pathElement(RUNNING_SERVER), PathElement.pathElement(SUBSYSTEM)); } //The issue with /host=*/server=*:read-resource-definition is that there will be an entry for each server, //along with an entry for /host=xxx/server=* boolean isReadResourceDescription = validationIndex % 2 != 0; if (isReadResourceDescription && pa.getLastElement().getKey().equals(RUNNING_SERVER)) { ServerWildcardChecker checker = new ServerWildcardChecker(pa); validateWildcardResponseList(pa, itemResult, checker); Assert.assertTrue(checker.seenWildcardServer); Assert.assertTrue(1 < checker.nonWildcardServers); } else { validateWildcardResponseList(pa, itemResult, new StandardWildcardChecker()); } break; case 8: assertEquals(property.getName() + " result " + itemResult, ModelType.OBJECT, itemResult.getType()); assertTrue(property.getName() + " result " + itemResult, itemResult.hasDefined(MANAGEMENT_MAJOR_VERSION)); assertTrue(property.getName() + " result " + itemResult, itemResult.hasDefined(SUBSYSTEM)); assertEquals(property.getName() + " result " + itemResult, "main-one", itemResult.get(NAME).asString()); assertEquals(property.getName() + " result " + itemResult, "master", itemResult.get(HOST).asString()); break; case 9: if (allowNested) { // recurse validateWildCardCompositeResponse(item, readOnly, false); break; } // else fall through default: throw new IllegalStateException(); } } } @Test public void testReadOperationDescription() throws Exception { // Domain level resource PathAddress pa = PathAddress.pathAddress(PathElement.pathElement(PATH)); readOperationDescriptionTest(pa); // Host level resource pa = PathAddress.pathAddress(PathElement.pathElement(HOST)); readOperationDescriptionTest(pa); // Host level child resource pa = PathAddress.pathAddress(PathElement.pathElement(HOST), PathElement.pathElement(SERVER_CONFIG)); readOperationDescriptionTest(pa); // Server level resource pa = PathAddress.pathAddress(PathElement.pathElement(HOST), PathElement.pathElement(RUNNING_SERVER)); readOperationDescriptionTest(pa); // Server level child resource pa = PathAddress.pathAddress(PathElement.pathElement(HOST), PathElement.pathElement(RUNNING_SERVER), PathElement.pathElement(SYSTEM_PROPERTY)); readOperationDescriptionTest(pa); } private void readOperationDescriptionTest(PathAddress pa) throws IOException, MgmtOperationException { readOperationDescriptionTest(READ_RESOURCE_OPERATION, pa); readOperationDescriptionTest(READ_ATTRIBUTE_OPERATION, pa); readOperationDescriptionTest(READ_ATTRIBUTE_GROUP_OPERATION, pa); readOperationDescriptionTest(WRITE_ATTRIBUTE_OPERATION, pa); } private void readOperationDescriptionTest(String opName, PathAddress pa) throws IOException, MgmtOperationException { ModelNode op = Util.createEmptyOperation(READ_OPERATION_DESCRIPTION_OPERATION, pa); op.get(NAME).set(opName); DomainTestUtils.executeForResult(op, domainMasterLifecycleUtil.getDomainClient()); } static ModelNode executeReadResource(final ModelNode address, final ModelControllerClient client) throws IOException, MgmtOperationException { final ModelNode operation = createReadResourceOperation(address); final ModelNode result = DomainTestUtils.executeForResult(operation, client); assertEquals(ModelType.LIST, result.getType()); final PathAddress toMatch = PathAddress.pathAddress(operation.get(OP_ADDR)); validateWildcardResponseList(toMatch, result, new StandardWildcardChecker()); return result; } static ModelNode createReadResourceOperation(final ModelNode address) { final ModelNode operation = new ModelNode(); operation.get(OP).set(READ_RESOURCE_OPERATION); operation.get(OP_ADDR).set(address); return operation; } static ModelNode executeReadResourceDescription(final ModelNode address, final ModelControllerClient client) throws IOException, MgmtOperationException { return executeReadResourceDescription(address, client, new StandardWildcardChecker()); } static ModelNode executeReadResourceDescription(final ModelNode address, final ModelControllerClient client, final WildcardChecker wildcardChecker) throws IOException, MgmtOperationException { final ModelNode operation = createReadResourceDescriptionOperation(address); final ModelNode result = DomainTestUtils.executeForResult(operation, client); assertEquals(ModelType.LIST, result.getType()); final PathAddress toMatch = PathAddress.pathAddress(operation.get(OP_ADDR)); WildcardChecker checker = wildcardChecker == null ? new StandardWildcardChecker() : wildcardChecker; validateWildcardResponseList(toMatch, result, checker); return result; } static ModelNode createReadResourceDescriptionOperation(final ModelNode address) { final ModelNode operation = new ModelNode(); operation.get(OP).set(READ_RESOURCE_DESCRIPTION_OPERATION); operation.get(OP_ADDR).set(address); return operation; } private static void validateWildcardResponseList(PathAddress toMatch, ModelNode responseList, WildcardChecker wildcardChecker) { assertEquals(responseList.toString(), ModelType.LIST, responseList.getType()); for (final ModelNode responseItem : responseList.asList()) { assertMatchingAddress(toMatch, responseItem, wildcardChecker); assertTrue(responseItem.toString(), responseItem.hasDefined(OUTCOME)); assertEquals(responseItem.toString(), SUCCESS, responseItem.get(OUTCOME).asString()); assertTrue(responseItem.toString(), responseItem.hasDefined(RESULT)); } } static void assertMatchingAddress(PathAddress toMatch, ModelNode node, WildcardChecker wildcardChecker) { assertTrue(node.hasDefined(OP_ADDR)); PathAddress testee = PathAddress.pathAddress(node.get(OP_ADDR)); assertEquals(testee + " length matches " + toMatch, toMatch.size(), testee.size()); for (int i = 0; i < toMatch.size(); i++) { PathElement matchElement = toMatch.getElement(i); PathElement testeeElement = testee.getElement(i); assertEquals(testee.toString(), matchElement.getKey(), testeeElement.getKey()); wildcardChecker.checkPathElement(testeeElement); if (!matchElement.isWildcard()) { if (matchElement.isMultiTarget()) { boolean matched = false; for (String option : matchElement.getSegments()) { if (option.equals(testeeElement.getValue())) { matched = true; break; } } assertTrue(testeeElement + " value in " + matchElement.getValue(), matched); } else { assertEquals(matchElement.getValue(), testeeElement.getValue()); } } } } private interface WildcardChecker { void checkPathElement(PathElement actualElement); } // The standard behavious is that nothing should be resolved private static class StandardWildcardChecker implements WildcardChecker { @Override public void checkPathElement(PathElement actualElement) { assertFalse(actualElement + " is multi-target", actualElement.isMultiTarget()); } } // When doing /host=*/server=*:read-resource-definition there will be the expected resolved /host=x/server=y elements, // but in addition there will be /host=x/server=*. private static class ServerWildcardChecker extends StandardWildcardChecker { private final PathAddress toMatch; private boolean seenWildcardServer; private int nonWildcardServers; private ServerWildcardChecker(PathAddress toMatch) { this.toMatch = toMatch; } @Override public void checkPathElement(PathElement actualElement) { if (!actualElement.getKey().equals(RUNNING_SERVER)) { super.checkPathElement(actualElement); } else { if (actualElement.isWildcard()) { seenWildcardServer = true; } else { nonWildcardServers++; } } } } private static class WildcardRegistrationChecker extends StandardWildcardChecker { private final Set<String> wildcardRegistrations; public WildcardRegistrationChecker(String...wildcardRegistrations) { this.wildcardRegistrations = new HashSet<>(Arrays.asList(wildcardRegistrations)); } @Override public void checkPathElement(PathElement actualElement) { if (!wildcardRegistrations.contains(actualElement.getKey())) { super.checkPathElement(actualElement); } else { Assert.assertTrue(actualElement.isWildcard()); } } } }