/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, 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.domain.controller.operations;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DOMAIN_MODEL;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.GROUP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST;
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.PROFILE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_CONFIG;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_GROUP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING_GROUP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import java.io.File;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.CompositeOperationHandler;
import org.jboss.as.controller.ManagementModel;
import org.jboss.as.controller.ModelOnlyAddStepHandler;
import org.jboss.as.controller.ModelOnlyRemoveStepHandler;
import org.jboss.as.controller.ModelOnlyWriteAttributeHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationDefinition;
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.ProcessType;
import org.jboss.as.controller.ProxyController;
import org.jboss.as.controller.RunningMode;
import org.jboss.as.controller.RunningModeControl;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver;
import org.jboss.as.controller.extension.ExtensionRegistry;
import org.jboss.as.controller.extension.RuntimeHostControllerInfoAccessor;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
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.Resource;
import org.jboss.as.domain.controller.operations.deployment.SyncModelParameters;
import org.jboss.as.domain.controller.resources.ProfileResourceDefinition;
import org.jboss.as.domain.controller.resources.ServerGroupResourceDefinition;
import org.jboss.as.host.controller.ignored.IgnoredDomainResourceRegistry;
import org.jboss.as.host.controller.mgmt.HostControllerRegistrationHandler;
import org.jboss.as.host.controller.util.AbstractControllerTestBase;
import org.jboss.as.repository.ContentReference;
import org.jboss.as.repository.HostFileRepository;
import org.jboss.as.server.services.net.SocketBindingGroupResourceDefinition;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.junit.Assert;
/**
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
*/
public class AbstractOrderedChildResourceSyncModelTestCase extends AbstractControllerTestBase {
private final ExtensionRegistry extensionRegistry = new ExtensionRegistry(ProcessType.HOST_CONTROLLER, new RunningModeControl(RunningMode.NORMAL), null, null, null, RuntimeHostControllerInfoAccessor.SERVER);
private volatile IgnoredDomainResourceRegistry ignoredDomainResourceRegistry;
static final PathElement HOST_ELEMEMT = PathElement.pathElement(HOST, "slave");
static final PathElement PROFILE_ELEMENT = PathElement.pathElement(PROFILE, "test");
static final PathElement SUBSYSTEM_ELEMENT = PathElement.pathElement(SUBSYSTEM, "test");
static final PathElement ORDERED_CHILD = PathElement.pathElement("ordered-child");
static final PathElement NON_ORDERED_CHILD = PathElement.pathElement("non-ordered-child");
static final PathElement EXTRA_CHILD = PathElement.pathElement("extra-child");
static final AttributeDefinition ATTR = new SimpleAttributeDefinitionBuilder("attr", ModelType.STRING, true).build();
static final AttributeDefinition[] REQUEST_ATTRIBUTES = new AttributeDefinition[]{ATTR};
static final OperationDefinition TRIGGER_SYNC = new SimpleOperationDefinitionBuilder("trigger-sync", new NonResolvingResourceDescriptionResolver())
.addParameter(ATTR)
.build();
final boolean registerExtraChildren;
final boolean localIndexedAdd;
public AbstractOrderedChildResourceSyncModelTestCase(boolean registerExtraChildren, boolean localIndexedAdd) {
this.registerExtraChildren = registerExtraChildren;
this.localIndexedAdd = localIndexedAdd;
}
@Override
protected void initModel(ManagementModel managementModel) {
ManagementResourceRegistration registration = managementModel.getRootResourceRegistration();
GlobalOperationHandlers.registerGlobalOperations(registration, processType);
registration.registerOperationHandler(CompositeOperationHandler.DEFINITION, CompositeOperationHandler.INSTANCE);
registration.registerOperationHandler(TRIGGER_SYNC, new TriggerSyncHandler());
registration.registerOperationHandler(GenericModelDescribeOperationHandler.DEFINITION, GenericModelDescribeOperationHandler.INSTANCE, true);
registration.registerSubModel(new ServerGroupResourceDefinition(false, hostControllerInfo, new HostFileRepository() {
@Override
public File getDeploymentRoot(ContentReference reference) {
return null;
}
@Override
public File[] getDeploymentFiles(ContentReference reference) {
return null;
}
@Override
public void deleteDeployment(ContentReference reference) {
}
@Override
public File getFile(String relativePath) {
return null;
}
@Override
public File getConfigurationFile(String relativePath) {
return null;
}
}));
registration.registerSubModel(SocketBindingGroupResourceDefinition.INSTANCE);
ignoredDomainResourceRegistry = new IgnoredDomainResourceRegistry(hostControllerInfo);
ManagementResourceRegistration profileReg = registration.registerSubModel(new ProfileResourceDefinition(hostControllerInfo, ignoredDomainResourceRegistry));
profileReg.registerSubModel(new SubsystemResourceDefinition());
GlobalNotifications.registerGlobalNotifications(registration, processType);
registerCommonChildren(managementModel.getRootResource(), localIndexedAdd);
}
void executeTriggerSyncOperation(Resource rootResource) throws Exception {
ReadMasterDomainModelUtil util = ReadMasterDomainModelUtil.readMasterDomainResourcesForInitialConnect(new NoopTransformers(), null, null, rootResource);
ModelNode op = Util.createEmptyOperation(TRIGGER_SYNC.getName(), PathAddress.EMPTY_ADDRESS);
op.get(DOMAIN_MODEL).set(util.getDescribedResources());
executeForResult(op);
}
Resource createMasterDcResources() {
Resource rootResource = Resource.Factory.create();
registerCommonChildren(rootResource, true);
rootResource.removeChild(HOST_ELEMEMT);
return rootResource;
}
void registerCommonChildren(Resource rootResource, boolean sortedChildren) {
//This is needed by the handlers but not used. Without this we get an NPE
Resource host = Resource.Factory.create();
host.getModel().setEmptyObject();
rootResource.registerChild(HOST_ELEMEMT, host);
Resource serverConfig = Resource.Factory.create();
serverConfig.getModel().get(GROUP).set("main");
host.registerChild(PathElement.pathElement(SERVER_CONFIG, "server"), serverConfig);
Resource serverGroup = Resource.Factory.create();
serverGroup.getModel().get(PROFILE).set("test");
serverGroup.getModel().get(SOCKET_BINDING_GROUP).set("test-sockets");
rootResource.registerChild(PathElement.pathElement(SERVER_GROUP, "main"), serverGroup);
Resource socketBindingGroup = Resource.Factory.create();
socketBindingGroup.getModel().setEmptyObject();
rootResource.registerChild(PathElement.pathElement(SOCKET_BINDING_GROUP, "test-sockets"), socketBindingGroup);
//Now register our tree of resources
Resource profile = Resource.Factory.create();
profile.getModel().setEmptyObject();
rootResource.registerChild(PROFILE_ELEMENT, profile);
Resource subsystem = sortedChildren ? Resource.Factory.create(false, Collections.singleton(ORDERED_CHILD.getKey())) : Resource.Factory.create();
subsystem.getModel().setEmptyObject();
profile.registerChild(SUBSYSTEM_ELEMENT, subsystem);
createAndRegisterSubsystemChildren(subsystem, "apple");
createAndRegisterSubsystemChildren(subsystem, "orange");
}
void createAndRegisterSubsystemChildren(Resource subsystem, String value) {
createAndRegisterSubsystemChild(subsystem, ORDERED_CHILD.getKey(), value);
createAndRegisterSubsystemChild(subsystem, NON_ORDERED_CHILD.getKey(), value);
}
Resource createAndRegisterSubsystemChild(Resource subsystem, String childType, String value) {
return createAndRegisterSubsystemChild(subsystem, childType, value, -1);
}
Resource createAndRegisterSubsystemChild(Resource subsystem, String childType, String value, int index) {
Resource resource = createChildResource(subsystem, childType, value, index);
if (registerExtraChildren && childType.equals(ORDERED_CHILD.getKey())) {
Resource childA = Resource.Factory.create();
String valueA = "jam";
childA.getModel().get(ATTR.getName()).set(valueA.toUpperCase(Locale.ENGLISH));
resource.registerChild(PathElement.pathElement(EXTRA_CHILD.getKey(), valueA), childA);
Resource childB = Resource.Factory.create();
String valueB = "juice";
childB.getModel().get(ATTR.getName()).set(valueB.toUpperCase(Locale.ENGLISH));
resource.registerChild(PathElement.pathElement(EXTRA_CHILD.getKey(), valueB), childB);
}
return resource;
}
Resource createChildResource(Resource parent, String childType, String value, int index) {
Resource resource = registerExtraChildren ? Resource.Factory.create(false, Collections.singleton(EXTRA_CHILD.getKey())) : Resource.Factory.create();
resource.getModel().get(ATTR.getName()).set(value.toUpperCase(Locale.ENGLISH));
if (index < 0) {
parent.registerChild(PathElement.pathElement(childType, value.toLowerCase(Locale.ENGLISH)), resource);
} else {
parent.registerChild(PathElement.pathElement(childType, value.toLowerCase(Locale.ENGLISH)), index, resource);
}
return resource;
}
Resource createAndRegisterSubsystemChildFromRoot(Resource rootResource, String childType, String value) {
return createAndRegisterSubsystemChild(findSubsystemResource(rootResource), childType, value);
}
Resource createAndRegisterSubsystemChildFromRoot(Resource rootResource, String childType, String value, int index) {
return createAndRegisterSubsystemChild(findSubsystemResource(rootResource), childType, value, index);
}
Resource findSubsystemResource(Resource rootResource) {
Resource subsystem = rootResource.requireChild(PROFILE_ELEMENT);
return subsystem.requireChild(SUBSYSTEM_ELEMENT);
}
ModelNode findSubsystemResource(ModelNode modelNode) {
ModelNode current = modelNode;
current = current.require(PROFILE_ELEMENT.getKey());
current = current.require(PROFILE_ELEMENT.getValue());
current = current.require(SUBSYSTEM_ELEMENT.getKey());
current = current.require(SUBSYSTEM_ELEMENT.getValue());
return current;
}
void compareSubsystemModels(ModelNode expected, ModelNode actual) {
Assert.assertEquals(findSubsystemResource(expected), findSubsystemResource(actual));
}
void compare(Set<String> keys, String... expected) {
String[] actual = keys.toArray(new String[keys.size()]);
Assert.assertArrayEquals(expected, actual);
}
class SubsystemResourceDefinition extends SimpleResourceDefinition {
public SubsystemResourceDefinition() {
super(SUBSYSTEM_ELEMENT,
new NonResolvingResourceDescriptionResolver(),
new AbstractAddStepHandler(REQUEST_ATTRIBUTES),
new ModelOnlyRemoveStepHandler());
}
@Override
public void registerChildren(ManagementResourceRegistration resourceRegistration) {
resourceRegistration.registerSubModel(new OrderedChildResourceDefinition());
resourceRegistration.registerSubModel(new NonOrderedChildResourceDefinition());
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
}
static class NonOrderedChildResourceDefinition extends SimpleResourceDefinition {
public NonOrderedChildResourceDefinition() {
super(NON_ORDERED_CHILD, new NonResolvingResourceDescriptionResolver(), new ModelOnlyAddStepHandler(REQUEST_ATTRIBUTES), new ModelOnlyRemoveStepHandler());
}
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
resourceRegistration.registerReadWriteAttribute(ATTR, null, new ModelOnlyWriteAttributeHandler(ATTR));
}
}
abstract class AbstractChildResourceDefinition extends SimpleResourceDefinition {
public AbstractChildResourceDefinition(PathElement element, OperationStepHandler addHandler) {
super(new Parameters(element, new NonResolvingResourceDescriptionResolver())
.setAddHandler(addHandler)
.setRemoveHandler(new ModelOnlyRemoveStepHandler())
.setOrderedChild());
}
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
resourceRegistration.registerReadWriteAttribute(ATTR, null, new ModelOnlyWriteAttributeHandler(ATTR));
}
}
class OrderedChildResourceDefinition extends AbstractChildResourceDefinition {
OrderedChildResourceDefinition() {
super(ORDERED_CHILD,
new AbstractAddStepHandler((REQUEST_ATTRIBUTES)));
}
@Override
public void registerChildren(ManagementResourceRegistration resourceRegistration) {
if (registerExtraChildren) {
resourceRegistration.registerSubModel(new ExtraChildResourceDefinition());
}
}
}
class ExtraChildResourceDefinition extends AbstractChildResourceDefinition {
public ExtraChildResourceDefinition() {
super(EXTRA_CHILD,
new AbstractAddStepHandler(REQUEST_ATTRIBUTES));
}
}
class TriggerSyncHandler implements OperationStepHandler {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final ModelNode syncOperation = new ModelNode();
syncOperation.get(OP).set("calculate-diff-and-sync");
syncOperation.get(OP_ADDR).setEmptyList();
syncOperation.get(DOMAIN_MODEL).set(operation.get(DOMAIN_MODEL));
Resource original = context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS);
final TestRepository repo = new TestRepository();
final HostControllerRegistrationHandler.OperationExecutor internalExecutor = getControllerService().getInternalExecutor();
SyncModelParameters parameters =
new SyncModelParameters(new MockDomainController(), ignoredDomainResourceRegistry,
hostControllerEnvironment, extensionRegistry, internalExecutor, true,
Collections.<String, ProxyController>emptyMap(), repo, repo);
final SyncServerGroupOperationHandler handler =
new SyncServerGroupOperationHandler("slave", original, parameters);
context.addStep(syncOperation, handler, OperationContext.Stage.MODEL, true);
}
}
}