You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by ji...@apache.org on 2016/12/07 21:10:18 UTC

[18/76] [abbrv] hadoop git commit: YARN-5461. Initial code ported from slider-core module. (jianhe)

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentRoles.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentRoles.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentRoles.java
new file mode 100644
index 0000000..281895a
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentRoles.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ProviderRole;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AgentRoles {
+
+  /**
+   * List of roles Agent provider does not have any roles by default. All roles are read from the application
+   * specification.
+   */
+  protected static final List<ProviderRole> ROLES =
+      new ArrayList<ProviderRole>();
+
+  public static List<ProviderRole> getRoles() {
+    return ROLES;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentUtils.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentUtils.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentUtils.java
new file mode 100644
index 0000000..cfcfc5d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/AgentUtils.java
@@ -0,0 +1,134 @@
+/*
+ * 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.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.slider.common.tools.SliderFileSystem;
+import org.apache.slider.common.tools.SliderUtils;
+import org.apache.slider.core.exceptions.BadConfigException;
+import org.apache.slider.providers.agent.application.metadata.AbstractMetainfoParser;
+import org.apache.slider.providers.agent.application.metadata.AddonPackageMetainfoParser;
+import org.apache.slider.providers.agent.application.metadata.DefaultConfig;
+import org.apache.slider.providers.agent.application.metadata.DefaultConfigParser;
+import org.apache.slider.providers.agent.application.metadata.Metainfo;
+import org.apache.slider.providers.agent.application.metadata.MetainfoParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ *
+ */
+public class AgentUtils {
+  private static final Logger log = LoggerFactory.getLogger(AgentUtils.class);
+
+  public static Metainfo getApplicationMetainfoFromSummaryFile(
+      SliderFileSystem fileSystem, String metainfoPath, boolean metainfoForAddon) {
+    FileSystem fs = fileSystem.getFileSystem();
+    Path appPathXML = new Path(metainfoPath + ".metainfo.xml");
+    Path appPathJson = new Path(metainfoPath + ".metainfo.json");
+    Path appPathUsed = null;
+    try {
+      FSDataInputStream appStream = null;
+      if (fs.exists(appPathXML)) {
+        appPathUsed = appPathXML;
+        appStream = fs.open(appPathXML);
+        return parseMetainfo(appStream, metainfoForAddon, "xml");
+      } else if (fs.exists(appPathJson)) {
+        appPathUsed = appPathJson;
+        appStream = fs.open(appPathJson);
+        return parseMetainfo(appStream, metainfoForAddon, "json");
+      }
+    } catch (IOException e) {
+      log.info("Failed to get metainfo from summary file {} - {}", appPathUsed,
+          e.getMessage());
+      log.debug("Failed to get metainfo", e);
+    }
+    return null;
+  }
+
+  public static Metainfo getApplicationMetainfo(SliderFileSystem fileSystem,
+      String metainfoPath, boolean metainfoForAddon) throws IOException,
+      BadConfigException {
+    log.info("Reading metainfo at {}", metainfoPath);
+    Metainfo metainfo = getApplicationMetainfoFromSummaryFile(fileSystem,
+        metainfoPath, metainfoForAddon);
+    if (metainfo != null) {
+      log.info("Got metainfo from summary file");
+      return metainfo;
+    }
+
+    FileSystem fs = fileSystem.getFileSystem();
+    Path appPath = new Path(metainfoPath);
+
+    InputStream metainfoJsonStream = SliderUtils.getApplicationResourceInputStream(
+        fs, appPath, "metainfo.json");
+    if (metainfoJsonStream == null) {
+      InputStream metainfoXMLStream = SliderUtils.getApplicationResourceInputStream(
+          fs, appPath, "metainfo.xml");
+      if (metainfoXMLStream != null) {
+        metainfo = parseMetainfo(metainfoXMLStream, metainfoForAddon, "xml");
+      }
+    } else {
+      metainfo = parseMetainfo(metainfoJsonStream, metainfoForAddon, "json");
+    }
+
+    if (metainfo == null) {
+      log.error("metainfo is unavailable at {}.", metainfoPath);
+      throw new FileNotFoundException("metainfo.xml/json is required in app package. " +
+                                      appPath);
+    }
+    return metainfo;
+  }
+
+  private static Metainfo parseMetainfo(InputStream stream,
+      boolean metainfoForAddon, String type) throws IOException {
+    AbstractMetainfoParser metainfoParser = null;
+    if (metainfoForAddon) {
+      metainfoParser = new AddonPackageMetainfoParser();
+    } else {
+      metainfoParser = new MetainfoParser();
+    }
+    if (type.equals("xml")) {
+      return metainfoParser.fromXmlStream(stream);
+    } else if (type.equals("json")) {
+      return metainfoParser.fromJsonStream(stream);
+    }
+    return null;
+  }
+
+  static DefaultConfig getDefaultConfig(SliderFileSystem fileSystem,
+                                        String appDef, String configFileName)
+      throws IOException {
+    // this is the path inside the zip file
+    String fileToRead = "configuration/" + configFileName;
+    log.info("Reading default config file {} at {}", fileToRead, appDef);
+    InputStream configStream = SliderUtils.getApplicationResourceInputStream(
+        fileSystem.getFileSystem(), new Path(appDef), fileToRead);
+    if (configStream == null) {
+      log.error("{} is unavailable at {}.", fileToRead, appDef);
+      throw new IOException("Expected config file " + fileToRead + " is not available.");
+    }
+
+    return new DefaultConfigParser().parse(configStream);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/Command.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/Command.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/Command.java
new file mode 100644
index 0000000..647cb86
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/Command.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+/** The states a component instance can be. */
+public enum Command {
+  NOP,           // do nothing
+  INSTALL,       // Install the component
+  INSTALL_ADDON, // Install add on packages if any
+  START,         // Start the component
+  STOP,          // Stop the component
+  UPGRADE,       // The component will undergo upgrade
+  TERMINATE;     // Send terminate signal to agent
+
+  public static Command getCommand(String commandVal) {
+    if (commandVal.equals(Command.START.toString())) {
+      return Command.START;
+    }
+    if (commandVal.equals(Command.INSTALL.toString())) {
+      return Command.INSTALL;
+    }
+    if (commandVal.equals(Command.STOP.toString())) {
+      return Command.STOP;
+    }
+    if (commandVal.equals(Command.UPGRADE.toString())) {
+      return Command.UPGRADE;
+    }
+    if (commandVal.equals(Command.TERMINATE.toString())) {
+      return Command.TERMINATE;
+    }
+
+    return Command.NOP;
+  }
+
+  public static String transform(Command command, boolean isUpgrade) {
+    switch (command) {
+    case STOP:
+      return isUpgrade ? "UPGRADE_STOP" : command.name();
+    default:
+      return command.name();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/CommandResult.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/CommandResult.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/CommandResult.java
new file mode 100644
index 0000000..35d9116
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/CommandResult.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+/** Command results. */
+public enum CommandResult {
+  IN_PROGRESS,  // Command is in progress
+  COMPLETED,    // Command has successfully completed
+  FAILED;        // Command has failed
+
+  public static CommandResult getCommandResult(String commandResVal) {
+    if (commandResVal.equals(CommandResult.COMPLETED.toString())) {
+      return CommandResult.COMPLETED;
+    }
+    if (commandResVal.equals(CommandResult.FAILED.toString())) {
+      return CommandResult.FAILED;
+    }
+    if (commandResVal.equals(CommandResult.IN_PROGRESS.toString())) {
+      return CommandResult.IN_PROGRESS;
+    }
+
+    throw new IllegalArgumentException("Unrecognized value " + commandResVal);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentCommandOrder.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentCommandOrder.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentCommandOrder.java
new file mode 100644
index 0000000..91f1259
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentCommandOrder.java
@@ -0,0 +1,181 @@
+/*
+ * 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<Command, Map<String, List<ComponentState>>>();
+
+  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<ComponentState>();
+    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 && state != State.INSTALLED) {
+      throw new IllegalArgumentException("Dependency order can only be specified against STARTED/INSTALLED.");
+    }
+    return new ComponentState(compStr, state);
+  }
+
+  // dependency is still on component level, but not package level
+  // so use component name to check dependency, not component-package
+  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.getComponentName(), currState.getState());
+          if (currState.getComponentName().equals(stateToMatch.componentName)) {
+            if (currState.getState() != stateToMatch.state) {
+              if (stateToMatch.state == State.STARTED) {
+                log.info("Cannot schedule {} {} as dependency {} is {}",
+                         component, command, currState.getComponentName(), currState.getState());
+                canExecute = false;
+              } else {
+                //state is INSTALLED
+                if (currState.getState() != State.STARTING && currState.getState() != State.STARTED) {
+                  log.info("Cannot schedule {} {} as dependency {} is {}",
+                           component, command, currState.getComponentName(), 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;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentInstanceState.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentInstanceState.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentInstanceState.java
new file mode 100644
index 0000000..6ee0ebb
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentInstanceState.java
@@ -0,0 +1,340 @@
+/*
+ * 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 java.util.Map;
+import java.util.TreeMap;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.yarn.api.records.ContainerId;
+import org.apache.slider.providers.agent.application.metadata.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This class implements a simple state machine for component instances. */
+public class ComponentInstanceState {
+  public static final Logger log =
+      LoggerFactory.getLogger(ComponentInstanceState.class);
+  private static int MAX_FAILURE_TOLERATED = 3;
+  private static String INVALID_TRANSITION_ERROR =
+      "Result %s for command %s is not expected for component %s in state %s.";
+
+  private final String componentName;
+  private final ContainerId containerId;
+  private final String containerIdAsString;
+  private final String applicationId;
+  private State state = State.INIT;
+  private State targetState = State.STARTED;
+  private int failuresSeen = 0;
+  private Boolean configReported = false;
+  private long lastHeartbeat = 0;
+  private String ip;
+  private String hostname;
+  private ContainerState containerState;
+
+  private Map<String, State> pkgStatuses;
+  private String nextPkgToInstall;
+
+  private boolean stopInitiated;
+
+  public ComponentInstanceState(String componentName,
+      ContainerId containerId,
+      String applicationId) {
+    this(componentName, containerId, applicationId,
+        new TreeMap<String, State>());
+  }
+
+  public ComponentInstanceState(String componentName,
+      ContainerId containerId,
+      String applicationId, Map<String, State> pkgStatuses) {
+    this.componentName = componentName;
+    this.containerId = containerId;
+    this.containerIdAsString = containerId.toString();
+    this.applicationId = applicationId;
+    this.containerState = ContainerState.INIT;
+    this.lastHeartbeat = System.currentTimeMillis();
+    this.pkgStatuses = pkgStatuses;
+  }
+  
+  public String getComponentName() {
+    return componentName;
+  }
+
+  public Boolean getConfigReported() {
+    return configReported;
+  }
+
+  public void setConfigReported(Boolean configReported) {
+    this.configReported = configReported;
+  }
+
+  public ContainerState getContainerState() {
+    return containerState;
+  }
+
+  public void setContainerState(ContainerState containerState) {
+    this.containerState = containerState;
+  }
+
+  public long getLastHeartbeat() {
+    return lastHeartbeat;
+  }
+
+  /**
+   * Update the heartbeat, and change container state
+   * to mark as healthy if appropriate
+   * @param heartbeatTime last time the heartbeat was seen
+   * @return the current container state
+   */
+  public ContainerState heartbeat(long heartbeatTime) {
+    this.lastHeartbeat = heartbeatTime;
+    if(containerState == ContainerState.UNHEALTHY ||
+       containerState == ContainerState.INIT) {
+      containerState = ContainerState.HEALTHY;
+    }
+    return containerState;
+  }
+  
+
+  public ContainerId getContainerId() {
+    return containerId;
+  }
+
+  public void commandIssued(Command command) {
+    commandIssued(command, false);
+  }
+
+  public void commandIssued(Command command, boolean isInUpgradeMode) {
+    Command expected = getNextCommand(isInUpgradeMode);
+    if (expected != command) {
+      throw new IllegalArgumentException("Command " + command + " is not allowed in state " + state);
+    }
+    if (expected == Command.INSTALL_ADDON) {
+      // for add on packages, the pkg must be nextPkgToInstall
+      State currentState = pkgStatuses.get(nextPkgToInstall);
+      log.debug("Command issued: component: {} is in {}", componentName,
+          currentState);
+      State nextState = currentState.getNextState(command);
+      pkgStatuses.put(nextPkgToInstall, nextState);
+      log.debug("Command issued: component: {} is now in {}", componentName,
+          nextState);
+    } else {
+      // for master package
+      state = state.getNextState(command);
+    }
+  }
+
+  public void applyCommandResult(CommandResult result, Command command,
+      String pkg) {
+    // if the heartbeat is for a package
+    // update that package's state in the component status
+    // and don't bother with the master pkg
+    if (StringUtils.isNotEmpty(pkg)
+        && !Component.MASTER_PACKAGE_NAME.equals(pkg)) {
+      log.debug("This result is for component: {} pkg: {}", componentName, pkg);
+      State previousPkgState = pkgStatuses.get(pkg);
+      log.debug("Currently component: {} pkg: {} is in state: {}",
+          componentName, pkg, previousPkgState.toString());
+      State nextPkgState = previousPkgState.getNextState(result);
+      pkgStatuses.put(pkg, nextPkgState);
+      log.debug("Component: {} pkg: {} next state: {}", componentName, pkg,
+          nextPkgState);
+    } else {
+      log.debug("This result is for component: {} master package",
+          componentName);
+      applyCommandResult(result, command);
+    }
+  }
+
+  public void applyCommandResult(CommandResult result, Command command) {
+    if (!this.state.couldHaveIssued(command)) {
+      throw new IllegalStateException("Invalid command " + command + " for state " + this.state);
+    }
+
+    try {
+      if (result == CommandResult.FAILED) {
+        failuresSeen++;
+      } else if (result == CommandResult.COMPLETED) {
+        failuresSeen = 0;
+      }
+      state = state.getNextState(result);
+    } catch (IllegalArgumentException e) {
+      String message = String.format(INVALID_TRANSITION_ERROR,
+                                     result.toString(),
+                                     command.toString(),
+                                     componentName,
+                                     state.toString());
+      log.warn(message);
+      throw new IllegalStateException(message);
+    }
+  }
+
+  public boolean hasPendingCommand() {
+    if (state.canIssueCommands() &&
+        state != targetState &&
+        failuresSeen < MAX_FAILURE_TOLERATED) {
+      return true;
+    }
+
+    return false;
+  }
+
+  public Command getNextCommand() {
+    return getNextCommand(false);
+  }
+
+  public Command getNextCommand(boolean isInUpgradeMode) {
+    if (!hasPendingCommand()) {
+      nextPkgToInstall = null;
+      return Command.NOP;
+    }
+
+    log.debug("In getNextCommand, checking for component: {} ", componentName);
+    // if the master pkg is just installed, check if any add on pkg need to be
+    // installed
+    nextPkgToInstall = null;
+    if (state == State.INSTALLED) {
+      for (Map.Entry<String, State> pkgStatus : pkgStatuses.entrySet()) {
+        String pkg = pkgStatus.getKey();
+        State pkgState = pkgStatus.getValue();
+        log.debug("In getNextCommand, pkg: {} is in {}", pkg, pkgState);
+        if (pkgState == State.INSTALLING) {
+          // first check if any pkg is install in progress, if so, wait
+          // so we don't need to do anything, just return NOP
+          log.debug("In getNextCommand, pkg: {} we are issuing NOP", pkg);
+          nextPkgToInstall = pkg;
+          return Command.NOP;
+        } else if (pkgState == State.INIT) {
+          // temporarily storing pkg here
+          // in case no pkg in 'installing' state
+          // will return the package to install
+          nextPkgToInstall = pkg;
+        }
+      }
+      // when we reach here, no pkg is in 'installing' state
+      if (nextPkgToInstall != null) {
+        // nextPkgToInstall != null means some pkg is in INIT state 
+        // issue 'install' to the pkg we have stored in nextPkgToInstall
+        log.debug("In getNextCommand, pkg: {} we are issuing install addon",
+            nextPkgToInstall);
+        return Command.INSTALL_ADDON;
+      }
+    }
+    return this.state.getSupportedCommand(isInUpgradeMode, stopInitiated);
+  }
+
+  public State getState() {
+    return state;
+  }
+
+  @VisibleForTesting
+  protected void setState(State state) {
+    this.state = state;
+  }
+
+  public State getTargetState() {
+    return targetState;
+  }
+
+  public void setTargetState(State targetState) {
+    this.targetState = targetState;
+  }
+
+  public String getNextPkgToInstall() {
+    return nextPkgToInstall;
+  }
+
+  public boolean isStopInitiated() {
+    return stopInitiated;
+  }
+
+  public void setStopInitiated(boolean stopInitiated) {
+    this.stopInitiated = stopInitiated;
+  }
+
+  @Override
+  public int hashCode() {
+    int hashCode = 1;
+
+    hashCode = hashCode ^ (componentName != null ? componentName.hashCode() : 0);
+    hashCode = hashCode ^ (containerIdAsString != null ? containerIdAsString.hashCode() : 0);
+    hashCode = hashCode ^ (applicationId != null ? applicationId.hashCode() : 0);
+    return hashCode;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ComponentInstanceState that = (ComponentInstanceState) o;
+
+    if (this.componentName != null ?
+        !this.componentName.equals(that.componentName) : this.componentName != null) {
+      return false;
+    }
+
+    if (this.containerIdAsString != null ?
+        !this.containerIdAsString.equals(that.containerIdAsString) : this.containerIdAsString != null) {
+      return false;
+    }
+
+    if (this.applicationId != null ?
+        !this.applicationId.equals(that.applicationId) : this.applicationId != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("ComponentInstanceState{");
+    sb.append("containerIdAsString='").append(containerIdAsString).append('\'');
+    sb.append(", state=").append(state);
+    sb.append(", failuresSeen=").append(failuresSeen);
+    sb.append(", lastHeartbeat=").append(lastHeartbeat);
+    sb.append(", containerState=").append(containerState);
+    sb.append(", componentName='").append(componentName).append('\'');
+    sb.append(", ip=").append(ip);
+    sb.append(", hostname='").append(hostname).append('\'');
+    sb.append('}');
+    return sb.toString();
+  }
+
+  public String getIp() {
+    return ip;
+  }
+
+  public void setIp(String ip) {
+    this.ip = ip;
+  }
+
+  public String getHostname() {
+    return hostname;
+  }
+
+  public void setHostname(String hostname) {
+    this.hostname = hostname;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentTagProvider.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentTagProvider.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentTagProvider.java
new file mode 100644
index 0000000..68f63fa
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ComponentTagProvider.java
@@ -0,0 +1,127 @@
+/*
+ * 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.common.tools.SliderUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/** A simple tag provider that attempts to associate tags from 1-N to all container of a component */
+public class ComponentTagProvider {
+  private static final Logger log = LoggerFactory.getLogger(ComponentTagProvider.class);
+  private static String FREE = "free";
+  private final ConcurrentHashMap<String, ConcurrentHashMap<String, String>> allTags;
+
+  public ComponentTagProvider() {
+    allTags = new ConcurrentHashMap<String, ConcurrentHashMap<String, String>>();
+  }
+
+  /**
+   * Record an assigned tag to a container
+   *
+   * @param component
+   * @param containerId
+   * @param tag
+   */
+  public void recordAssignedTag(String component, String containerId, String tag) {
+    if (SliderUtils.isSet(component) && SliderUtils.isSet(containerId)) {
+      Integer key = null;
+      try {
+        key = Integer.valueOf(tag);
+      } catch (NumberFormatException nfe) {
+        //ignore
+      }
+      if (key != null && key > 0) {
+        ConcurrentHashMap<String, String> compTags = getComponentSpecificTags(component);
+        synchronized (compTags) {
+          for (int index = 1; index <= key.intValue(); index++) {
+            String tempKey = new Integer(index).toString();
+            if (!compTags.containsKey(tempKey)) {
+              compTags.put(tempKey, FREE);
+            }
+          }
+          compTags.put(key.toString(), containerId);
+        }
+      }
+    }
+  }
+
+  /**
+   * Get a tag for container
+   *
+   * @param component
+   * @param containerId
+   *
+   * @return
+   */
+  public String getTag(String component, String containerId) {
+    if (SliderUtils.isSet(component) && SliderUtils.isSet(containerId)) {
+      ConcurrentHashMap<String, String> compTags = getComponentSpecificTags(component);
+      synchronized (compTags) {
+        for (String key : compTags.keySet()) {
+          if (compTags.get(key).equals(containerId)) {
+            return key;
+          }
+        }
+        for (String key : compTags.keySet()) {
+          if (compTags.get(key).equals(FREE)) {
+            compTags.put(key, containerId);
+            return key;
+          }
+        }
+        String newKey = new Integer(compTags.size() + 1).toString();
+        compTags.put(newKey, containerId);
+        return newKey;
+      }
+    }
+    return "";
+  }
+
+  /**
+   * Release a tag associated with a container
+   *
+   * @param component
+   * @param containerId
+   */
+  public void releaseTag(String component, String containerId) {
+    if (SliderUtils.isSet(component) && SliderUtils.isSet(containerId)) {
+      ConcurrentHashMap<String, String> compTags = allTags.get(component);
+      if (compTags != null) {
+        synchronized (compTags) {
+          for (String key : compTags.keySet()) {
+            if (compTags.get(key).equals(containerId)) {
+              compTags.put(key, FREE);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private ConcurrentHashMap<String, String> getComponentSpecificTags(String component) {
+    if (!allTags.containsKey(component)) {
+      synchronized (allTags) {
+        if (!allTags.containsKey(component)) {
+          allTags.put(component, new ConcurrentHashMap<String, String>());
+        }
+      }
+    }
+    return allTags.get(component);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ContainerState.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ContainerState.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ContainerState.java
new file mode 100644
index 0000000..0394ba2
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/ContainerState.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+/** The states a component instance can be. */
+public enum ContainerState {
+  INIT,           // Container is not net activated
+  HEALTHY,     // Agent is heartbeating
+  UNHEALTHY,      // Container is unhealthy - no heartbeat for some interval
+  HEARTBEAT_LOST;  // Container is lost - request a new instance
+
+  /**
+   * Indicates whether or not it is a valid state to produce a command.
+   *
+   * @return true if command can be issued for this state.
+   */
+  public boolean canIssueCommands() {
+    switch (this) {
+      case HEALTHY:
+        return true;
+      default:
+        return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/HeartbeatMonitor.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/HeartbeatMonitor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/HeartbeatMonitor.java
new file mode 100644
index 0000000..4293916
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/HeartbeatMonitor.java
@@ -0,0 +1,130 @@
+/**
+ * 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 com.google.common.annotations.VisibleForTesting;
+
+import org.apache.hadoop.yarn.api.records.ContainerId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+/** Monitors the container state and heartbeats. */
+public class HeartbeatMonitor implements Runnable {
+  protected static final Logger log =
+      LoggerFactory.getLogger(HeartbeatMonitor.class);
+  private final int threadWakeupInterval; //1 minute
+  private final AgentProviderService provider;
+  private volatile boolean shouldRun = true;
+  private Thread monitorThread = null;
+
+  public HeartbeatMonitor(AgentProviderService provider, int threadWakeupInterval) {
+    this.provider = provider;
+    this.threadWakeupInterval = threadWakeupInterval;
+  }
+
+  public void shutdown() {
+    shouldRun = false;
+  }
+
+  public void start() {
+    log.info("Starting heartbeat monitor with interval {}", threadWakeupInterval);
+    monitorThread = new Thread(this);
+    monitorThread.start();
+  }
+
+  void join(long millis) throws InterruptedException {
+    if (isAlive()) {
+      monitorThread.join(millis);
+    }
+  }
+
+  public boolean isAlive() {
+    return monitorThread != null && monitorThread.isAlive();
+  }
+
+  @Override
+  public void run() {
+    while (shouldRun) {
+      try {
+        log.debug("Putting monitor to sleep for " + threadWakeupInterval + " " +
+                  "milliseconds");
+        Thread.sleep(threadWakeupInterval);
+        doWork(System.currentTimeMillis());
+      } catch (InterruptedException ex) {
+        log.warn("Scheduler thread is interrupted going to stop", ex);
+        shouldRun = false;
+      } catch (Exception ex) {
+        log.warn("Exception received", ex);
+      } catch (Throwable t) {
+        log.warn("ERROR", t);
+      }
+    }
+  }
+
+  /**
+   * Every interval the current state of the container are checked. If the state is INIT or HEALTHY and no HB are
+   * received in last check interval they are marked as UNHEALTHY. INIT is when the agent is started but it did not
+   * communicate at all. HEALTHY being the AM has received heartbeats. After an interval as UNHEALTHY the container is
+   * declared unavailable
+   * @param now current time in milliseconds ... tests can set this explicitly
+   */
+  @VisibleForTesting
+  public void doWork(long now) {
+    Map<String, ComponentInstanceState> componentStatuses = provider.getComponentStatuses();
+    if (componentStatuses != null) {
+      for (String containerLabel : componentStatuses.keySet()) {
+        ComponentInstanceState componentInstanceState = componentStatuses.get(containerLabel);
+        long timeSinceLastHeartbeat = now - componentInstanceState.getLastHeartbeat();
+
+        if (timeSinceLastHeartbeat > threadWakeupInterval) {
+          switch (componentInstanceState.getContainerState()) {
+            case INIT:
+            case HEALTHY:
+              componentInstanceState.setContainerState(ContainerState.UNHEALTHY);
+              log.warn(
+                  "Component {} marked UNHEALTHY. Last heartbeat received at {} approx. {} ms. back.",
+                  componentInstanceState,
+                  componentInstanceState.getLastHeartbeat(),
+                  timeSinceLastHeartbeat);
+              break;
+            case UNHEALTHY:
+              if (timeSinceLastHeartbeat > threadWakeupInterval * 2) {
+                componentInstanceState.setContainerState(
+                    ContainerState.HEARTBEAT_LOST);
+                log.warn(
+                    "Component {} marked HEARTBEAT_LOST. Last heartbeat received at {} approx. {} ms. back.",
+                    componentInstanceState, componentInstanceState.getLastHeartbeat(),
+                    timeSinceLastHeartbeat);
+                ContainerId containerId =
+                    componentInstanceState.getContainerId();
+                provider.lostContainer(containerLabel, containerId);
+              }
+              break;
+            case HEARTBEAT_LOST:
+              // unexpected case
+              log.warn("Heartbeat from lost component: {}", componentInstanceState);
+              break;
+          }
+            
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/State.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/State.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/State.java
new file mode 100644
index 0000000..5603f8d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/State.java
@@ -0,0 +1,199 @@
+/*
+ * 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;
+
+/** The states a component instance can be. */
+public enum State {
+  INIT,           // Not installed
+  INSTALLING,     // Being installed
+  INSTALLED,      // Installed (or stopped)
+  STARTING,       // Starting
+  STARTED,        // Started
+  INSTALL_FAILED, // Install failed, start failure in INSTALLED
+  UPGRADING,      // Undergoing upgrade, perform necessary pre-upgrade steps
+  UPGRADED,       // Pre-upgrade steps completed
+  STOPPING,       // Stop has been issued
+  STOPPED,        // Agent has stopped
+  TERMINATING;    // Terminate signal to ask the agent to kill itself
+                  // No need for state TERMINATED (as the agent is dead by then)
+
+  /**
+   * Indicates whether or not it is a valid state to produce a command.
+   *
+   * @return true if command can be issued for this state.
+   */
+  public boolean canIssueCommands() {
+    switch (this) {
+      case INSTALLING:
+      case STARTING:
+      case UPGRADING:
+      case STOPPING:
+      case TERMINATING:
+        return false;
+      default:
+        return true;
+    }
+  }
+
+  /**
+   * Returns valid command in this state.
+   *
+   * @return command allowed in this state.
+   */
+  public Command getSupportedCommand() {
+    return getSupportedCommand(false);
+  }
+
+  public Command getSupportedCommand(boolean isInUpgradeMode) {
+    return getSupportedCommand(isInUpgradeMode, false);
+  }
+
+  public Command getSupportedCommand(boolean isInUpgradeMode,
+      boolean stopInitiated) {
+    switch (this) {
+      case INIT:
+      case INSTALL_FAILED:
+        return Command.INSTALL;
+      case INSTALLED:
+        return Command.START;
+      case STARTED:
+      return isInUpgradeMode ? Command.UPGRADE : (stopInitiated) ? Command.STOP
+          : Command.NOP;
+      case UPGRADED:
+        return Command.STOP;
+      case STOPPED:
+        return Command.TERMINATE;
+      default:
+        return Command.NOP;
+    }
+  }
+
+  /**
+   * Returns next state based on the command result.
+   *
+   * @return next state.
+   */
+  public State getNextState(CommandResult result) throws IllegalArgumentException {
+    switch (result) {
+      case IN_PROGRESS:
+        if (this == State.INSTALLING || this == State.STARTING
+            || this == State.UPGRADING || this == State.STOPPING
+            || this == State.TERMINATING) {
+          return this;
+        } else {
+          throw new IllegalArgumentException(result + " is not valid for " + this);
+        }
+      case COMPLETED:
+        if (this == State.INSTALLING) {
+          return State.INSTALLED;
+        } else if (this == State.STARTING) {
+          return State.STARTED;
+        } else if (this == State.UPGRADING) {
+          return State.UPGRADED;
+        } else if (this == State.STOPPING) {
+          return State.STOPPED;
+        } else {
+          throw new IllegalArgumentException(result + " is not valid for " + this);
+        }
+      case FAILED:
+        if (this == State.INSTALLING) {
+          return State.INSTALL_FAILED;
+        } else if (this == State.STARTING) {
+          return State.INSTALLED;
+        } else if (this == State.UPGRADING) {
+          // if pre-upgrade failed, force stop now, so mark it upgraded
+          // what other options can be exposed to app owner?
+          return State.UPGRADED;
+        } else if (this == State.STOPPING) {
+          // if stop fails, force mark it stopped (and let container terminate)
+          return State.STOPPED;
+        } else if (this == State.STOPPED) {
+          // if in stopped state, force mark it as terminating
+          return State.TERMINATING;
+        } else {
+          throw new IllegalArgumentException(result + " is not valid for " + this);
+        }
+      default:
+        throw new IllegalArgumentException("Bad command result " + result);
+    }
+  }
+
+  /**
+   * Returns next state based on the command.
+   *
+   * @return next state.
+   */
+  public State getNextState(Command command) throws IllegalArgumentException {
+    switch (command) {
+      case INSTALL:
+        if (this == State.INIT || this == State.INSTALL_FAILED) {
+          return State.INSTALLING;
+        } else {
+          throw new IllegalArgumentException(command + " is not valid for " + this);
+        }
+      case INSTALL_ADDON:
+          if (this == State.INIT || this == State.INSTALL_FAILED) {
+            return State.INSTALLING;
+          } else {
+            throw new IllegalArgumentException(command + " is not valid for " + this);
+          }
+      case START:
+        if (this == State.INSTALLED) {
+          return State.STARTING;
+        } else {
+          throw new IllegalArgumentException(command + " is not valid for " + this);
+        }
+      case UPGRADE:
+        if (this == State.STARTED) {
+          return State.UPGRADING;
+        } else {
+          throw new IllegalArgumentException(command + " is not valid for " + this);
+        }
+      case STOP:
+        if (this == State.STARTED || this == State.UPGRADED) {
+          return State.STOPPING;
+        } else {
+          throw new IllegalArgumentException(command + " is not valid for " + this);
+        }
+      case TERMINATE:
+        if (this == State.STOPPED) {
+          return State.TERMINATING;
+        } else {
+          throw new IllegalArgumentException(command + " is not valid for " + this);
+        }
+      case NOP:
+        return this;
+      default:
+        throw new IllegalArgumentException("Bad command " + command);
+    }
+  }
+
+  public boolean couldHaveIssued(Command command) {
+    if ((this == State.INSTALLING && command == Command.INSTALL)
+        || (this == State.STARTING && command == Command.START)
+        || (this == State.UPGRADING && command == Command.UPGRADE)
+        || (this == State.STOPPING 
+           && (command == Command.STOP || command == Command.NOP))
+        || (this == State.TERMINATING && command == Command.TERMINATE)
+       ) {
+      return true;
+    }
+    return false;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java
new file mode 100644
index 0000000..b6ae4de
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java
@@ -0,0 +1,80 @@
+/*
+ * 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.application.metadata;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ *  Component defined in master package metainfo.json
+ */
+public abstract class AbstractComponent implements Validate {
+  public static final String TYPE_STANDARD = "STANDARD";
+  public static final String TYPE_DOCKER = "DOCKER";
+  public static final String TYPE_PYTHON = "PYTHON";
+  public static final String CATEGORY_MASTER = "MASTER";
+  public static final String CATEGORY_SLAVE = "SLAVE";
+  public static final String CATEGORY_CLIENT = "CLIENT";
+  public static final String MASTER_PACKAGE_NAME = "MASTER";
+
+  protected String name;
+  protected CommandScript commandScript;
+  protected List<ComponentCommand> commands = new ArrayList<>();
+
+  public AbstractComponent() {
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public CommandScript getCommandScript() {
+    return commandScript;
+  }
+
+  public void addCommandScript(CommandScript commandScript) {
+    this.commandScript = commandScript;
+  }
+
+  @JsonProperty("commands")
+  public List<ComponentCommand> getCommands() {
+    return commands;
+  }
+
+  public void setCommands(List<ComponentCommand> commands) {
+    this.commands = commands;
+  }
+
+  public void addCommand(ComponentCommand command) {
+    commands.add(command);
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder("{");
+    sb.append("\n\"name\": ").append(name);
+    sb.append(",\n\"commandScript\" :").append(commandScript);
+    sb.append('}');
+    return sb.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoParser.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoParser.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoParser.java
new file mode 100644
index 0000000..67d1f15
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoParser.java
@@ -0,0 +1,130 @@
+/*
+ * 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.application.metadata;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+
+/**
+ * This abstract class provide common functionality to parse metainfo.json for
+ * either master package or add on packages.
+ */
+public abstract class AbstractMetainfoParser {
+  protected final GsonBuilder gsonBuilder = new GsonBuilder();
+  protected final Gson gson;
+  private static final Logger log = LoggerFactory
+      .getLogger(AbstractMetainfoParser.class);
+
+  public AbstractMetainfoParser() {
+    gson = gsonBuilder.create();
+  }
+
+  /**
+   * Convert to a JSON string
+   *
+   * @return a JSON string description
+   *
+   * @throws IOException Problems mapping/writing the object
+   */
+  public String toJsonString(Metainfo metaInfo) throws IOException {
+    return gson.toJson(metaInfo);
+  }
+
+  /**
+   * Convert from JSON
+   *
+   * @param json input
+   *
+   * @return the parsed JSON
+   *
+   * @throws IOException IO
+   */
+  public Metainfo fromJsonString(String json)
+      throws IOException {
+    return gson.fromJson(json, Metainfo.class);
+  }
+
+  /**
+   * Parse metainfo from an IOStream
+   *
+   * @param is
+   *
+   * @return
+   *
+   * @throws IOException
+   */
+  public Metainfo fromJsonStream(InputStream is) throws IOException {
+    log.debug("loading from xml stream");
+    StringWriter writer = new StringWriter();
+    IOUtils.copy(is, writer);
+    return fromJsonString(writer.toString());
+  }
+
+  /**
+   * Parse metainfo from an XML formatted IOStream
+   *
+   * @param metainfoStream
+   *
+   * @return
+   *
+   * @throws IOException
+   */
+  public Metainfo fromXmlStream(InputStream metainfoStream) throws IOException {
+    log.debug("loading from xml stream");
+    Digester digester = new Digester();
+    digester.setValidating(false);
+
+    composeSchema(digester);
+
+    try {
+      return (Metainfo) digester.parse(metainfoStream);
+    } catch (IOException e) {
+      log.debug("IOException in metainfoparser during fromXmlStream: "
+          + e.getMessage());
+    } catch (SAXException e) {
+      log.debug("SAXException in metainfoparser during fromXmlStream: "
+          + e.getMessage());
+    } finally {
+      if (metainfoStream != null) {
+        metainfoStream.close();
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Compose the schema for the metainfo
+   *
+   * @param Digester - The Digester object we passed in to compose the schema
+   *
+   * @return
+   *
+   * @throws IOException
+   */
+  abstract protected void composeSchema(Digester digester);
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoSchema.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoSchema.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoSchema.java
new file mode 100644
index 0000000..cfa2895
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractMetainfoSchema.java
@@ -0,0 +1,69 @@
+/*
+ * 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.application.metadata;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Application type defined in the metainfo
+ */
+public abstract class AbstractMetainfoSchema implements Validate {
+  protected String name;
+  protected String comment;
+  protected String version;
+  protected List<ConfigFile> configFiles = new ArrayList<>();
+
+  public AbstractMetainfoSchema() {
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getComment() {
+    return comment;
+  }
+
+  public void setComment(String comment) {
+    this.comment = comment;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  public void addConfigFile(ConfigFile configFile) {
+    this.configFiles.add(configFile);
+  }
+
+  @JsonProperty("configFiles")
+  public List<ConfigFile> getConfigFiles() {
+    return configFiles;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AddonPackageMetainfoParser.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AddonPackageMetainfoParser.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AddonPackageMetainfoParser.java
new file mode 100644
index 0000000..c75837f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AddonPackageMetainfoParser.java
@@ -0,0 +1,53 @@
+/*
+ * 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.application.metadata;
+
+import org.apache.commons.digester.Digester;
+
+/**
+ *
+ */
+public class AddonPackageMetainfoParser extends AbstractMetainfoParser {
+
+  protected void composeSchema(Digester digester) {
+    digester.addObjectCreate("metainfo", Metainfo.class);
+    digester.addBeanPropertySetter("metainfo/schemaVersion");
+
+    digester.addObjectCreate("*/applicationPackage", ApplicationPackage.class);
+    digester.addBeanPropertySetter("*/applicationPackage/name");
+    digester.addBeanPropertySetter("*/applicationPackage/comment");
+    digester.addBeanPropertySetter("*/applicationPackage/version");
+
+    digester.addObjectCreate("*/component", ComponentsInAddonPackage.class);
+    digester.addBeanPropertySetter("*/component/name");
+    digester.addSetNext("*/component", "addComponent");
+
+    digester.addObjectCreate("*/commandScript", CommandScript.class);
+    digester.addBeanPropertySetter("*/commandScript/script");
+    digester.addBeanPropertySetter("*/commandScript/scriptType");
+    digester.addBeanPropertySetter("*/commandScript/timeout");
+    digester.addSetNext("*/commandScript", "addCommandScript");
+
+    digester.addObjectCreate("*/configFile", ConfigFile.class);
+    digester.addBeanPropertySetter("*/configFile/type");
+    digester.addBeanPropertySetter("*/configFile/fileName");
+    digester.addBeanPropertySetter("*/configFile/dictionaryName");
+    digester.addSetNext("*/configFile", "addConfigFile");
+
+    digester.addSetRoot("*/applicationPackage", "setApplicationPackage");
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java
new file mode 100644
index 0000000..5556c7f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java
@@ -0,0 +1,193 @@
+/*
+ * 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.application.metadata;
+
+import org.apache.slider.common.tools.SliderUtils;
+import org.apache.slider.core.exceptions.BadCommandArgumentsException;
+import org.apache.slider.core.exceptions.SliderException;
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Application type defined in the metainfo
+ */
+public class Application extends AbstractMetainfoSchema {
+  String exportedConfigs;
+  List<ExportGroup> exportGroups = new ArrayList<>();
+  List<OSSpecific> osSpecifics = new ArrayList<>();
+  List<CommandOrder> commandOrders = new ArrayList<>();
+  List<Package> packages = new ArrayList<>();
+  private List<Component> components = new ArrayList<>();
+
+  public Application() {
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getComment() {
+    return comment;
+  }
+
+  public void setComment(String comment) {
+    this.comment = comment;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  public String getExportedConfigs() {
+    return exportedConfigs;
+  }
+
+  public void setExportedConfigs(String exportedConfigs) {
+    this.exportedConfigs = exportedConfigs;
+  }
+
+  public void addConfigFile(ConfigFile configFile) {
+    this.configFiles.add(configFile);
+  }
+
+  @JsonProperty("configFiles")
+  public List<ConfigFile> getConfigFiles() {
+    return configFiles;
+  }
+
+  public void addComponent(Component component) {
+    components.add(component);
+  }
+
+  @JsonProperty("components")
+  public List<Component> getComponents() {
+    return components;
+  }
+
+  public void addExportGroup(ExportGroup exportGroup) {
+    exportGroups.add(exportGroup);
+  }
+
+  @JsonProperty("exportGroups")
+  public List<ExportGroup> getExportGroups() {
+    return exportGroups;
+  }
+
+  public void addOSSpecific(OSSpecific osSpecific) {
+    osSpecifics.add(osSpecific);
+  }
+
+  @JsonIgnore
+  public List<OSSpecific> getOSSpecifics() {
+    return osSpecifics;
+  }
+
+  public void addCommandOrder(CommandOrder commandOrder) {
+    commandOrders.add(commandOrder);
+  }
+
+  @JsonProperty("commandOrders")
+  public List<CommandOrder> getCommandOrders() {
+    return commandOrders;
+  }
+
+  public void addPackage(Package pkg) {
+    packages.add(pkg);
+  }
+
+  @JsonProperty("packages")
+  public List<Package> getPackages() {
+    return packages;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("{");
+    sb.append(",\n\"name\": ").append(name);
+    sb.append(",\n\"comment\": ").append(comment);
+    sb.append(",\n\"version\" :").append(version);
+    sb.append(",\n\"components\" : {");
+    for (Component component : components) {
+      sb.append("\n").append(component.toString());
+    }
+    sb.append("\n},");
+    sb.append('}');
+    return sb.toString();
+  }
+
+  public void validate(String version) throws SliderException {
+    if(SliderUtils.isUnset(version)) {
+      throw new BadCommandArgumentsException("schema version cannot be null");
+    }
+
+    Metainfo.checkNonNull(getName(), "name", "application");
+
+    Metainfo.checkNonNull(getVersion(), "version", "application");
+
+    if(getComponents().size() == 0) {
+      throw new SliderException("application must contain at least one component");
+    }
+
+    if(version.equals(Metainfo.VERSION_TWO_ZERO)) {
+      if(getPackages().size() > 0) {
+        throw new SliderException("packages is not supported in version " + version);
+      }
+    }
+
+    if(version.equals(Metainfo.VERSION_TWO_ONE)) {
+      if(getOSSpecifics().size() > 0) {
+        throw new SliderException("osSpecifics is not supported in version " + version);
+      }
+    }
+
+    for(CommandOrder co : getCommandOrders()) {
+      co.validate(version);
+    }
+
+    for(Component comp : getComponents()) {
+      comp.validate(version);
+    }
+
+    for(ConfigFile cf : getConfigFiles()) {
+      cf.validate(version);
+    }
+
+    for(ExportGroup eg : getExportGroups()) {
+      eg.validate(version);
+    }
+
+    for(Package pkg : getPackages()) {
+      pkg.validate(version);
+    }
+
+    for(OSSpecific os : getOSSpecifics()) {
+      os.validate(version);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/ApplicationPackage.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/ApplicationPackage.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/ApplicationPackage.java
new file mode 100644
index 0000000..a94a213
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/ApplicationPackage.java
@@ -0,0 +1,69 @@
+/*
+ * 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.application.metadata;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.slider.core.exceptions.SliderException;
+
+public class ApplicationPackage extends AbstractMetainfoSchema{
+  private List<ComponentsInAddonPackage> components = new ArrayList<ComponentsInAddonPackage>();
+
+  public void addComponent(ComponentsInAddonPackage component) {
+    components.add(component);
+  }
+
+  // we must override getcomponent() as well. otherwise it is pointing to the
+  // overriden components of type List<Component>
+  public List<ComponentsInAddonPackage> getComponents(){
+    return this.components;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder("{");
+    sb.append("\n\"name\": ").append(name);
+    sb.append(",\n\"comment\": ").append(comment);
+    sb.append(",\n\"version\" :").append(version);
+    sb.append(",\n\"components\" : {");
+    for (ComponentsInAddonPackage component : components) {
+      sb.append("\n").append(component);
+    }
+    sb.append("\n},");
+    sb.append('}');
+    return sb.toString();
+  }
+
+  @Override
+  public void validate(String version) throws SliderException {
+    if (name == null || name.isEmpty()) {
+      throw new SliderException(
+          "Missing name in metainfo.json for add on packages");
+    }
+    if (components.isEmpty()) {
+      throw new SliderException(
+          "Missing components in metainfo.json for add on packages");
+    }
+    for (ComponentsInAddonPackage component : components) {
+      if (component.name == null || component.name.isEmpty()) {
+        throw new SliderException(
+            "Missing name of components in metainfo.json for add on packages");
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandOrder.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandOrder.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandOrder.java
new file mode 100644
index 0000000..40d8cc6
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandOrder.java
@@ -0,0 +1,61 @@
+/*
+ * 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.application.metadata;
+
+import org.apache.slider.core.exceptions.SliderException;
+
+/**
+ *
+ */
+public class CommandOrder implements Validate {
+  String command;
+  String requires;
+
+  public CommandOrder() {
+  }
+
+  public String getCommand() {
+    return command;
+  }
+
+  public void setCommand(String command) {
+    this.command = command;
+  }
+
+  public String getRequires() {
+    return requires;
+  }
+
+  public void setRequires(String requires) {
+    this.requires = requires;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("{");
+    sb.append(",\n\"command\": ").append(command);
+    sb.append(",\n\"requires\": ").append(requires);
+    sb.append('}');
+    return sb.toString();
+  }
+
+  public void validate(String version) throws SliderException {
+    Metainfo.checkNonNull(getCommand(), "command", "package");
+    Metainfo.checkNonNull(getRequires(), "requires", "package");
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/d8cab88d/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java
new file mode 100644
index 0000000..9915ba1
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/CommandScript.java
@@ -0,0 +1,72 @@
+/*
+ * 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.application.metadata;
+
+import org.apache.slider.core.exceptions.SliderException;
+
+/**
+ * CommandScript that implements all component commands
+ */
+public class CommandScript implements Validate {
+  String script;
+  String scriptType;
+  long timeout;
+
+  public CommandScript() {
+
+  }
+
+  public String getScript() {
+    return script;
+  }
+
+  public void setScript(String script) {
+    this.script = script;
+  }
+
+  public String getScriptType() {
+    return scriptType;
+  }
+
+  public void setScriptType(String scriptType) {
+    this.scriptType = scriptType;
+  }
+
+  public long getTimeout() {
+    return timeout;
+  }
+
+  public void setTimeout(long timeout) {
+    this.timeout = timeout;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("{");
+    sb.append(",\n\"script\": ").append(script);
+    sb.append(",\n\"scriptType\": ").append(scriptType);
+    sb.append(",\n\"timeout\" :").append(timeout);
+    sb.append('}');
+    return sb.toString();
+  }
+
+  public void validate(String version) throws SliderException {
+    Metainfo.checkNonNull(getScript(), "script", "commandScript");
+    Metainfo.checkNonNull(getScriptType(), "scriptType", "commandScript");
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org