/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.controller.services.path; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; 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.OPERATIONS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ONLY; 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.RECURSIVE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RELATIVE_TO; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UNDEFINE_ATTRIBUTE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.jboss.as.controller.ManagementModel; import org.jboss.as.controller.OperationFailedException; 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.services.path.PathManager.Event; import org.jboss.as.controller.services.path.PathManager.PathEventContext; import org.jboss.as.controller.test.AbstractControllerTestBase; import org.jboss.dmr.ModelNode; import org.jboss.msc.service.AbstractServiceListener; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceController.State; import org.jboss.msc.service.ServiceController.Transition; import org.jboss.msc.service.ServiceName; import org.junit.Assert; import org.junit.Test; /** * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> */ public class PathsTestCase extends AbstractControllerTestBase { PathManagerService pathManagerService; @Test public void testReadResourceDescription() throws Exception { //Just a sanity check to make sure the resource description can be read ModelNode operation = createOperation(READ_RESOURCE_DESCRIPTION_OPERATION); operation.get(RECURSIVE).set(true); operation.get(OPERATIONS).set(true); ModelNode result = executeForResult(operation); } @Test public void testAddPath() throws Exception { ModelNode result = readResource(); Assert.assertEquals(1, result.get(PATH).keys().size()); checkPath(result, "hardcoded", "/hard/coded", null, true); ModelNode operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(PATH).set("xyz"); executeForResult(operation); result = readResource(); Assert.assertTrue(result.hasDefined(PATH)); Assert.assertEquals(2, result.get(PATH).keys().size()); checkPath(result, "hardcoded", "/hard/coded", null, true); checkPath(result, "add1", "xyz", null, false); operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(PATH).set("123"); operation.get(RELATIVE_TO).set("add1"); executeForResult(operation); result = readResource(); Assert.assertTrue(result.hasDefined(PATH)); Assert.assertEquals(3, result.get(PATH).keys().size()); checkPath(result, "hardcoded", "/hard/coded", null, true); checkPath(result, "add1", "xyz", null, false); checkPath(result, "add2", "123", "add1", false); } /** * https://issues.jboss.org/browse/AS7-4917 */ @Test public void testAddPathWithExpression() throws Exception { String key = "my.path.expression"; String value = "log1234"; try { System.setProperty(key, value); ModelNode operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "path_with_expression"); operation.get(PATH).set("/path/${" + key + "}"); executeForResult(operation); ModelNode result = readResource(); Assert.assertTrue(result.hasDefined(PATH)); Assert.assertEquals(2, result.get(PATH).keys().size()); checkPath(result, "path_with_expression", "/path/${" + key + "}", null, false); checkServiceAndPathEntry("path_with_expression", "/path/" + value, null); } finally { System.clearProperty(key); } } @Test public void testRemovePath() throws Exception { testAddPath(); ModelNode operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add2"); executeForResult(operation); ModelNode result = readResource(); Assert.assertTrue(result.hasDefined(PATH)); Assert.assertEquals(2, result.get(PATH).keys().size()); checkPath(result, "hardcoded", "/hard/coded", null, true); checkPath(result, "add1", "xyz", null, false); operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add1"); executeForResult(operation); result = readResource(); checkPath(result, "hardcoded", "/hard/coded", null, true); } @Test public void testChangeAbsolutePath() throws Exception { testAddPath(); ModelNode operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(NAME).set(PATH); operation.get(VALUE).set("updated"); executeForResult(operation); checkPath(readResource(), "add1", "updated", null, false); } @Test public void testChangeRelativePath() throws Exception { testAddPath(); ModelNode operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(PATH); operation.get(VALUE).set("updated"); executeForResult(operation); checkPath(readResource(), "add2", "updated", "add1", false); } @Test public void testChangeBetweenRelativeAndAbsolutePaths() throws Exception { testAddPath(); ModelNode operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set(new ModelNode()); executeForResult(operation); checkPath(readResource(), "add2", "123", null, false); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add1"); executeForResult(operation); checkPath(readResource(), "add2", "123", "add1", false); operation = createOperation(UNDEFINE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(RELATIVE_TO); executeForResult(operation); checkPath(readResource(), "add2", "123", null, false); } @Test public void testRemoveHardcodedPathFails() throws Exception { ModelNode result = readResource(); Assert.assertEquals(1, result.get(PATH).keys().size()); checkPath(result, "hardcoded", "/hard/coded", null, true); ModelNode operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "hardcoded"); try { executeForResult(operation); Assert.fail("Removing a read-only path should have failed"); } catch (OperationFailedException expected) { } checkPath(result, "hardcoded", "/hard/coded", null, true); } @Test public void testChangeHardcodedPathFails() throws Exception { testAddPath(); checkPath(readResource(), "hardcoded", "/hard/coded", null, true); ModelNode operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "hardcoded"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add1"); try { executeForResult(operation); Assert.fail("Changing a read-only path should have failed"); } catch (OperationFailedException expected) { } checkPath(readResource(), "hardcoded", "/hard/coded", null, true); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "hardcoded"); operation.get(NAME).set(PATH); operation.get(VALUE).set("abc"); try { executeForResult(operation); Assert.fail("Changing a read-only path should have failed"); } catch (OperationFailedException expected) { } checkPath(readResource(), "hardcoded", "/hard/coded", null, true); } @Test public void testLegacyPathServices() throws Exception { getContainer().getRequiredService(AbstractPathService.pathNameOf("hardcoded")); ServiceName name1 = AbstractPathService.pathNameOf("add1"); ServiceName name2 = AbstractPathService.pathNameOf("add2"); ServiceName name3 = AbstractPathService.pathNameOf("add3"); Assert.assertNull(getContainer().getService(name1)); Assert.assertNull(getContainer().getService(name2)); TestServiceListener listener = new TestServiceListener(); getContainer().addListener(listener); listener.reset(1); ModelNode operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(PATH).set("xyz"); executeForResult(operation); listener.latch.await(); Assert.assertEquals(new ExpectedResultBuilder().addUp(name1).map, listener.services); checkServiceAndPathEntry("add1", "xyz", null); listener.reset(1); operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(PATH).set("abc"); executeForResult(operation); listener.latch.await(); Assert.assertEquals(new ExpectedResultBuilder().addUp(name2).map, listener.services); checkServiceAndPathEntry("add2", "abc", null); listener.reset(1); operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(PATH).set("456"); operation.get(RELATIVE_TO).set("add1"); executeForResult(operation); listener.latch.await(); ServiceController<?> original3 = getContainer().getRequiredService(name3); checkServiceAndPathEntry("add1", "xyz", null); checkServiceAndPathEntry("add2", "abc", null); checkServiceAndPathEntry("add3", "456", "add1"); listener.reset(2); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(PATH); operation.get(VALUE).set("new-value"); executeForResult(operation); listener.latch.await(); Assert.assertEquals(new ExpectedResultBuilder().addRemove(name3).addUp(name3).map, listener.services); checkServiceAndPathEntry("add3", "new-value", "add1"); listener.reset(2); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add2"); executeForResult(operation); listener.latch.await(); Assert.assertEquals(new ExpectedResultBuilder().addRemove(name3).addUp(name3).map, listener.services); checkServiceAndPathEntry("add3", "new-value", "add2"); listener.reset(2); operation = createOperation(UNDEFINE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(RELATIVE_TO); executeForResult(operation); listener.latch.await(); Assert.assertEquals(new ExpectedResultBuilder().addRemove(name3).addUp(name3).map, listener.services); checkServiceAndPathEntry("add3", "new-value", null); listener.reset(2); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(PATH); operation.get(VALUE).set("newer-value"); executeForResult(operation); listener.latch.await(); Assert.assertEquals(new ExpectedResultBuilder().addRemove(name3).addUp(name3).map, listener.services); checkServiceAndPathEntry("add3", "newer-value", null); listener.reset(2); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add1"); executeForResult(operation); listener.latch.await(); Assert.assertEquals(new ExpectedResultBuilder().addRemove(name3).addUp(name3).map, listener.services); checkServiceAndPathEntry("add3", "newer-value", "add1"); listener.reset(1); operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add3"); executeForResult(operation); listener.latch.await(); Assert.assertNull(getContainer().getService(name3)); Assert.assertEquals(new ExpectedResultBuilder().addRemove(name3).map, listener.services); } private void checkServiceAndPathEntry(String name, String path, String relativeTo) { ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); Assert.assertEquals(State.UP, pathManagerService.getState()); PathManagerService pathManager = (PathManagerService) pathManagerService.getValue(); ServiceController<?> pathService = getContainer().getRequiredService(AbstractPathService.pathNameOf(name)); String servicePath = (String) pathService.getValue(); PathEntry pathEntry = pathManager.getPathEntry(name); Assert.assertNotNull(pathEntry); Assert.assertEquals(name, pathEntry.getName()); Assert.assertEquals(path, pathEntry.getPath()); Assert.assertEquals(relativeTo, pathEntry.getRelativeTo()); Assert.assertEquals(servicePath, pathEntry.resolvePath()); } @Test public void testPathManagerAddNotifications() throws Exception { ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); PathManager pathManager = (PathManager) pathManagerService.getValue(); PerformChangeCallback allCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback addCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED); PerformChangeCallback changeCallback1 = new PerformChangeCallback(pathManager, "add1", Event.UPDATED); PerformChangeCallback removeCallback1 = new PerformChangeCallback(pathManager, "add1", Event.REMOVED); PerformChangeCallback allCallback2 = new PerformChangeCallback(pathManager, "add2", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback allCallback3 = new PerformChangeCallback(pathManager, "add3", Event.ADDED, Event.REMOVED, Event.UPDATED); //Test callbacks for 1 ModelNode operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(PATH).set("xyz"); executeForResult(operation); allCallback1.checkEvent(Event.ADDED, "add1", "xyz", null); allCallback1.checkDone(); addCallback1.checkEvent(Event.ADDED, "add1", "xyz", null); addCallback1.checkDone(); changeCallback1.checkDone(); removeCallback1.checkDone(); allCallback2.checkDone(); allCallback3.checkDone(); //Test callbacks for 2 operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(PATH).set("123"); operation.get(RELATIVE_TO).set("add1"); executeForResult(operation); allCallback1.checkDone(); addCallback1.checkDone(); changeCallback1.checkDone(); removeCallback1.checkDone(); allCallback2.checkEvent(Event.ADDED, "add2", "123", "add1"); allCallback2.checkDone(); allCallback3.checkDone(); //Check the removal of the callback worked allCallback3.remove(); operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(PATH).set("abc"); executeForResult(operation); allCallback1.checkDone(); addCallback1.checkDone(); changeCallback1.checkDone(); removeCallback1.checkDone(); allCallback2.checkDone(); allCallback3.checkDone(); } @Test public void testPathManagerRemoveNotifications() throws Exception { testAddPath(); ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); PathManager pathManager = (PathManager) pathManagerService.getValue(); PerformChangeCallback allCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback addCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED); PerformChangeCallback changeCallback1 = new PerformChangeCallback(pathManager, "add1", Event.UPDATED); PerformChangeCallback removeCallback1 = new PerformChangeCallback(pathManager, "add1", Event.REMOVED); PerformChangeCallback allCallback2 = new PerformChangeCallback(pathManager, "add2", Event.ADDED, Event.REMOVED, Event.UPDATED); //Test callbacks for 2 ModelNode operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add2"); executeForResult(operation); allCallback1.checkDone(); addCallback1.checkDone(); changeCallback1.checkDone(); removeCallback1.checkDone(); allCallback2.checkEvent(Event.REMOVED, "add2", "123", "add1"); allCallback2.checkDone(); //Test callbacks for 1 operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add1"); executeForResult(operation); allCallback1.checkEvent(Event.REMOVED, "add1", "xyz", null); allCallback1.checkDone(); addCallback1.checkDone(); changeCallback1.checkDone(); removeCallback1.checkEvent(Event.REMOVED, "add1", "xyz", null); removeCallback1.checkDone(); allCallback2.checkDone(); //Test that the removed callbacks don't get triggered allCallback1.remove(); addCallback1.remove(); changeCallback1.remove(); removeCallback1.remove(); allCallback2.remove(); testAddPath(); operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add2"); executeForResult(operation); operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add1"); executeForResult(operation); allCallback1.checkDone(); addCallback1.checkDone(); changeCallback1.checkDone(); removeCallback1.checkDone(); allCallback2.checkDone(); } @Test public void testPathManagerChangeNotifications() throws Exception { testAddPath(); ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); PathManager pathManager = (PathManager) pathManagerService.getValue(); ModelNode operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(PATH).set("xyz"); executeForResult(operation); PerformChangeCallback allCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback allCallback2 = new PerformChangeCallback(pathManager, "add2", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback addCallback2 = new PerformChangeCallback(pathManager, "add2", Event.ADDED); PerformChangeCallback changeCallback2 = new PerformChangeCallback(pathManager, "add2", Event.UPDATED); PerformChangeCallback removeCallback2 = new PerformChangeCallback(pathManager, "add2", Event.REMOVED); //Test callbacks for 2 relative to absolute operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add3"); executeForResult(operation); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "123", "add3"); allCallback2.checkDone(); addCallback2.checkDone(); changeCallback2.checkEvent(Event.UPDATED, "add2", "123", "add3"); changeCallback2.checkDone(); removeCallback2.checkDone(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set(new ModelNode()); executeForResult(operation); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "123", null); allCallback2.checkDone(); addCallback2.checkDone(); changeCallback2.checkEvent(Event.UPDATED, "add2", "123", null); changeCallback2.checkDone(); removeCallback2.checkDone(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(PATH); operation.get(VALUE).set("abc"); executeForResult(operation); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "abc", null); allCallback2.checkDone(); addCallback2.checkDone(); changeCallback2.checkEvent(Event.UPDATED, "add2", "abc", null); changeCallback2.checkDone(); removeCallback2.checkDone(); //Test callbacks for 2 relative to absolute operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add2"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add1"); executeForResult(operation); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "abc", "add1"); allCallback2.checkDone(); addCallback2.checkDone(); changeCallback2.checkEvent(Event.UPDATED, "add2", "abc", "add1"); changeCallback2.checkDone(); removeCallback2.checkDone(); } @Test public void testCannotRemoveDependentService() throws Exception { testAddPath(); ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); PathManager pathManager = (PathManager) pathManagerService.getValue(); PerformChangeCallback allCallback1 = new PerformChangeCallback(pathManager, "add2", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback allCallback2 = new PerformChangeCallback(pathManager, "add2", Event.ADDED, Event.REMOVED, Event.UPDATED); checkServiceAndPathEntry("add1", "xyz", null); checkServiceAndPathEntry("add2", "123", "add1"); ModelNode operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add1"); executeForFailure(operation); allCallback1.checkDone(); allCallback2.checkDone(); checkServiceAndPathEntry("add1", "xyz", null); checkServiceAndPathEntry("add2", "123", "add1"); } @Test public void testBadAdd() throws Exception { ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); PathManager pathManager = (PathManager) pathManagerService.getValue(); PerformChangeCallback allCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED, Event.REMOVED, Event.UPDATED); ModelNode operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(PATH).set("123"); operation.get(RELATIVE_TO).set("bad"); executeForFailure(operation); try { ServiceController<?> svc = getContainer().getRequiredService(AbstractPathService.pathNameOf("add1")); if (svc.getState() == State.UP) { Assert.fail("Should not managed to install service"); } } catch (Exception expected) { } allCallback1.checkEvent(Event.ADDED, "add1", "123", "bad"); allCallback1.checkEvent(Event.REMOVED, "add1", "123", "bad"); allCallback1.checkDone(); } @Test public void testBadChangeNoNotification() throws Exception { ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); PathManager pathManager = (PathManager) pathManagerService.getValue(); PerformChangeCallback allCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED, Event.REMOVED, Event.UPDATED); ModelNode operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(PATH).set("123"); executeForResult(operation); allCallback1.checkEvent(Event.ADDED, "add1", "123", null); allCallback1.checkDone(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("bad"); //TODO I changed this to fail, not 100% sure that is correct executeForFailure(operation); ServiceController<?> svc = getContainer().getRequiredService(AbstractPathService.pathNameOf("add1")); allCallback1.checkDone(); checkServiceAndPathEntry("add1", "123", null); } @Test public void testChangeDependentServiceNotificationIsCascaded() throws Exception { testAddPath(); ServiceController<?> pathManagerService = getContainer().getRequiredService(PathManagerService.SERVICE_NAME); PathManager pathManager = (PathManager) pathManagerService.getValue(); PerformChangeCallback allCallback1 = new PerformChangeCallback(pathManager, "add1", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback allCallback2 = new PerformChangeCallback(pathManager, "add2", Event.ADDED, Event.REMOVED, Event.UPDATED); PerformChangeCallback allCallback3 = new PerformChangeCallback(pathManager, "add3", Event.ADDED, Event.REMOVED, Event.UPDATED); checkServiceAndPathEntry("add1", "xyz", null); checkServiceAndPathEntry("add2", "123", "add1"); ModelNode operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(NAME).set(PATH); operation.get(VALUE).set("abc"); executeForResult(operation); checkServiceAndPathEntry("add1", "abc", null); checkServiceAndPathEntry("add2", "123", "add1"); allCallback1.checkEvent(Event.UPDATED, "add1", "abc", null); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "123", "add1"); allCallback2.checkDone(); allCallback3.checkDone(); operation = createOperation(ADD); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(PATH); operation.get(PATH).set("def"); operation.get(RELATIVE_TO).set("add2"); executeForResult(operation); allCallback1.clear(); allCallback2.clear(); allCallback3.clear(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(NAME).set(PATH); operation.get(VALUE).set("456"); executeForResult(operation); checkServiceAndPathEntry("add1", "456", null); checkServiceAndPathEntry("add2", "123", "add1"); checkServiceAndPathEntry("add3", "def", "add2"); allCallback1.checkEvent(Event.UPDATED, "add1", "456", null); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "123", "add1"); allCallback2.checkDone(); allCallback3.checkEvent(Event.UPDATED, "add3", "def", "add2"); allCallback3.checkDone(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set(new ModelNode()); executeForResult(operation); allCallback1.clear(); allCallback2.clear(); allCallback3.clear(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(NAME).set(PATH); operation.get(VALUE).set("abc"); executeForResult(operation); checkServiceAndPathEntry("add1", "abc", null); checkServiceAndPathEntry("add2", "123", "add1"); checkServiceAndPathEntry("add3", "def", null); allCallback1.checkEvent(Event.UPDATED, "add1", "abc", null); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "123", "add1"); allCallback2.checkDone(); allCallback3.checkDone(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add2"); executeForResult(operation); allCallback1.clear(); allCallback2.clear(); allCallback3.clear(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(NAME).set(PATH); operation.get(VALUE).set("456"); executeForResult(operation); checkServiceAndPathEntry("add1", "456", null); checkServiceAndPathEntry("add2", "123", "add1"); checkServiceAndPathEntry("add3", "def", "add2"); allCallback1.checkEvent(Event.UPDATED, "add1", "456", null); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "123", "add1"); allCallback2.checkDone(); allCallback3.checkEvent(Event.UPDATED, "add3", "def", "add2"); allCallback3.checkDone(); operation = createOperation(REMOVE); operation.get(OP_ADDR).add(PATH, "add3"); operation.get(NAME).set(RELATIVE_TO); operation.get(VALUE).set("add2"); executeForResult(operation); allCallback1.clear(); allCallback2.clear(); allCallback3.clear(); operation = createOperation(WRITE_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(PATH, "add1"); operation.get(NAME).set(PATH); operation.get(VALUE).set("abc"); executeForResult(operation); checkServiceAndPathEntry("add1", "abc", null); checkServiceAndPathEntry("add2", "123", "add1"); allCallback1.checkEvent(Event.UPDATED, "add1", "abc", null); allCallback1.checkDone(); allCallback2.checkEvent(Event.UPDATED, "add2", "123", "add1"); allCallback2.checkDone(); allCallback3.checkDone(); } private void checkPath(ModelNode result, String pathName, String path, String relativeTo, boolean readOnly) { Assert.assertTrue(result.get(PATH).hasDefined(pathName)); Assert.assertTrue(result.get(PATH, pathName).hasDefined(NAME)); Assert.assertEquals(pathName, result.get(PATH, pathName, NAME).asString()); Assert.assertTrue(result.get(PATH, pathName).hasDefined(PATH)); Assert.assertEquals(path, result.get(PATH, pathName, PATH).asString()); if (relativeTo == null) { Assert.assertFalse(result.get(PATH, pathName).hasDefined(RELATIVE_TO)); } else { Assert.assertTrue(result.get(PATH, pathName).hasDefined(RELATIVE_TO)); Assert.assertEquals(relativeTo, result.get(PATH, pathName, RELATIVE_TO).asString()); } Assert.assertEquals(readOnly, result.get(PATH, pathName, READ_ONLY).asBoolean()); } private ModelNode readResource() throws Exception { ModelNode operation = createOperation(READ_RESOURCE_OPERATION); operation.get(RECURSIVE).set(true); operation.get(INCLUDE_RUNTIME).set(true); return executeForResult(operation); } @Override protected void initModel(ManagementModel managementModel) { ManagementResourceRegistration registration = managementModel.getRootResourceRegistration(); pathManagerService = new PathManagerService() { { super.addHardcodedAbsolutePath(getContainer(), "hardcoded", "/hard/coded"); } }; GlobalOperationHandlers.registerGlobalOperations(registration, processType); GlobalNotifications.registerGlobalNotifications(registration, processType); TestServiceListener listener = new TestServiceListener(); listener.reset(1); getContainer().addService(PathManagerService.SERVICE_NAME, pathManagerService) .addListener(listener) .install(); try { listener.latch.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } registration.registerSubModel(PathResourceDefinition.createSpecified(pathManagerService)); pathManagerService.addPathManagerResources(managementModel.getRootResource()); } private class TestServiceListener extends AbstractServiceListener<Object> { volatile CountDownLatch latch; Map<Transition, ServiceName> services = Collections.synchronizedMap(new LinkedHashMap<ServiceController.Transition, ServiceName>()); void reset(int count) { latch = new CountDownLatch(count); services.clear(); } public void transition(ServiceController<? extends Object> controller, Transition transition) { if (transition == Transition.STARTING_to_UP || transition == Transition.REMOVING_to_REMOVED) { services.put(transition, controller.getName()); latch.countDown(); } } } private static class ExpectedResultBuilder { LinkedHashMap<Transition, ServiceName> map = new LinkedHashMap<ServiceController.Transition, ServiceName>(); ExpectedResultBuilder addUp(ServiceName name) { map.put(Transition.STARTING_to_UP, name); return this; } ExpectedResultBuilder addRemove(ServiceName name) { map.put(Transition.REMOVING_to_REMOVED, name); return this; } } private static class PerformChangeCallback implements PathManager.Callback { private LinkedHashMap<Event, PathEntry> paths = new LinkedHashMap<PathManager.Event, PathEntry>(); private Handle handle; PerformChangeCallback(PathManager pathManager, String pathName, Event... events) { if (handle != null) { throw new IllegalStateException("Already registered"); } handle = pathManager.registerCallback(pathName, this, events); } void remove() { handle.remove(); handle = null; } void clear() { paths.clear(); } @Override public void pathEvent(Event event, PathEntry pathEntry) { paths.put(event, pathEntry); } void checkEvent(Event event, String name, String path, String relativeTo) { Iterator<Map.Entry<Event, PathEntry>> it = paths.entrySet().iterator(); Assert.assertTrue(it.hasNext()); Map.Entry<Event, PathEntry> entry = it.next(); Assert.assertEquals(event, entry.getKey()); PathEntry pathEntry = entry.getValue(); Assert.assertEquals(name, pathEntry.getName()); Assert.assertEquals(path, pathEntry.getPath()); Assert.assertEquals(relativeTo, pathEntry.getRelativeTo()); it.remove(); } void checkDone() { Assert.assertTrue(paths.isEmpty()); } @Override public void pathModelEvent(PathEventContext eventContext, String name) { } } }