blob: 6808d634ccfa22353d90af076698ba2ec4b60504 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.slider.providers.agent;
import org.apache.slider.providers.agent.application.metadata.CommandOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Stores the command dependency order for all components in a service. <commandOrder>
* <command>SUPERVISOR-START</command> <requires>NIMBUS-STARTED</requires> </commandOrder> Means, SUPERVISOR START
* requires NIMBUS to be STARTED
*/
public class ComponentCommandOrder {
public static final Logger log =
LoggerFactory.getLogger(ComponentCommandOrder.class);
private static char SPLIT_CHAR = '-';
Map<Command, Map<String, List<ComponentState>>> dependencies = new HashMap<>();
public ComponentCommandOrder(List<CommandOrder> commandOrders) {
if (commandOrders != null && commandOrders.size() > 0) {
for (CommandOrder commandOrder : commandOrders) {
ComponentCommand componentCmd = getComponentCommand(commandOrder.getCommand());
String requires = commandOrder.getRequires();
List<ComponentState> requiredStates = parseRequiredStates(requires);
if (requiredStates.size() > 0) {
Map<String, List<ComponentState>> compDep = dependencies.get(componentCmd.command);
if (compDep == null) {
compDep = new HashMap<>();
dependencies.put(componentCmd.command, compDep);
}
List<ComponentState> requirements = compDep.get(componentCmd.componentName);
if (requirements == null) {
requirements = new ArrayList<>();
compDep.put(componentCmd.componentName, requirements);
}
requirements.addAll(requiredStates);
}
}
}
}
private List<ComponentState> parseRequiredStates(String requires) {
if (requires == null || requires.length() < 2) {
throw new IllegalArgumentException("Input cannot be null and must contain component and state.");
}
String[] componentStates = requires.split(",");
List<ComponentState> retList = new ArrayList<>();
for (String componentStateStr : componentStates) {
retList.add(getComponentState(componentStateStr));
}
return retList;
}
private ComponentCommand getComponentCommand(String compCmdStr) {
if (compCmdStr == null || compCmdStr.trim().length() < 2) {
throw new IllegalArgumentException("Input cannot be null and must contain component and command.");
}
compCmdStr = compCmdStr.trim();
int splitIndex = compCmdStr.lastIndexOf(SPLIT_CHAR);
if (splitIndex == -1 || splitIndex == 0 || splitIndex == compCmdStr.length() - 1) {
throw new IllegalArgumentException("Input does not appear to be well-formed.");
}
String compStr = compCmdStr.substring(0, splitIndex);
String cmdStr = compCmdStr.substring(splitIndex + 1);
Command cmd = Command.valueOf(cmdStr);
if(cmd != Command.START) {
throw new IllegalArgumentException("Dependency order can only be specified for START.");
}
return new ComponentCommand(compStr, cmd);
}
private ComponentState getComponentState(String compStStr) {
if (compStStr == null || compStStr.trim().length() < 2) {
throw new IllegalArgumentException("Input cannot be null.");
}
compStStr = compStStr.trim();
int splitIndex = compStStr.lastIndexOf(SPLIT_CHAR);
if (splitIndex == -1 || splitIndex == 0 || splitIndex == compStStr.length() - 1) {
throw new IllegalArgumentException("Input does not appear to be well-formed.");
}
String compStr = compStStr.substring(0, splitIndex);
String stateStr = compStStr.substring(splitIndex + 1);
State state = State.valueOf(stateStr);
if(state != State.STARTED) {
throw new IllegalArgumentException("Dependency order can only be specified against STARTED.");
}
return new ComponentState(compStr, state);
}
public boolean canExecute(String component, Command command, Collection<ComponentInstanceState> currentStates) {
boolean canExecute = true;
if (dependencies.containsKey(command) && dependencies.get(command).containsKey(component)) {
List<ComponentState> required = dependencies.get(command).get(component);
for (ComponentState stateToMatch : required) {
for (ComponentInstanceState currState : currentStates) {
log.debug("Checking schedule {} {} against dependency {} is {}",
component, command, currState.getCompName(), currState.getState());
if (currState.getCompName().equals(stateToMatch.componentName)) {
if (currState.getState() != stateToMatch.state) {
log.info("Cannot schedule {} {} as dependency {} is {}",
component, command, currState.getCompName(), currState.getState());
canExecute = false;
}
}
if (!canExecute) {
break;
}
}
if (!canExecute) {
break;
}
}
}
return canExecute;
}
static class ComponentState {
public String componentName;
public State state;
public ComponentState(String componentName, State state) {
this.componentName = componentName;
this.state = state;
}
}
static class ComponentCommand {
public String componentName;
public Command command;
public ComponentCommand(String componentName, Command command) {
this.componentName = componentName;
this.command = command;
}
}
}