/*
* 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.coordination;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ACCESS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.AUDIT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONFIGURATION_CHANGES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT_OVERLAY;
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.INTERFACE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.JVM;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LOGGER;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MANAGEMENT;
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.OPERATION_HEADERS;
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.PROFILE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REPLACE_DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME;
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.SERVER_LOGGER;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVICE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING_GROUP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING_PORT_OFFSET;
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.jboss.as.domain.controller.logging.DomainControllerLogger.HOST_CONTROLLER_LOGGER;
import static org.jboss.as.domain.controller.operations.coordination.DomainServerUtils.getAllRunningServers;
import static org.jboss.as.domain.controller.operations.coordination.DomainServerUtils.getRelatedElements;
import static org.jboss.as.domain.controller.operations.coordination.DomainServerUtils.getServersForGroup;
import static org.jboss.as.domain.controller.operations.coordination.DomainServerUtils.getServersForType;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationContext.AttachmentKey;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProxyController;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EMPTY;
import org.jboss.as.controller.operations.DomainOperationTransformer;
import org.jboss.as.controller.operations.OperationAttachments;
import org.jboss.as.controller.operations.common.ResolveExpressionHandler;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.resource.InterfaceDefinition;
import org.jboss.as.domain.controller.ServerIdentity;
import org.jboss.as.domain.controller.logging.DomainControllerLogger;
import org.jboss.as.domain.controller.operations.ResolveExpressionOnDomainHandler;
import org.jboss.as.domain.controller.operations.deployment.DeploymentFullReplaceHandler;
import static org.jboss.as.server.controller.resources.DeploymentAttributes.CONTENT_HASH;
import org.jboss.as.server.operations.ServerProcessStateHandler;
import org.jboss.as.server.operations.SystemPropertyAddHandler;
import org.jboss.as.server.operations.SystemPropertyRemoveHandler;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
/**
* Logic for creating a server-level operation that realizes the effect
* of a domain or host level change on the server.
*
* @author Brian Stansberry (c) 2011 Red Hat Inc.
*/
public class ServerOperationResolver {
public static final AttachmentKey<Set<ModelNode>> DONT_PROPAGATE_TO_SERVERS_ATTACHMENT = AttachmentKey.create(Set.class);
private static final AttachmentKey<ModelNode> DOMAIN_MODEL_ATTACHMENT = AttachmentKey.create(ModelNode.class);
private enum DomainKey {
UNKNOWN(null),
EXTENSION(ModelDescriptionConstants.EXTENSION),
PATH(ModelDescriptionConstants.PATH),
SYSTEM_PROPERTY(ModelDescriptionConstants.SYSTEM_PROPERTY),
CORE_SERVICE(ModelDescriptionConstants.CORE_SERVICE),
PROFILE(ModelDescriptionConstants.PROFILE),
INTERFACE(ModelDescriptionConstants.INTERFACE),
SOCKET_BINDING_GROUP(ModelDescriptionConstants.SOCKET_BINDING_GROUP),
DEPLOYMENT(ModelDescriptionConstants.DEPLOYMENT),
SERVER_GROUP(ModelDescriptionConstants.SERVER_GROUP),
HOST_EXCLUDE(ModelDescriptionConstants.HOST_EXCLUDE),
MANAGMENT_CLIENT_CONTENT(ModelDescriptionConstants.MANAGEMENT_CLIENT_CONTENT),
HOST(ModelDescriptionConstants.HOST),
DEPLOYMENT_OVERLAY(ModelDescriptionConstants.DEPLOYMENT_OVERLAY),
HOST_CONNECTION(ModelDescriptionConstants.HOST_CONNECTION),
;
private final String name;
DomainKey(final String name) {
this.name = name;
}
private static final Map<String, DomainKey> MAP;
static {
final Map<String, DomainKey> map = new HashMap<String, DomainKey>();
for (DomainKey element : values()) {
final String name = element.name;
if (name != null) map.put(name, element);
}
MAP = map;
}
public static DomainKey forName(String localName) {
final DomainKey element = MAP.get(localName);
return element == null ? UNKNOWN : element;
}
}
private enum HostKey {
UNKNOWN(null),
EXTENSION(ModelDescriptionConstants.EXTENSION),
PATH(ModelDescriptionConstants.PATH),
SYSTEM_PROPERTY(ModelDescriptionConstants.SYSTEM_PROPERTY),
CORE_SERVICE(ModelDescriptionConstants.CORE_SERVICE),
INTERFACE(ModelDescriptionConstants.INTERFACE),
JVM(ModelDescriptionConstants.JVM),
SERVER(ModelDescriptionConstants.SERVER),
SERVER_CONFIG(ModelDescriptionConstants.SERVER_CONFIG),
SUBSYSTEM(ModelDescriptionConstants.SUBSYSTEM),
SOCKET_BINDING_GROUP(ModelDescriptionConstants.SOCKET_BINDING_GROUP);
private final String name;
HostKey(final String name) {
this.name = name;
}
private static final Map<String, HostKey> MAP;
static {
final Map<String, HostKey> map = new HashMap<String, HostKey>();
for (HostKey element : values()) {
final String name = element.name;
if (name != null) map.put(name, element);
}
MAP = map;
}
public static HostKey forName(String localName) {
final HostKey element = MAP.get(localName);
return element == null ? UNKNOWN : element;
}
}
private enum Level {
DOMAIN, SERVER_GROUP, HOST, SERVER
}
private final String localHostName;
private final Map<String, ProxyController> serverProxies;
public ServerOperationResolver(final String localHostName, final Map<String, ProxyController> serverProxies) {
this.localHostName = localHostName;
this.serverProxies = serverProxies;
}
public static void addToDontPropagateToServersAttachment(OperationContext context, ModelNode op) {
ModelNode cleanOp = cleanOpForDontPropagate(op);
Set<ModelNode> ops = Collections.synchronizedSet(new HashSet<ModelNode>(Collections.singleton(cleanOp)));
Set<ModelNode> existing = context.attachIfAbsent(DONT_PROPAGATE_TO_SERVERS_ATTACHMENT, ops);
if (existing != null){
existing.add(cleanOp);
}
}
private boolean isDontPropagateToServers(OperationContext context, ModelNode op) {
Set<ModelNode> dontPropagate = context.getAttachment(DONT_PROPAGATE_TO_SERVERS_ATTACHMENT);
if (dontPropagate != null && dontPropagate.contains(cleanOpForDontPropagate(op))) {
return true;
}
return false;
}
private static ModelNode cleanOpForDontPropagate(ModelNode op) {
if (op.has(OPERATION_HEADERS)) {
op = op.clone();
op.remove(OPERATION_HEADERS);
}
return op;
}
public Map<Set<ServerIdentity>, ModelNode> getServerOperations(OperationContext context, ModelNode originalOperation, PathAddress address) {
if (HOST_CONTROLLER_LOGGER.isTraceEnabled()) {
HOST_CONTROLLER_LOGGER.tracef("Resolving %s", originalOperation);
}
List<DomainOperationTransformer> transformers = context.getAttachment(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS);
ModelNode operation = originalOperation;
if(transformers != null) {
for(DomainOperationTransformer transformer : transformers) {
operation = transformer.transform(context, operation);
}
}
if (isDontPropagateToServers(context, operation)) {
return Collections.emptyMap();
}
final ModelNode domain = getDomainModel(context);
final ModelNode host = domain.get(HOST, localHostName);
if (address.size() == 0) {
return resolveDomainRootOperation(operation, domain, host);
} else {
DomainKey domainKey = DomainKey.forName(address.getElement(0).getKey());
switch (domainKey) {
case EXTENSION: {
Set<ServerIdentity> allServers = getAllRunningServers(host, localHostName, serverProxies);
return Collections.singletonMap(allServers, operation);
}
case DEPLOYMENT: {
return getServerExplodedDeploymentOperations(operation, address, domain, host);
}
case PATH: {
return getServerPathOperations(operation, address, host, true);
}
case SYSTEM_PROPERTY: {
return getServerSystemPropertyOperations(operation, address, Level.DOMAIN, domain, null, host);
}
case CORE_SERVICE: {
return getServerCoreServiceOperations(operation, address, host);
}
case PROFILE: {
return getServerProfileOperations(operation, address, domain, host);
}
case INTERFACE: {
return getServerInterfaceOperations(operation, address, host, true);
}
case SOCKET_BINDING_GROUP: {
return getServerSocketBindingGroupOperations(operation, address, domain, host);
}
case SERVER_GROUP: {
return getServerGroupOperations(operation, address, domain, host);
}
case MANAGMENT_CLIENT_CONTENT: {
return Collections.emptyMap();
}
case HOST: {
return getServerHostOperations(operation, address, domain, host);
}
case DEPLOYMENT_OVERLAY: {
return getDeploymentOverlayOperations(operation, host);
}
case HOST_CONNECTION: {
return Collections.emptyMap();
}
case HOST_EXCLUDE: {
return Collections.emptyMap();
}
default:
throw DomainControllerLogger.HOST_CONTROLLER_LOGGER.unexpectedInitialPathKey(address.getElement(0).getKey());
}
}
}
private static ModelNode getDomainModel(OperationContext context) {
ModelNode model = context.getAttachment(DOMAIN_MODEL_ATTACHMENT);
if (model == null) {
model = Resource.Tools.readModel(context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS, true));
context.attach(DOMAIN_MODEL_ATTACHMENT, model);
}
return model;
}
private Map<Set<ServerIdentity>, ModelNode> getServerProfileOperations(ModelNode operation, PathAddress address,
ModelNode domain, ModelNode host) {
if (address.size() == 1) {
return Collections.emptyMap();
}
String profileName = address.getElement(0).getValue();
PathElement subsystem = address.getElement(1);
Set<String> relatedProfiles = getRelatedElements(PROFILE, profileName, subsystem.getKey(), subsystem.getValue(), domain);
Set<ServerIdentity> allServers = new HashSet<ServerIdentity>();
for (String profile : relatedProfiles) {
allServers.addAll(getServersForType(PROFILE, profile, domain, host, localHostName, serverProxies));
}
ModelNode serverOp = operation.clone();
PathAddress serverAddress = address.subAddress(1);
serverOp.get(OP_ADDR).set(serverAddress.toModelNode());
return Collections.singletonMap(allServers, serverOp);
}
private Map<Set<ServerIdentity>, ModelNode> getDeploymentOverlayOperations(ModelNode operation,
ModelNode host) {
final Set<ServerIdentity> allServers = getAllRunningServers(host, localHostName, serverProxies);
return Collections.singletonMap(allServers, operation.clone());
}
private Map<Set<ServerIdentity>, ModelNode> getServerCoreServiceOperations(ModelNode operation, PathAddress address,
ModelNode host) {
if (address.size() >= 2 && SERVICE.equals(address.getElement(1).getKey()) && CONFIGURATION_CHANGES.equals(address.getElement(1).getValue())) {
return Collections.emptyMap();
}
final Set<ServerIdentity> allServers = getAllRunningServers(host, localHostName, serverProxies);
return Collections.singletonMap(allServers, operation.clone());
}
private Map<Set<ServerIdentity>, ModelNode> getServerInterfaceOperations(ModelNode operation, PathAddress address,
ModelNode hostModel, boolean forDomain) {
String pathName = address.getElement(0).getValue();
Map<Set<ServerIdentity>, ModelNode> result;
if (forDomain && hostModel.hasDefined(INTERFACE) && hostModel.get(INTERFACE).keys().contains(pathName)) {
// Host will take precedence; ignore the domain
result = Collections.emptyMap();
} else if (forDomain && ADD.equals(operation.get(OP).asString()) && !InterfaceDefinition.isOperationDefined(operation)) {
// don't create named interfaces
result = Collections.emptyMap();
} else if (hostModel.hasDefined(SERVER_CONFIG)) {
Set<ServerIdentity> servers = new HashSet<ServerIdentity>();
for (Property prop : hostModel.get(SERVER_CONFIG).asPropertyList()) {
String serverName = prop.getName();
if (serverProxies.get(serverName) == null) {
continue;
}
ModelNode server = prop.getValue();
String serverGroupName = server.require(GROUP).asString();
if (server.hasDefined(INTERFACE) && server.get(INTERFACE).keys().contains(pathName)) {
// Server takes precedence; ignore domain
continue;
}
ServerIdentity groupedServer = new ServerIdentity(localHostName, serverGroupName, serverName);
servers.add(groupedServer);
}
ModelNode serverOp = operation.clone();
serverOp.get(OP_ADDR).setEmptyList().add(INTERFACE, pathName);
result = Collections.singletonMap(servers, serverOp);
} else {
result = Collections.emptyMap();
}
return result;
}
private Map<Set<ServerIdentity>, ModelNode> getJVMRestartOperations(final PathAddress address, final ModelNode hostModel) {
// See which servers are affected by this JVM change
final String pathName = address.getElement(0).getValue();
final Map<Set<ServerIdentity>, ModelNode> result;
if (hostModel.hasDefined(SERVER_CONFIG)) {
final Set<ServerIdentity> servers = new HashSet<ServerIdentity>();
for (Property prop : hostModel.get(SERVER_CONFIG).asPropertyList()) {
final String serverName = prop.getName();
if (serverProxies.get(serverName) == null) {
// No running server
continue;
}
final ModelNode server = prop.getValue();
if (server.hasDefined(JVM) && server.get(JVM).keys().contains(pathName)) {
final String serverGroupName = server.require(GROUP).asString();
final ServerIdentity groupedServer = new ServerIdentity(localHostName, serverGroupName, serverName);
servers.add(groupedServer);
}
}
result = getServerRestartRequiredOperations(servers);
} else {
result = Collections.emptyMap();
}
return result;
}
private Map<Set<ServerIdentity>, ModelNode> getServerPathOperations(ModelNode operation, PathAddress address, ModelNode hostModel, boolean forDomain) {
String pathName = address.getElement(0).getValue();
Map<Set<ServerIdentity>, ModelNode> result;
if (forDomain && hostModel.hasDefined(PATH) && hostModel.get(PATH).keys().contains(pathName)) {
// Host will take precedence; ignore the domain
result = Collections.emptyMap();
} else if (ADD.equals(operation.get(OP).asString()) && !operation.hasDefined(PATH)) {
// don't push named only paths
result = Collections.emptyMap();
} else if (hostModel.hasDefined(SERVER_CONFIG)) {
Set<ServerIdentity> servers = new HashSet<ServerIdentity>();
for (Property prop : hostModel.get(SERVER_CONFIG).asPropertyList()) {
String serverName = prop.getName();
if (serverProxies.get(serverName) == null) {
continue;
}
ModelNode server = prop.getValue();
String serverGroupName = server.require(GROUP).asString();
if (server.hasDefined(PATH) && server.get(PATH).keys().contains(pathName)) {
// Server takes precedence; ignore domain
continue;
}
ServerIdentity groupedServer = new ServerIdentity(localHostName, serverGroupName, serverName);
servers.add(groupedServer);
}
ModelNode serverOp = operation.clone();
serverOp.get(OP_ADDR).setEmptyList().add(PATH, pathName);
result = Collections.singletonMap(servers, serverOp);
} else {
result = Collections.emptyMap();
}
return result;
}
private Map<Set<ServerIdentity>, ModelNode> getServerSocketBindingGroupOperations(ModelNode operation,
PathAddress address, ModelNode domain, ModelNode host) {
final String bindingGroupName = address.getElement(0).getValue();
final Set<String> relatedBindingGroups;
if (address.size() > 1) {
PathElement element = address.getElement(1);
relatedBindingGroups = getRelatedElements(SOCKET_BINDING_GROUP, bindingGroupName, element.getKey(), element.getValue(), domain);
} else {
relatedBindingGroups = Collections.emptySet();
}
final Set<ServerIdentity> result = new HashSet<ServerIdentity>();
for (String bindingGroup : relatedBindingGroups) {
result.addAll(getServersForType(SOCKET_BINDING_GROUP, bindingGroup, domain, host, localHostName, serverProxies));
}
//If /socket-binding-group=child includes /socket-binding-group=root, and a server/server-group is set up
//to use /socket-binding-group=child, /socket-binding-group=child becomes the name of the group in the server model.
//So if a change was made to /socket-binding-group=root, we need to translate that to use /socket-binding-group=child
//before pushing the op to the server
final Map<String, Set<ServerIdentity>> serversBySocketBindingGroup = new HashMap<>();
for (Iterator<ServerIdentity> iter = result.iterator(); iter.hasNext(); ) {
final ServerIdentity id = iter.next();
final ModelNode server = host.get(SERVER_CONFIG, id.getServerName());
final String socketBindingGroupName;
if (server.hasDefined(SOCKET_BINDING_GROUP)) {
socketBindingGroupName = server.get(SOCKET_BINDING_GROUP).asString();
} else {
socketBindingGroupName = domain.get(SERVER_GROUP, id.getServerGroupName(), SOCKET_BINDING_GROUP).asString();
}
Set<ServerIdentity> servers = serversBySocketBindingGroup.get(socketBindingGroupName);
if (servers == null) {
servers = new HashSet<>();
serversBySocketBindingGroup.put(socketBindingGroupName, servers);
}
servers.add(id);
}
final Map<Set<ServerIdentity>, ModelNode> ret = new HashMap<>();
for (Map.Entry<String, Set<ServerIdentity>> entry : serversBySocketBindingGroup.entrySet()) {
final ModelNode serverOp = operation.clone();
PathAddress changed = PathAddress.EMPTY_ADDRESS;
for (PathElement element : address) {
if (!element.getKey().equals(SOCKET_BINDING_GROUP)) {
changed = changed.append(element);
} else {
changed = changed.append(PathElement.pathElement(SOCKET_BINDING_GROUP, entry.getKey()));
}
}
serverOp.get(OP_ADDR).set(changed.toModelNode());
ret.put(entry.getValue(), serverOp);
}
return ret;
}
private Map<Set<ServerIdentity>, ModelNode> getServerGroupOperations(ModelNode operation, PathAddress address,
ModelNode domain, ModelNode host) {
Map<Set<ServerIdentity>, ModelNode> result = null;
if (address.size() > 1) {
String type = address.getElement(1).getKey();
if (JVM.equals(type)) {
// Changes to the JVM require a restart
String groupName = address.getElement(0).getValue();
Set<ServerIdentity> servers = getServersForGroup(groupName, host, localHostName, serverProxies);
return getServerRestartRequiredOperations(servers);
} else if (DEPLOYMENT.equals(type)) {
String groupName = address.getElement(0).getValue();
Set<ServerIdentity> servers = getServersForGroup(groupName, host, localHostName, serverProxies);
ModelNode serverOp = operation.clone();
if (ADD.equals(serverOp.get(OP).asString())) {
// The op is missing the runtime-name and content values that the server will need
ModelNode domainDeployment = domain.get(DEPLOYMENT, address.getElement(1).getValue());
if (!serverOp.hasDefined(RUNTIME_NAME)) {
serverOp.get(RUNTIME_NAME).set(domainDeployment.get(RUNTIME_NAME));
}
List<ModelNode> contents = domainDeployment.require(CONTENT).asList();
for (ModelNode content : contents) {
if ((content.hasDefined(CONTENT_HASH.getName()))) {
ModelNode contentItemNode = content.clone();
if (contentItemNode.hasDefined(EMPTY)) {
contentItemNode.remove(EMPTY);
}
serverOp.get(CONTENT).add(contentItemNode);
} else {
serverOp.get(CONTENT).add(content);
}
}
}
PathAddress serverAddress = address.subAddress(1);
serverOp.get(OP_ADDR).set(serverAddress.toModelNode());
result = Collections.singletonMap(servers, serverOp);
} else if (SYSTEM_PROPERTY.equals(type)) {
String affectedGroup = address.getElement(0).getValue();
result = getServerSystemPropertyOperations(operation, address, Level.SERVER_GROUP, domain, affectedGroup, host);
} else if (DEPLOYMENT_OVERLAY.equals(type) && address.getLastElement().getKey().equals(DEPLOYMENT)) {
String groupName = address.getElement(0).getValue();
Set<ServerIdentity> servers = getServersForGroup(groupName, host, localHostName, serverProxies);
ModelNode serverOp = operation.clone();
PathAddress serverAddress = address.subAddress(1);
serverOp.get(OP_ADDR).set(serverAddress.toModelNode());
result = Collections.singletonMap(servers, serverOp);
}
} else if (REPLACE_DEPLOYMENT.equals(operation.require(OP).asString())) {
String groupName = address.getElement(0).getValue();
Set<ServerIdentity> servers = getServersForGroup(groupName, host, localHostName, serverProxies);
ModelNode serverOp = operation.clone();
serverOp.get(OP_ADDR).setEmptyList();
// The op is missing the runtime-name and content values that the server will need
ModelNode domainDeployment = domain.get(DEPLOYMENT, operation.require(NAME).asString());
serverOp.get(RUNTIME_NAME).set(domainDeployment.get(RUNTIME_NAME));
serverOp.get(CONTENT).set(domainDeployment.require(CONTENT));
result = Collections.singletonMap(servers, serverOp);
} else if (ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION.equals(operation.require(OP).asString())) {
final String attr = operation.get(NAME).asString();
if (PROFILE.equals(attr)) {
String groupName = address.getElement(0).getValue();
Set<ServerIdentity> servers = getServersForGroup(groupName, host, localHostName, serverProxies);
return getServerReloadRequiredOperations(servers);
} else if (SOCKET_BINDING_GROUP.equals(attr)) {
String groupName = address.getElement(0).getValue();
Set<ServerIdentity> servers = getServersForGroup(groupName, host, localHostName, serverProxies);
if (servers.size() > 0) {
//Get rid of servers overriding the socket-binding-group
Set<ServerIdentity> affectedServers = new HashSet<>();
for (ServerIdentity server : servers) {
ModelNode serverConfig = host.get(SERVER_CONFIG, server.getServerName());
if (!serverConfig.hasDefined(SOCKET_BINDING_GROUP)) {
affectedServers.add(server);
}
}
servers = affectedServers;
}
return getServerReloadRequiredOperations(servers);
}
}
if (result == null) {
result = Collections.emptyMap();
}
return result;
}
private Map<Set<ServerIdentity>, ModelNode> resolveDomainRootOperation(ModelNode operation, ModelNode domain, ModelNode host) {
Map<Set<ServerIdentity>, ModelNode> result = null;
String opName = operation.require(OP).asString();
if (DeploymentFullReplaceHandler.OPERATION_NAME.equals(opName)) {
String propName = operation.require(NAME).asString();
Set<String> groups = getServerGroupsForDeployment(propName, domain);
Set<ServerIdentity> allServers = new HashSet<ServerIdentity>();
for (String group : groups) {
allServers.addAll(getServersForGroup(group, host, localHostName, serverProxies));
}
result = Collections.singletonMap(allServers, operation);
} else if (ResolveExpressionOnDomainHandler.OPERATION_NAME.equals(opName)) {
final ModelNode serverOp = operation.clone();
serverOp.get(OP).set(ResolveExpressionHandler.OPERATION_NAME);
serverOp.get(OP_ADDR).setEmptyList();
final Set<ServerIdentity> allServers = getAllRunningServers(host, localHostName, serverProxies);
result = Collections.singletonMap(allServers, serverOp);
}
if (result == null) {
result = Collections.emptyMap();
}
return result;
}
private Set<String> getServerGroupsForDeployment(String deploymentName, ModelNode domainModel) {
Set<String> groups;
if (domainModel.hasDefined(SERVER_GROUP)) {
groups = new HashSet<String>();
for (Property prop : domainModel.get(SERVER_GROUP).asPropertyList()) {
ModelNode serverGroup = prop.getValue();
if (serverGroup.hasDefined(DEPLOYMENT) && serverGroup.get(DEPLOYMENT).hasDefined(deploymentName)) {
groups.add(prop.getName());
}
}
} else {
groups = Collections.emptySet();
}
return groups;
}
private boolean hasSystemProperty(ModelNode resource, String propName) {
return resource.hasDefined(SYSTEM_PROPERTY) && resource.get(SYSTEM_PROPERTY).hasDefined(propName);
}
private Map<Set<ServerIdentity>, ModelNode> getServerHostOperations(ModelNode operation, PathAddress address,
ModelNode domain, ModelNode host) {
if (address.size() == 1) {
return resolveHostRootOperation(operation, domain, host);
} else {
HostKey hostKey = HostKey.forName(address.getElement(1).getKey());
address = address.subAddress(1); // Get rid of the host=hostName
switch (hostKey) {
case PATH: {
return getServerPathOperations(operation, address, host, false);
}
case SYSTEM_PROPERTY: {
return getServerSystemPropertyOperations(operation, address, Level.HOST, domain, null, host);
}
case CORE_SERVICE: {
return resolveCoreServiceOperations(operation, address, domain, host);
}
case INTERFACE: {
return getServerInterfaceOperations(operation, address, host, false);
}
case JVM: {
return getJVMRestartOperations(address, host);
}
case SERVER_CONFIG: {
return resolveServerConfigOperation(operation, address, domain, host);
}
case EXTENSION:
case SUBSYSTEM:
case SOCKET_BINDING_GROUP: {
//Changes made to the extensions, subsystems and socket-bindings on a host should not be propagated to the servers
return Collections.emptyMap();
}
case SERVER:
default:
throw DomainControllerLogger.HOST_CONTROLLER_LOGGER.unexpectedInitialPathKey(address.getElement(0).getKey());
}
}
}
private Map<Set<ServerIdentity>, ModelNode> resolveHostRootOperation(ModelNode operation, ModelNode domain, ModelNode host) {
Map<Set<ServerIdentity>, ModelNode> result = null;
String opName = operation.require(OP).asString();
if (ResolveExpressionOnDomainHandler.OPERATION_NAME.equals(opName)) {
final ModelNode serverOp = operation.clone();
serverOp.get(OP).set(ResolveExpressionHandler.OPERATION_NAME);
serverOp.get(OP_ADDR).setEmptyList();
final Set<ServerIdentity> allServers = getAllRunningServers(host, localHostName, serverProxies);
result = Collections.singletonMap(allServers, serverOp);
}
if (result == null) {
result = Collections.emptyMap();
}
return result;
}
/**
* Get server operations to affect a change to a system property.
*
* @param operation the domain or host level operation
* @param address address associated with {@code operation}
* @param domain the domain model, or {@code null} if {@code address} isn't for a domain level resource
* @param affectedGroup the name of the server group affected by the operation, or {@code null}
* if {@code address} isn't for a server group level resource
* @param host the host model
* @return the server operations
*/
private Map<Set<ServerIdentity>, ModelNode> getServerSystemPropertyOperations(ModelNode operation, PathAddress address, Level level,
ModelNode domain, String affectedGroup, ModelNode host) {
Map<Set<ServerIdentity>, ModelNode> result = null;
if (isServerAffectingSystemPropertyOperation(operation)) {
String propName = address.getLastElement().getValue();
boolean overridden = false;
Set<String> groups = null;
if (level == Level.DOMAIN || level == Level.SERVER_GROUP) {
if (hasSystemProperty(host, propName)) {
// host level value takes precedence
overridden = true;
} else if (affectedGroup != null) {
groups = Collections.singleton(affectedGroup);
} else if (domain.hasDefined(SERVER_GROUP)) {
// Top level domain update applies to all groups where it was not overridden
groups = new HashSet<String>();
for (Property groupProp : domain.get(SERVER_GROUP).asPropertyList()) {
String groupName = groupProp.getName();
if (!hasSystemProperty(groupProp.getValue(), propName)) {
groups.add(groupName);
}
}
}
}
Set<ServerIdentity> servers = null;
if (!overridden && host.hasDefined(SERVER_CONFIG)) {
servers = new HashSet<ServerIdentity>();
for (Property serverProp : host.get(SERVER_CONFIG).asPropertyList()) {
String serverName = serverProp.getName();
if (serverProxies.get(serverName) == null) {
continue;
}
ModelNode server = serverProp.getValue();
if (!hasSystemProperty(server, propName)) {
String groupName = server.require(GROUP).asString();
if (groups == null || groups.contains(groupName)) {
servers.add(new ServerIdentity(localHostName, groupName, serverName));
}
}
}
}
if (servers != null && servers.size() > 0) {
Map<ModelNode, Set<ServerIdentity>> ops = new HashMap<ModelNode, Set<ServerIdentity>>();
for (ServerIdentity server : servers) {
ModelNode serverOp = getServerSystemPropertyOperation(operation, propName, server, level, domain, host);
Set<ServerIdentity> set = ops.get(serverOp);
if (set == null) {
set = new HashSet<ServerIdentity>();
ops.put(serverOp, set);
}
set.add(server);
}
result = new HashMap<Set<ServerIdentity>, ModelNode>();
for (Map.Entry<ModelNode, Set<ServerIdentity>> entry : ops.entrySet()) {
result.put(entry.getValue(), entry.getKey());
}
}
}
if (result == null) {
result = Collections.emptyMap();
}
return result;
}
private ModelNode getServerSystemPropertyOperation(ModelNode operation, String propName, ServerIdentity server, Level level, ModelNode domain, ModelNode host) {
ModelNode result = null;
String opName = operation.get(OP).asString();
if (ADD.equals(opName) || REMOVE.equals(opName)) {
// See if there is a higher level value
ModelNode value = null;
switch (level) {
case SERVER: {
value = getSystemPropertyValue(host, propName);
if (value == null) {
value = getSystemPropertyValue(domain.get(SERVER_GROUP, server.getServerGroupName()), propName);
}
if (value == null) {
value = getSystemPropertyValue(domain, propName);
}
break;
}
case HOST: {
value = getSystemPropertyValue(domain.get(SERVER_GROUP, server.getServerGroupName()), propName);
if (value == null) {
value = getSystemPropertyValue(domain, propName);
}
break;
}
case SERVER_GROUP: {
value = getSystemPropertyValue(domain, propName);
break;
}
default: {
break;
}
}
if (value != null) {
// A higher level defined the property, so we know this property exists on the server.
// We convert the op to WRITE_ATTRIBUTE since we cannot ADD again and a REMOVE
// means the higher level definition again takes effect.
if (ADD.equals(opName)) {
// Use the ADD op's value
value = operation.has(VALUE) ? operation.get(VALUE) : new ModelNode();
}
// else use the higher level value that is no longer overridden
ModelNode addr = new ModelNode();
addr.add(SYSTEM_PROPERTY, propName);
result = Util.getEmptyOperation(WRITE_ATTRIBUTE_OPERATION, addr);
result.get(NAME).set(VALUE);
if (value.isDefined()) {
result.get(VALUE).set(value);
}
}
}
if (result == null) {
result = operation.clone();
ModelNode addr = new ModelNode();
addr.add(SYSTEM_PROPERTY, propName);
result.get(OP_ADDR).set(addr);
}
return result;
}
private ModelNode getSystemPropertyValue(ModelNode root, String propName) {
ModelNode result = null;
if (root.hasDefined(SYSTEM_PROPERTY) && root.get(SYSTEM_PROPERTY).hasDefined(propName)) {
ModelNode resource = root.get(SYSTEM_PROPERTY, propName);
result = resource.hasDefined(VALUE) ? resource.get(VALUE) : new ModelNode();
}
return result;
}
private Map<Set<ServerIdentity>, ModelNode> resolveServerConfigOperation(ModelNode operation, PathAddress address,
ModelNode domain, ModelNode host) {
Map<Set<ServerIdentity>, ModelNode> result;
ModelNode serverOp = null;
if (address.size() > 1) {
String type = address.getElement(1).getKey();
if (PATH.equals(type) || INTERFACE.equals(type)) {
final String serverName = address.getElement(0).getValue();
if (serverProxies.containsKey(serverName)) {
PathAddress serverAddress = address.subAddress(1);
serverOp = operation.clone();
serverOp.get(OP_ADDR).set(serverAddress.toModelNode());
}
} else if(JVM.equals(type)) {
final String serverName = address.getElement(0).getValue();
// If the server is running require a restart
if(serverProxies.containsKey(serverName)) {
final String group = host.get(address.getLastElement().getKey(), address.getLastElement().getValue(), GROUP).asString();
final ServerIdentity id = new ServerIdentity(localHostName, group, serverName);
return getServerRestartRequiredOperations(Collections.singleton(id));
}
} else if (SYSTEM_PROPERTY.equals(type) && isServerAffectingSystemPropertyOperation(operation)) {
String propName = address.getLastElement().getValue();
String serverName = address.getElement(0).getValue();
if (serverProxies.containsKey(serverName)) {
ServerIdentity serverId = getServerIdentity(serverName, host);
serverOp = getServerSystemPropertyOperation(operation, propName, serverId, Level.SERVER, domain, host);
}
}
} else if (address.size() == 1) {
// TODO - deal with "add", "remove" and changing "auto-start" attribute
if (ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION.equals(operation.require(OP).asString())) {
final String attr = operation.get(NAME).asString();
if (GROUP.equals(attr) || SOCKET_BINDING_GROUP.equals(attr) || SOCKET_BINDING_PORT_OFFSET.equals(attr)) {
final String serverName = address.getElement(0).getValue();
// If the server is running require a restart
if(serverProxies.containsKey(serverName)) {
final String group = host.get(address.getLastElement().getKey(), address.getLastElement().getValue(), GROUP).asString();
final ServerIdentity id = new ServerIdentity(localHostName, group, serverName);
result = getServerReloadRequiredOperations(Collections.singleton(id));
return result;
}
}
}
}
if (serverOp == null) {
result = Collections.emptyMap();
} else {
String serverName = address.getElement(0).getValue();
ServerIdentity gs = getServerIdentity(serverName, host);
Set<ServerIdentity> set = Collections.singleton(gs);
result = Collections.singletonMap(set, serverOp);
}
return result;
}
private Map<Set<ServerIdentity>, ModelNode> getServerExplodedDeploymentOperations(ModelNode operation, PathAddress address,
ModelNode domain, ModelNode host) {
Map<Set<ServerIdentity>, ModelNode> result = null;
if (isExplodedDeploymentOperation(operation)) {
String deploymentName = address.getLastElement().getValue();
Set<String> groups = getServerGroupsForDeployment(deploymentName, domain);
Set<ServerIdentity> allServers = new HashSet<>();
for (String group : groups) {
allServers.addAll(getServersForGroup(group, host, localHostName, serverProxies));
}
result = Collections.singletonMap(allServers, operation);
}
if (result == null) {
result = Collections.emptyMap();
}
return result;
}
private static Map<Set<ServerIdentity>, ModelNode> getServerRestartRequiredOperations(Set<ServerIdentity> servers) {
return getSimpleServerOperations(servers, ServerProcessStateHandler.REQUIRE_RESTART_OPERATION);
}
private static Map<Set<ServerIdentity>, ModelNode> getServerReloadRequiredOperations(Set<ServerIdentity> servers) {
return getSimpleServerOperations(servers, ServerProcessStateHandler.REQUIRE_RELOAD_OPERATION);
}
private static Map<Set<ServerIdentity>, ModelNode> getSimpleServerOperations(Set<ServerIdentity> servers, String operationName) {
ModelNode op = new ModelNode();
op.get(OP).set(operationName);
op.get(OP_ADDR).setEmptyList();
return Collections.singletonMap(servers, op);
}
private Map<Set<ServerIdentity>, ModelNode> resolveCoreServiceOperations(ModelNode operation, PathAddress address, ModelNode domain, ModelNode host) {
if (address.getElement(0).getValue().equals(MANAGEMENT)){
if (address.size() >= 2 && address.getElement(1).getKey().equals(ACCESS) && address.getElement(1).getValue().equals(AUDIT)) {
ModelNode op = operation.clone();
op.get(OP_ADDR).set(address.toModelNode());
if (address.size() >= 3) {
PathAddress newAddr = PathAddress.EMPTY_ADDRESS;
for (PathElement element : address) {
if (element.getKey().equals(LOGGER)) {
//logger=>audit-log is only for the HC
return Collections.emptyMap();
} else {
PathElement myElement = element;
if (myElement.getKey().equals(SERVER_LOGGER)) {
//server-logger=audit-log gets sent to the servers as logger=>audit-log
myElement = PathElement.pathElement(LOGGER, element.getValue());
}
newAddr = newAddr.append(myElement);
}
}
op.get(OP_ADDR).set(newAddr.toModelNode());
}
// String key = address.getElement(2).getKey();
// if (key.equals(LOGGER)) {
// //logger=>audit-log is only for the HC
// return Collections.emptyMap();
// } else if (key.equals(SERVER_LOGGER)) {
// //server-logger=audit-log gets sent to the servers as logger=>audit-log
// PathAddress newAddr = address.subAddress(0, 2);
// newAddr = newAddr.append(PathElement.pathElement(LOGGER, address.getElement(2).getValue()));
// op.get(OP_ADDR).set(newAddr.toModelNode());
// }
// }
return Collections.singletonMap(getAllRunningServers(host, localHostName, serverProxies), op);
} else if (address.size() >= 2 && SERVICE.equals(address.getElement(1).getKey()) && CONFIGURATION_CHANGES.equals(address.getElement(1).getValue())) {
if("list-changes".equals(operation.get(OP).asString())) {
return Collections.emptyMap();
}
ModelNode op = operation.clone();
op.get(OP_ADDR).set(address.toModelNode());
return Collections.singletonMap(getAllRunningServers(host, localHostName, serverProxies), op);
}
// TODO does server need to know about other changes?
}
return Collections.emptyMap();
}
private ServerIdentity getServerIdentity(String serverName, ModelNode host) {
ModelNode serverNode = host.get(SERVER_CONFIG, serverName);
return new ServerIdentity(localHostName, serverNode.require(GROUP).asString(), serverName);
}
private boolean isServerAffectingSystemPropertyOperation(ModelNode operation) {
String opName = operation.require(OP).asString();
return (SystemPropertyAddHandler.OPERATION_NAME.equals(opName)
|| SystemPropertyRemoveHandler.OPERATION_NAME.equals(opName)
|| (ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION.equals(opName) && VALUE.equals(operation.require(NAME).asString())));
}
private boolean isExplodedDeploymentOperation(ModelNode operation) {
String op = operation.require(OP).asString();
return ModelDescriptionConstants.EXPLODE.equals(op) ||
ModelDescriptionConstants.ADD_CONTENT.equals(op) ||
ModelDescriptionConstants.REMOVE_CONTENT.equals(op);
}
}