/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.controller.client.helpers.domain.impl;
import static org.jboss.as.controller.client.helpers.ClientConstants.ADD;
import static org.jboss.as.controller.client.helpers.ClientConstants.COMPOSITE;
import static org.jboss.as.controller.client.helpers.ClientConstants.CONTENT;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT_DEPLOY_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT_FULL_REPLACE_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT_REDEPLOY_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT_REMOVE_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT_REPLACE_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT_UNDEPLOY_OPERATION;
import static org.jboss.as.controller.client.helpers.ClientConstants.NAME;
import static org.jboss.as.controller.client.helpers.ClientConstants.OP;
import static org.jboss.as.controller.client.helpers.ClientConstants.OPERATION_HEADERS;
import static org.jboss.as.controller.client.helpers.ClientConstants.OP_ADDR;
import static org.jboss.as.controller.client.helpers.ClientConstants.ROLLBACK_ON_RUNTIME_FAILURE;
import static org.jboss.as.controller.client.helpers.ClientConstants.ROLLOUT_PLAN;
import static org.jboss.as.controller.client.helpers.ClientConstants.RUNTIME_NAME;
import static org.jboss.as.controller.client.helpers.ClientConstants.STEPS;
import static org.jboss.as.controller.client.helpers.ClientConstants.TO_REPLACE;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
import org.jboss.as.controller.client.Operation;
import org.jboss.as.controller.client.OperationBuilder;
import org.jboss.as.controller.client.logging.ControllerClientLogger;
import org.jboss.as.controller.client.helpers.domain.DeploymentPlan;
import org.jboss.as.controller.client.helpers.domain.DeploymentPlanResult;
import org.jboss.as.controller.client.helpers.domain.DomainDeploymentManager;
import org.jboss.as.controller.client.helpers.domain.DuplicateDeploymentNameException;
import org.jboss.as.controller.client.helpers.domain.InitialDeploymentPlanBuilder;
import org.jboss.as.controller.client.helpers.domain.ServerGroupDeploymentPlan;
import org.jboss.as.controller.client.helpers.domain.ServerIdentity;
import org.jboss.dmr.ModelNode;
/**
* Client-side {@link DomainDeploymentManager}.
*
* @author Brian Stansberry
*/
class DomainDeploymentManagerImpl implements DomainDeploymentManager {
private final DomainClientImpl client;
private final DeploymentContentDistributor contentDistributor;
DomainDeploymentManagerImpl(final DomainClientImpl client) {
assert client != null : "client is null";
this.client = client;
this.contentDistributor = new DeploymentContentDistributor() {
@Override
public byte[] distributeDeploymentContent(String name, String runtimeName, InputStream stream)
throws IOException, DuplicateDeploymentNameException {
boolean unique = DomainDeploymentManagerImpl.this.client.isDeploymentNameUnique(name);
if (!unique) {
throw new DuplicateDeploymentNameException(name, false);
}
return DomainDeploymentManagerImpl.this.client.addDeploymentContent(stream);
}
@Override
public byte[] distributeReplacementDeploymentContent(String name, String runtimeName, InputStream stream)
throws IOException {
return DomainDeploymentManagerImpl.this.client.addDeploymentContent(stream);
}
};
}
@Override
public Future<DeploymentPlanResult> execute(DeploymentPlan plan) {
if (!(plan instanceof DeploymentPlanImpl)) {
throw ControllerClientLogger.ROOT_LOGGER.cannotUseDeploymentPlan();
}
DeploymentPlanImpl planImpl = DeploymentPlanImpl.class.cast(plan);
Map<UUID, List<String>> actionsById = new HashMap<UUID, List<String>>();
Operation operation = getDeploymentPlanOperation(planImpl, actionsById);
Future<ModelNode> future = client.executeAsync(operation, null);
return new DomainDeploymentPlanResultFuture(planImpl, future, new LinkedHashSet<ServerIdentity>(client.getServerStatuses().keySet()), actionsById);
}
@Override
public InitialDeploymentPlanBuilder newDeploymentPlan() {
return InitialDeploymentPlanBuilderFactory.newInitialDeploymentPlanBuilder(this.contentDistributor);
}
private Operation getDeploymentPlanOperation(DeploymentPlanImpl plan, Map<UUID, List<String>> actionsById) {
Operation op = getCompositeOperation(plan, actionsById);
addRollbackPlan(plan, op);
return op;
}
private Operation getCompositeOperation(DeploymentPlanImpl plan, Map<UUID, List<String>> actionsById) {
Set<String> deployments = getCurrentDomainDeployments();
Set<String> serverGroups = getServerGroupNames(plan);
ModelNode op = new ModelNode();
op.get(OP).set(COMPOSITE);
op.get(OP_ADDR).setEmptyList();
ModelNode steps = op.get(STEPS);
steps.setEmptyList();
op.get(OPERATION_HEADERS, ROLLBACK_ON_RUNTIME_FAILURE).set(plan.isSingleServerRollback());
// FIXME deal with shutdown params
OperationBuilder builder = new OperationBuilder(op);
int stepNum = 1;
for (DeploymentActionImpl action : plan.getDeploymentActionImpls()) {
final List<String> actionStepIds = new ArrayList<String>();
actionsById.put(action.getId(), actionStepIds);
List<ModelNode> actionSteps = new ArrayList<ModelNode>();
String uniqueName = action.getDeploymentUnitUniqueName();
switch (action.getType()) {
case ADD: {
if (!deployments.contains(uniqueName)) {
// We need to add to the domain
ModelNode step = configureDeploymentOperation(ADD, uniqueName, null);
step.get(RUNTIME_NAME).set(action.getNewContentFileName());
step.get(CONTENT).get(0).get("hash").set(action.getNewContentHash());
actionSteps.add(step);
}
for (String group : serverGroups) {
ModelNode groupStep = configureDeploymentOperation(ADD, uniqueName, group);
actionSteps.add(groupStep);
}
break;
}
case DEPLOY: {
for (String group : serverGroups) {
ModelNode groupStep = configureDeploymentOperation(DEPLOYMENT_DEPLOY_OPERATION, uniqueName, group);
actionSteps.add(groupStep);
}
break;
}
case FULL_REPLACE: {
ModelNode step = new ModelNode();
step.get(OP).set(DEPLOYMENT_FULL_REPLACE_OPERATION);
step.get(OP_ADDR).setEmptyList();
step.get(NAME).set(uniqueName);
step.get(RUNTIME_NAME).set(action.getNewContentFileName());
step.get(CONTENT).get(0).get("hash").set(action.getNewContentHash());
actionSteps.add(step);
break;
}
case REDEPLOY: {
for (String group : serverGroups) {
ModelNode groupStep = configureDeploymentOperation(DEPLOYMENT_REDEPLOY_OPERATION, uniqueName, group);
actionSteps.add(groupStep);
}
break;
}
case REMOVE: {
// From each group
for (String group : serverGroups) {
ModelNode groupStep = configureDeploymentOperation(DEPLOYMENT_REMOVE_OPERATION, uniqueName, group);
actionSteps.add(groupStep);
}
// and from the domain
ModelNode step = configureDeploymentOperation(DEPLOYMENT_REMOVE_OPERATION, uniqueName, null);
actionSteps.add(step);
break;
}
case REPLACE: {
for (String group : serverGroups) {
ModelNode groupStep = new ModelNode();
groupStep.get(OP).set(DEPLOYMENT_REPLACE_OPERATION);
groupStep.get(OP_ADDR).add("server-group", group);
groupStep.get(NAME).set(uniqueName);
groupStep.get(TO_REPLACE).set(action.getReplacedDeploymentUnitUniqueName());
actionSteps.add(groupStep);
}
break;
}
case UNDEPLOY: {
for (String group : serverGroups) {
ModelNode groupStep = configureDeploymentOperation(DEPLOYMENT_UNDEPLOY_OPERATION, uniqueName, group);
actionSteps.add(groupStep);
}
break;
}
default: {
throw ControllerClientLogger.ROOT_LOGGER.unknownActionType(action.getType());
}
}
for (ModelNode actionStep : actionSteps) {
actionStepIds.add(String.format("step-%d", stepNum++));
steps.add(actionStep);
}
}
return builder.build();
}
private Set<String> getCurrentDomainDeployments() {
ModelNode op = new ModelNode();
op.get("operation").set("read-children-names");
op.get("child-type").set("deployment");
ModelNode rsp = client.executeForResult(new OperationBuilder(op).build());
Set<String> deployments = new HashSet<String>();
if (rsp.isDefined()) {
for (ModelNode node : rsp.asList()) {
deployments.add(node.asString());
}
}
return deployments;
}
private void addRollbackPlan(DeploymentPlanImpl plan, Operation op) {
ModelNode opNode = op.getOperation();
ModelNode rolloutPlan = opNode.get(OPERATION_HEADERS, ROLLOUT_PLAN);
rolloutPlan.get("rollback-across-groups").set(plan.isRollbackAcrossGroups());
ModelNode series = rolloutPlan.get("in-series");
for (Set<ServerGroupDeploymentPlan> concurrent : plan.getServerGroupDeploymentPlans()) {
if (concurrent.size() == 1) {
ModelNode single = new ModelNode();
ServerGroupDeploymentPlan sgdp = concurrent.iterator().next();
single.get("server-group", sgdp.getServerGroupName()).set(createServerGroupPlan(sgdp));
series.add(single);
}
else {
ModelNode multiple = new ModelNode();
for (ServerGroupDeploymentPlan sgdp : concurrent) {
multiple.get("concurrent-groups", sgdp.getServerGroupName()).set(createServerGroupPlan(sgdp));
}
series.add(multiple);
}
}
}
private ModelNode createServerGroupPlan(ServerGroupDeploymentPlan sgdp) {
ModelNode result = new ModelNode();
result.get("rolling-to-servers").set(sgdp.isRollingToServers());
if (sgdp.isRollback()) {
if (sgdp.getMaxServerFailurePercentage() > 0) {
result.get("max-failure-percentage").set(sgdp.getMaxServerFailurePercentage());
}
else {
result.get("max-failed-servers").set(sgdp.getMaxServerFailures());
}
}
else {
result.get("max-failure-percentage").set(100);
}
return result;
}
private Set<String> getServerGroupNames(DeploymentPlan plan) {
Set<String> names = new HashSet<String>();
for (Set<ServerGroupDeploymentPlan> sgdps : plan.getServerGroupDeploymentPlans()) {
for (ServerGroupDeploymentPlan sgdp : sgdps) {
names.add(sgdp.getServerGroupName());
}
}
return names;
}
private ModelNode configureDeploymentOperation(String operationName, String uniqueName, String serverGroup) {
ModelNode op = new ModelNode();
op.get(OP).set(operationName);
if (serverGroup != null) {
op.get(OP_ADDR).add("server-group", serverGroup);
}
op.get(OP_ADDR).add(DEPLOYMENT, uniqueName);
return op;
}
}