AMBARI-8542. Provide a way to parse and handle Kerberos descriptors

Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e7b8383b
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e7b8383b
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e7b8383b

Branch: refs/heads/trunk
Commit: e7b8383b5af0a39b2530dbfc24563eefd7f781c7
Parents: 1a18dde
Author: Robert Levas <[email protected]>
Authored: Mon Dec 8 17:44:37 2014 -0500
Committer: John Speidel <[email protected]>
Committed: Mon Dec 8 17:45:52 2014 -0500

----------------------------------------------------------------------
 .../kerberos/AbstractKerberosDescriptor.java    | 331 ++++++++++
 .../AbstractKerberosDescriptorContainer.java    | 626 +++++++++++++++++++
 .../kerberos/KerberosComponentDescriptor.java   | 115 ++++
 .../KerberosConfigurationDescriptor.java        | 205 ++++++
 .../state/kerberos/KerberosDescriptor.java      | 351 +++++++++++
 .../state/kerberos/KerberosDescriptorType.java  |  54 ++
 .../kerberos/KerberosIdentityDescriptor.java    | 278 ++++++++
 .../kerberos/KerberosKeytabDescriptor.java      | 445 +++++++++++++
 .../kerberos/KerberosPrincipalDescriptor.java   | 212 +++++++
 .../kerberos/KerberosServiceDescriptor.java     | 318 ++++++++++
 .../KerberosComponentDescriptorTest.java        | 206 ++++++
 .../KerberosConfigurationDescriptorTest.java    | 242 +++++++
 .../state/kerberos/KerberosDescriptorTest.java  | 328 ++++++++++
 .../KerberosIdentityDescriptorTest.java         | 154 +++++
 .../kerberos/KerberosKeytabDescriptorTest.java  | 146 +++++
 .../KerberosPrincipalDescriptorTest.java        | 109 ++++
 .../kerberos/KerberosServiceDescriptorTest.java | 238 +++++++
 17 files changed, 4358 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java
new file mode 100644
index 0000000..b18e412
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptor.java
@@ -0,0 +1,331 @@
+/**
+ * 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.ambari.server.state.kerberos;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.apache.ambari.server.AmbariException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * AbstractKerberosDescriptor is the base class for all Kerberos*Descriptor 
and associated classes.
+ * <p/>
+ * It provides storage and management for the parent and name values on behalf 
of implementing classes.
+ * It also provides utility and helper methods.
+ */
+public abstract class AbstractKerberosDescriptor {
+
+  /**
+   * a regular expression Pattern used to find "variable" placeholders in 
strings
+   */
+  private static final Pattern PATTERN_VARIABLE = 
Pattern.compile("\\$\\{(?:(.+?)/)?(.+?)\\}");
+
+  /**
+   * An AbstractKerberosDescriptor serving as the parent (or container) for 
this
+   * AbstractKerberosDescriptor.
+   * <p/>
+   * This value may be null in the event no parent has been identified.
+   */
+  private AbstractKerberosDescriptor parent = null;
+
+  /**
+   * A String declaring the name of this AbstractKerberosDescriptor.
+   * <p/>
+   * This value may be null in the event a name (or identifier) is not 
relevant.
+   */
+  private String name = null;
+
+  /**
+   * Performs variable replacement on the supplied String value using values 
from the replacementsMap.
+   * <p/>
+   * The value is a String containing one or more "variables" in the form of 
${variable_name}, such
+   * that "variable_name" may indicate a group identifier; else "" is used as 
the group.
+   * For example:
+   * <p/>
+   * variable_name:  group: ""; property: "variable_name"
+   * group1/variable_name:  group: "group1"; property: "variable_name"
+   * root/group1/variable_name:  Not Supported
+   * <p/>
+   * The replacementsMap is a Map of Maps creating a (small) hierarchy of data 
to traverse in order
+   * to resolve the variable.
+   * <p/>
+   * If a variable resolves to one or more variables, that new variable(s) 
will be processed and replaced.
+   * If variable exists after a set number of iterations it is assumed that a 
cycle has been created
+   * and the process will abort returning a String in a possibly unexpected 
state.
+   *
+   * @param value           a String containing zero or more variables to be 
replaced
+   * @param replacementsMap a Map of data used to perform the variable 
replacements
+   * @return a new String
+   */
+  public static String replaceVariables(String value, Map<String, Map<String, 
String>> replacementsMap) throws AmbariException {
+    if ((value != null) && (replacementsMap != null) && 
!replacementsMap.isEmpty()) {
+      int count = 0; // Used to help prevent an infinite loop...
+      boolean replacementPerformed;
+
+      do {
+        if (++count > 1000) {
+          throw new AmbariException(String.format("Circular reference found 
while replacing variables in %s", value));
+        }
+
+        Matcher matcher = PATTERN_VARIABLE.matcher(value);
+        StringBuffer sb = new StringBuffer();
+
+        replacementPerformed = false;
+
+        while (matcher.find()) {
+          String type = matcher.group(1);
+          String name = matcher.group(2);
+          Map<String, String> replacements;
+
+          if ((name != null) && !name.isEmpty()) {
+            if (type == null) {
+              replacements = replacementsMap.get("");
+            } else {
+              replacements = replacementsMap.get(type);
+            }
+
+            if (replacements != null) {
+              String replacement = replacements.get(name);
+
+              if (replacement != null) {
+                // Escape '$' and '\' so they don't cause any issues.
+                matcher.appendReplacement(sb, replacement.replace("\\", 
"\\\\").replace("$", "\\$"));
+                replacementPerformed = true;
+              }
+            }
+          }
+        }
+
+        matcher.appendTail(sb);
+        value = sb.toString();
+      }
+      while (replacementPerformed); // Process the string again to make sure 
new variables were not introduced
+    }
+
+    return value;
+  }
+
+  /**
+   * Generates a Map of data that represents this AbstractKerberosDescriptor 
implementation.
+   * <p/>
+   * This method should be overwritten by AbstractKerberosDescriptor 
implementations to generate a
+   * Map of data specific to it.
+   * <p/>
+   * It is not necessary to call this method from the overriding method.
+   * <p/>
+   * The map of data generated should be setup so that it can be fed back into 
the implementation
+   * class to build a copy of it. For example:
+   * <p/>
+   * <pre>
+   *  descriptor1 = AbstractKerberosDescriptorImpl(...)
+   *  map = descriptor1.toMap()
+   *  descriptor2 = AbstractKerberosDescriptor(map)
+   *  descriptor1 should have the same data as descriptor2
+   * </pre>
+   *
+   * @return a Map of date representing this AbstractKerberosDescriptor 
implementation
+   */
+  public Map<String, Object> toMap() {
+    HashMap<String, Object> dataMap = new HashMap<String, Object>();
+    String name = getName();
+
+    if (name != null) {
+      dataMap.put("name", name);
+    }
+
+    return dataMap;
+  }
+
+  /**
+   * Returns the name of this descriptor
+   *
+   * @return a String indicating the name of this descriptor
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Sets the name of this descriptor
+   *
+   * @param name a String indicating the name of this descriptor
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * Returns the parent (or container) of this descriptor
+   *
+   * @return an AbstractKerberosDescriptor representing the parent (or 
container) of this descriptor
+   * or null if no parent was set
+   */
+  public AbstractKerberosDescriptor getParent() {
+    return parent;
+  }
+
+  /**
+   * Sets the parent (or container) of this descriptor
+   *
+   * @param parent an AbstractKerberosDescriptor representing the parent (or 
container) of this
+   *               descriptor or null to clear the value
+   */
+  public void setParent(AbstractKerberosDescriptor parent) {
+    this.parent = parent;
+  }
+
+  /**
+   * Test this AbstractKerberosDescriptor to see if it is a container.
+   * <p/>
+   * The default implementation always returns false.  Implementing classes 
should override this
+   * method to return something different, like true.
+   *
+   * @return true if this AbstractKerberosDescriptor is a container, false 
otherwise
+   */
+  public boolean isContainer() {
+    return false;
+  }
+
+  /**
+   * Parses a file containing JSON-formatted text into a (generic) Map.
+   *
+   * @param file a File containing the JSON-formatted text to parse
+   * @return a Map of the data
+   * @throws FileNotFoundException if the specified File does not point to a 
valid file
+   * @throws IOException           if the specified File is not a readable file
+   */
+  protected static Map<String, Object> parseFile(File file) throws IOException 
{
+    if (file == null) {
+      return Collections.emptyMap();
+    } else if (!file.isFile() || !file.canRead()) {
+      throw new IOException(String.format("%s is not a readable file", 
file.getAbsolutePath()));
+    } else {
+      return new Gson().fromJson(new FileReader(file),
+          new TypeToken<Map<String, Object>>() {
+          }.getType());
+    }
+  }
+
+  /**
+   * Parses a JSON-formatted String into a (generic) Map.
+   *
+   * @param json a String containing the JSON-formatted text to parse
+   * @return a Map of the data
+   */
+  protected static Map<String, Object> parseJSON(String json) {
+    if ((json == null) || json.isEmpty()) {
+      return Collections.emptyMap();
+    } else {
+      return new Gson().fromJson(json,
+          new TypeToken<Map<String, Object>>() {
+          }.getType());
+    }
+  }
+
+  /**
+   * Safely retrieves the requested value from the supplied Map
+   *
+   * @param map a Map containing the relevant data
+   * @param key a String declaring the item to retrieve
+   * @return an Object representing the requested data; or null if not found
+   */
+  protected static Object getValue(Map<?, ?> map, String key) {
+    return ((map == null) || key == null) ? null : map.get(key);
+  }
+
+  /**
+   * Safely retrieves the requested value (converted to a String) from the 
supplied Map
+   * <p/>
+   * The found value will be converted to a String using the {@link 
Object#toString()} method.
+   *
+   * @param map a Map containing the relevant data
+   * @param key a String declaring the item to retrieve
+   * @return a String representing the requested data; or null if not found
+   */
+  protected static String getStringValue(Map<?, ?> map, String key) {
+    Object value = getValue(map, key);
+    return (value == null) ? null : value.toString();
+  }
+
+  /**
+   * Gets the requested AbstractKerberosDescriptor implementation using a type 
name and a relevant
+   * descriptor name.
+   * <p/>
+   * Implementation classes should override this method to handle relevant 
descriptor types.
+   *
+   * @param type a String indicating the type of the requested descriptor
+   * @param name a String indicating the name of the requested descriptor
+   * @return a AbstractKerberosDescriptor representing the requested 
descriptor or null if not found
+   */
+  protected AbstractKerberosDescriptor getDescriptor(KerberosDescriptorType 
type, String name) {
+    return null;
+  }
+
+  /**
+   * Traverses up the hierarchy to find the "root" or "parent" container.
+   * <p/>
+   * The root AbstractKerberosDescriptor is the first descriptor in the 
hierarchy with a null parent.
+   *
+   * @return the AbstractKerberosDescriptor implementation that is found to be 
the root of the hierarchy.
+   */
+  protected AbstractKerberosDescriptor getRoot() {
+    AbstractKerberosDescriptor root = this;
+
+    while (root.getParent() != null) {
+      root = root.getParent();
+    }
+
+    return root;
+  }
+
+  @Override
+  public int hashCode() {
+    return 37 *
+        ((getName() == null)
+            ? 0
+            : getName().hashCode());
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (object == null) {
+      return false;
+    } else if (object == this) {
+      return true;
+    } else if (object instanceof AbstractKerberosDescriptor) {
+      AbstractKerberosDescriptor descriptor = (AbstractKerberosDescriptor) 
object;
+      return (
+          (getName() == null)
+              ? (descriptor.getName() == null)
+              : getName().equals(descriptor.getName())
+      );
+    } else {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
new file mode 100644
index 0000000..c6389cb
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
@@ -0,0 +1,626 @@
+/*
+ * 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.ambari.server.state.kerberos;
+
+import org.apache.ambari.server.AmbariException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AbstractKerberosDescriptorContainer is an abstract class implementing 
AbstractKerberosDescriptor
+ * and providing facility to handle common descriptor container functionality.
+ * <p/>
+ * Each AbstractKerberosDescriptorContainer contains identities and 
configurations as well as a
+ * name and a parent (which is inherited from AbstractKerberosDescriptor).
+ * <p/>
+ * An AbstractKerberosDescriptorContainer has the following properties:
+ * <ul>
+ * <li>identities</li>
+ * <li>configurations</li>
+ * </ul>
+ * <p/>
+ * The following (pseudo) JSON Schema will yield a valid 
AbstractKerberosDescriptorContainer
+ * <pre>
+ *   {
+ *      "$schema": "http://json-schema.org/draft-04/schema#";,
+ *      "title": "AbstractKerberosDescriptorContainer",
+ *      "description": "Describes an AbstractKerberosDescriptorContainer",
+ *      "type": "object",
+ *      "properties": {
+ *        "identities": {
+ *          "description": "A list of Kerberos identity descriptors",
+ *          "type": "array",
+ *          "items": {
+ *            "title": "KerberosIdentityDescriptor"
+ *            "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor}"
+ *          }
+ *        },
+ *        "configurations": {
+ *          "description": "A list of relevant configuration blocks",
+ *          "type": "array",
+ *          "items": {
+ *            "title": "KerberosConfigurationDescriptor"
+ *            "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor}"
+ *          }
+ *        }
+ *      }
+ *   }
+ * </pre>
+ * <p/>
+ * This implementation does not set the
+ * {@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} value, 
it is
+ * left up to the implementing class to do so.
+ */
+public abstract class AbstractKerberosDescriptorContainer extends 
AbstractKerberosDescriptor {
+
+  /**
+   * A List of KerberosIdentityDescriptors contained in this 
AbstractKerberosDescriptorContainer
+   */
+  private List<KerberosIdentityDescriptor> identities = null;
+
+  /**
+   * A Map of KerberosConfigurationDescriptors contained in this 
AbstractKerberosDescriptorContainer
+   */
+  private Map<String, KerberosConfigurationDescriptor> configurations = null;
+
+  /**
+   * Constructs a new AbstractKerberosDescriptorContainer
+   * <p/>
+   * This constructor must be called from the constructor(s) of the 
implementing classes
+   *
+   * @param data a Map of data used for collecting groups of common descriptors
+   */
+  protected AbstractKerberosDescriptorContainer(Map<?, ?> data) {
+    if (data != null) {
+      Object list;
+
+      // (Safely) Get the set of KerberosIdentityDescriptors
+      list = 
data.get(KerberosDescriptorType.IDENTITY.getDescriptorPluralName());
+      if (list instanceof Collection) {
+        for (Object item : (Collection) list) {
+          if (item instanceof Map) {
+            putIdentity(new KerberosIdentityDescriptor((Map<?, ?>) item));
+          }
+        }
+      }
+
+      // (Safely) Get the set of KerberosConfigurationDescriptors
+      list = 
data.get(KerberosDescriptorType.CONFIGURATION.getDescriptorPluralName());
+      if (list instanceof Collection) {
+        for (Object item : (Collection) list) {
+          if (item instanceof Map) {
+            putConfiguration(new KerberosConfigurationDescriptor((Map<?, ?>) 
item));
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Returns the raw List of KerberosIdentityDescriptors contained within this
+   * AbstractKerberosDescriptorContainer.
+   * <p/>
+   * The returned KerberosIdentityDescriptors are not merged with data from 
referenced
+   * KerberosConfigurationDescriptors. This is the same calling
+   * {@link AbstractKerberosDescriptorContainer#getIdentities(boolean)} and 
setting the argument to
+   * 'false'
+   *
+   * @return the relevant List of KerberosIdentityDescriptors
+   */
+  public List<KerberosIdentityDescriptor> getIdentities() {
+    try {
+      return getIdentities(false);
+    } catch (AmbariException e) {
+      // AmbariException will not be thrown unless an error occurs while 
trying to dereference
+      // identities.  This method does not attempt to dereference identities.
+      return null;
+    }
+  }
+
+  /**
+   * Returns a List of KerberosIdentityDescriptors contained within this
+   * AbstractKerberosDescriptorContainer.
+   * <p/>
+   * If resolveReferences is true, a "detached" set of 
KerberosIdentityDescriptors are returned.
+   * Any KerberosIdentityDescriptor that implies it references some other 
KerberosIdentityDescriptor
+   * in the hierarchy will be resolved. Meaning, if the name of the 
KerberosIdentityDescriptor
+   * indicates a path to some other KerberosIdentityDescriptor (i.e, /spnego, 
/HDFS/NAMENODE/nn, etc...)
+   * the referenced KerberosIdentityDescriptor is found, detached (or copied), 
and updated with
+   * the information from the initial KerberosIdentityDescriptor.  Because of 
this, all of the
+   * KerberosIdentityDescriptors to be included are copied into the resulting 
list, and dissociating
+   * them with the rest of the hierarchy such that changes to them will not be 
reflected within the
+   * entire KerberosDescriptor tree.
+   * <p/>
+   * If resolveReferences is false, the raw List of 
KerberosIdentityDescriptors are returned. This
+   * data is not manipulated by resolving references and therefore it may be 
missing data, however
+   * this List is of "attached" descriptors, so changes will be reflected 
within the KerberosDescriptor
+   * hierarchy.
+   *
+   * @param resolveReferences a Boolean value indicating whether to resolve 
references (true) or not
+   *                          (false)
+   * @return a List of the requested KerberosIdentityDescriptors
+   */
+  public List<KerberosIdentityDescriptor> getIdentities(boolean 
resolveReferences) throws AmbariException {
+    if (resolveReferences) {
+      if (identities == null) {
+        return Collections.emptyList();
+      } else {
+        List<KerberosIdentityDescriptor> list = new 
ArrayList<KerberosIdentityDescriptor>();
+
+        // For each KerberosIdentityDescriptor, copy it and then attempt to 
find the referenced
+        // KerberosIdentityDescriptor.
+        // * If a reference is found, copy that, update it with the initial 
KerberosIdentityDescriptor
+        //   and then add it to the list.
+        // * If a reference is not found, simply add the initial 
KerberosIdentityDescriptor to the list
+        for (KerberosIdentityDescriptor identity : identities) {
+          KerberosIdentityDescriptor referencedIdentity;
+          try {
+            referencedIdentity = 
getReferencedIdentityDescriptor(identity.getName());
+          } catch (AmbariException e) {
+            throw new AmbariException(String.format("Invalid Kerberos identity 
reference: %s", identity.getName()), e);
+          }
+
+          // Detach this identity from the tree...
+          identity = new KerberosIdentityDescriptor(identity.toMap());
+
+          if (referencedIdentity != null) {
+            KerberosIdentityDescriptor detachedIdentity = new 
KerberosIdentityDescriptor(referencedIdentity.toMap());
+            detachedIdentity.update(identity);
+            list.add(detachedIdentity);
+          } else {
+            list.add(identity);
+          }
+        }
+
+        return list;
+      }
+    } else {
+      return identities;
+    }
+  }
+
+  /**
+   * Return a KerberosIdentityDescriptor with the specified name.
+   *
+   * @param name a String declaring the name of the descriptor to retrieve
+   * @return the requested KerberosIdentityDescriptor
+   */
+  public KerberosIdentityDescriptor getIdentity(String name) {
+    KerberosIdentityDescriptor identity = null;
+
+    if ((name != null) && (identities != null)) {
+      // Iterate over the List of KerberosIdentityDescriptors to find one with 
the requested name
+      // If one is found, break out of the loop.
+      for (KerberosIdentityDescriptor descriptor : identities) {
+        if (name.equals(descriptor.getName())) {
+          identity = descriptor;
+          break;
+        }
+      }
+    }
+
+    return identity;
+  }
+
+  /**
+   * Adds the specified KerberosIdentityDescriptor to the list of 
KerberosIdentityDescriptor.
+   * <p/>
+   * This method attempts to ensure that the names or 
KerberosIdentityDescriptors are unique within
+   * the List.
+   *
+   * @param identity the KerberosIdentityDescriptor to add
+   */
+  public void putIdentity(KerberosIdentityDescriptor identity) {
+    if (identity != null) {
+      String name = identity.getName();
+
+      if (identities == null) {
+        identities = new ArrayList<KerberosIdentityDescriptor>();
+      }
+
+      // If the identity has a name, ensure that one one with that name is in 
the List
+      // Note: this cannot be enforced since any AbstractKerberosDescriptor+'s 
name property can be
+      // changed
+      if ((name != null) && !name.isEmpty()) {
+        removeIdentity(identity.getName());
+      }
+
+      identities.add(identity);
+
+      // Set the identity's parent to this AbstractKerberosDescriptorContainer
+      identity.setParent(this);
+    }
+  }
+
+  /**
+   * Remove all KerberosIdentityDescriptors have have the specified name
+   * <p/>
+   * One or more KerberosIdentityDescriptors may be removed if multiple 
KerberosIdentityDescriptors
+   * have the same name.
+   *
+   * @param name a String declaring the name of the descriptors to remove
+   */
+  public void removeIdentity(String name) {
+    if ((name != null) && (identities != null)) {
+      Iterator<KerberosIdentityDescriptor> iterator = identities.iterator();
+
+      while (iterator.hasNext()) {
+        KerberosIdentityDescriptor identity = iterator.next();
+        if (name.equals(identity.getName())) {
+          identity.setParent(null);
+          iterator.remove();
+        }
+      }
+    }
+  }
+
+  /**
+   * Returns a Map of raw KerberosConfigurationDescriptors contained within 
this
+   * AbstractKerberosDescriptorContainer.
+   * <p/>
+   * The returned KerberosConfigurationDescriptors are not merged with data 
from KerberosDescriptor
+   * hierarchy. This is the same calling
+   * {@link AbstractKerberosDescriptorContainer#getConfigurations(boolean)} 
and setting the argument
+   * to 'false'
+   *
+   * @return a List of KerberosConfigurationDescriptors
+   */
+  public Map<String, KerberosConfigurationDescriptor> getConfigurations() {
+    return getConfigurations(false);
+  }
+
+  /**
+   * Returns a Map of KerberosConfigurationDescriptors contained within this
+   * AbstractKerberosDescriptorContainer.
+   * <p/>
+   * If includeInherited is true, the Map will contain "detached" 
KerberosConfigurationDescriptor
+   * instances, but the data will be merged with all relevant 
KerberosConfigurationDescriptors within
+   * the hierarchy.  Data higher in the hierarchy (the service level is higher 
in the hierarchy than
+   * the component level) will be overwritten by data lower in the hierarchy.
+   * <p/>
+   * If includeInherited is false, the Map will consist of attached 
(non-merged)
+   * KerberosConfigurationDescriptors. This data is not manipulated by merging 
with data within the
+   * KerberosDescriptor hierarchy and therefore it may be missing data, 
however this Map is of
+   * "attached" descriptors, so changes will be reflected within the 
KerberosDescriptor
+   * hierarchy.
+   *
+   * @param includeInherited a Boolean value indicating whether to include 
configuration within the
+   *                         KerberosDescriptor hierarchy (true) or not (false)
+   * @return a Map of Strings to KerberosConfigurationDescriptors, where the 
key is the type
+   * (core-site, etc...)
+   */
+  public Map<String, KerberosConfigurationDescriptor> 
getConfigurations(boolean includeInherited) {
+    if (includeInherited) {
+      Map<String, KerberosConfigurationDescriptor> mergedConfigurations = new 
HashMap<String, KerberosConfigurationDescriptor>();
+      List<Map<String, KerberosConfigurationDescriptor>> configurationSets = 
new ArrayList<Map<String, KerberosConfigurationDescriptor>>();
+      AbstractKerberosDescriptor currentDescriptor = this;
+
+      // Walk up the hierarchy and collect the configuration sets.
+      while (currentDescriptor != null) {
+        if (currentDescriptor.isContainer()) {
+          Map<String, KerberosConfigurationDescriptor> configurations = 
((AbstractKerberosDescriptorContainer) currentDescriptor).getConfigurations();
+
+          if (configurations != null) {
+            configurationSets.add(configurations);
+          }
+        }
+
+        currentDescriptor = currentDescriptor.getParent();
+      }
+
+      // Reverse the collection so that we can merge from top to bottom
+      Collections.reverse(configurationSets);
+
+      for (Map<String, KerberosConfigurationDescriptor> map : 
configurationSets) {
+        for (Map.Entry<String, KerberosConfigurationDescriptor> entry : 
map.entrySet()) {
+          // For each configuration type, copy it and determine if an entry 
exists or not.
+          // ** If one exists, merge the current data into the existing data 
(potentially
+          //    overwriting values).
+          // ** If one does not exist, simply add a copy of the current one to 
the Map
+          String currentType = entry.getKey();
+          KerberosConfigurationDescriptor currentConfiguration = 
entry.getValue();
+
+          if (currentConfiguration != null) {
+            KerberosConfigurationDescriptor detachedConfiguration = new 
KerberosConfigurationDescriptor(currentConfiguration.toMap());
+            KerberosConfigurationDescriptor mergedConfiguration = 
mergedConfigurations.get(entry.getKey());
+
+            if (mergedConfiguration == null) {
+              mergedConfigurations.put(currentType, detachedConfiguration);
+            } else {
+              mergedConfiguration.update(detachedConfiguration);
+            }
+          }
+        }
+      }
+
+      return mergedConfigurations;
+    } else {
+      return configurations;
+    }
+  }
+
+  /**
+   * Adds the specified KerberosConfigurationDescriptor to the list of 
KerberosConfigurationDescriptors.
+   * <p/>
+   * If an entry exists of the same configuration type, it will be overwritten.
+   *
+   * @param configuration the KerberosConfigurationDescriptor to add
+   */
+  public void putConfiguration(KerberosConfigurationDescriptor configuration) {
+    if (configuration != null) {
+      String type = configuration.getType();
+
+      if (type == null) {
+        throw new IllegalArgumentException("The configuration type must not be 
null");
+      }
+
+      if (configurations == null) {
+        configurations = new HashMap<String, 
KerberosConfigurationDescriptor>();
+      }
+
+      configurations.put(type, configuration);
+
+      // Set the configuration's parent to this 
AbstractKerberosDescriptorContainer
+      configuration.setParent(this);
+    }
+  }
+
+  /**
+   * Returns the requested KerberosConfigurationDescriptor
+   *
+   * @param name a String declaring the name of the descriptor to retrieve
+   * @return the requested KerberosConfigurationDescriptor or null if not found
+   */
+  public KerberosConfigurationDescriptor getConfiguration(String name) {
+    return ((name == null) || (configurations == null)) ? null : 
configurations.get(name);
+  }
+
+  /**
+   * Test this AbstractKerberosDescriptor to see if it is a container.
+   * <p/>
+   * This implementation always returns true since it implements a descriptor 
container.
+   *
+   * @return true if this AbstractKerberosDescriptor is a container, false 
otherwise
+   */
+  public boolean isContainer() {
+    return true;
+  }
+
+  /**
+   * Updates this AbstractKerberosDescriptorContainer using information from 
the supplied
+   * AbstractKerberosDescriptorContainer.
+   * <p/>
+   * Information from updates will overwrite information in this 
AbstractKerberosDescriptorContainer.
+   * More specifically, the name of this AbstractKerberosDescriptorContainer 
may be updated as well
+   * as each individual KerberosIdentityDescriptor and 
KerberosConfigurationDescriptor contained
+   * within it.  Any new KerberosIdentityDescriptors and 
KerberosConfigurationDescriptors will be
+   * appended to there appropriate lists.
+   *
+   * @param updates an AbstractKerberosDescriptorContainer containing the 
updates to this
+   *                AbstractKerberosDescriptorContainer
+   */
+  public void update(AbstractKerberosDescriptorContainer updates) {
+    if (updates != null) {
+      String updatedName = updates.getName();
+      if (updatedName != null) {
+        setName(updatedName);
+      }
+
+      Map<String, KerberosConfigurationDescriptor> updatedConfigurations = 
updates.getConfigurations();
+      if (updatedConfigurations != null) {
+        for (Map.Entry<String, KerberosConfigurationDescriptor> entry : 
updatedConfigurations.entrySet()) {
+          KerberosConfigurationDescriptor existingConfiguration = 
getConfiguration(entry.getKey());
+
+          // Copy this descriptor so we don't alter the hierarchy of existing 
data we don't intend to change
+          KerberosConfigurationDescriptor clone = new 
KerberosConfigurationDescriptor(entry.getValue().toMap());
+
+          if (existingConfiguration == null) {
+            putConfiguration(clone);
+          } else {
+            existingConfiguration.update(clone);
+          }
+        }
+      }
+
+      List<KerberosIdentityDescriptor> updatedIdentities = 
updates.getIdentities();
+      if (updatedIdentities != null) {
+        for (KerberosIdentityDescriptor updatedIdentity : updatedIdentities) {
+          KerberosIdentityDescriptor existing = 
getIdentity(updatedIdentity.getName());
+
+          // Copy this descriptor so we don't alter the hierarchy of existing 
data we don't intend to change
+          KerberosIdentityDescriptor clone = new 
KerberosIdentityDescriptor(updatedIdentity.toMap());
+
+          if (existing == null) {
+            putIdentity(clone);
+          } else {
+            existing.update(clone);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Attempts to find the KerberosIdentityDescriptor at the specified path.
+   * <p/>
+   * The path value is expected to be an "absolute" path through the Kerberos 
Descriptor hierarchy
+   * to some specific KerberosIdentityDescriptor.  The path must be in one of 
the following forms:
+   * <p/>
+   * /identity
+   * /service/identity
+   * /service/component/identity
+   *
+   * @param path a String declaring the path to a KerberosIdentityDescriptor
+   * @return a KerberosIdentityDescriptor identified by the path or null if 
not found
+   */
+  protected KerberosIdentityDescriptor getReferencedIdentityDescriptor(String 
path)
+      throws AmbariException {
+    KerberosIdentityDescriptor identityDescriptor = null;
+
+    if ((path != null) && path.startsWith("/")) {
+      // The name indicates it is referencing an identity somewhere in the 
hierarchy... try to find it.
+      // /[<service name>/[<component name>/]]<identity name>
+      String[] pathParts = path.split("/");
+
+      String serviceName = null;
+      String componentName = null;
+      String identityName;
+
+      switch (pathParts.length) {
+        case 3:
+          serviceName = pathParts[0];
+          componentName = pathParts[1];
+          identityName = pathParts[2];
+          break;
+        case 2:
+          serviceName = pathParts[0];
+          identityName = pathParts[1];
+          break;
+        case 1:
+          identityName = pathParts[0];
+          break;
+        default:
+          throw new AmbariException(String.format("Unexpected path length in 
%s", path));
+      }
+
+      if (identityName != null) {
+        // Start at the top of the hierarchy
+        AbstractKerberosDescriptor descriptor = getRoot();
+
+        if (descriptor != null) {
+          if ((serviceName != null) && !serviceName.isEmpty()) {
+            descriptor = 
descriptor.getDescriptor(KerberosDescriptorType.SERVICE, serviceName);
+
+            if ((descriptor != null) && (componentName != null) && 
!componentName.isEmpty()) {
+              descriptor = 
descriptor.getDescriptor(KerberosDescriptorType.COMPONENT, componentName);
+            }
+          }
+
+          if (descriptor != null) {
+            descriptor = 
descriptor.getDescriptor(KerberosDescriptorType.IDENTITY, identityName);
+
+            if (descriptor instanceof KerberosIdentityDescriptor) {
+              identityDescriptor = (KerberosIdentityDescriptor) descriptor;
+            }
+          }
+        }
+      }
+    }
+
+    return identityDescriptor;
+
+  }
+
+  /**
+   * Gets the requested AbstractKerberosDescriptor implementation using a type 
name and a relevant
+   * descriptor name.
+   * <p/>
+   * This implementation handles identity and configuration descriptors within 
this
+   * AbstractKerberosDescriptorContainer.
+   * <p/>
+   * Implementing classes should override this to handle other types, but call 
this method to
+   * ensure no data is missed.
+   *
+   * @param type a String indicating the type of the requested descriptor
+   * @param name a String indicating the name of the requested descriptor
+   * @return a AbstractKerberosDescriptor representing the requested 
descriptor or null if not found
+   */
+  @Override
+  protected AbstractKerberosDescriptor getDescriptor(KerberosDescriptorType 
type, String name) {
+    if (KerberosDescriptorType.IDENTITY == type) {
+      return getIdentity(name);
+    } else if (KerberosDescriptorType.CONFIGURATION == type) {
+      return getConfiguration(name);
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Creates a Map of values that can be used to create a copy of this 
AbstractKerberosDescriptorContainer
+   * or generate the JSON structure described in
+   * {@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer}
+   *
+   * @return a Map of values for this AbstractKerberosDescriptorContainer
+   * @see 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer
+   */
+  @Override
+  public Map<String, Object> toMap() {
+    Map<String, Object> map = super.toMap();
+
+    if (identities != null) {
+      List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
+      for (KerberosIdentityDescriptor identity : identities) {
+        list.add(identity.toMap());
+      }
+      map.put(KerberosDescriptorType.IDENTITY.getDescriptorPluralName(), list);
+    }
+
+    if (configurations != null) {
+      List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
+      for (KerberosConfigurationDescriptor configuration : 
configurations.values()) {
+        list.add(configuration.toMap());
+      }
+      map.put(KerberosDescriptorType.CONFIGURATION.getDescriptorPluralName(), 
list);
+    }
+
+    return map;
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() +
+        ((getIdentities() == null)
+            ? 0
+            : getIdentities().hashCode()) +
+        ((getConfigurations() == null)
+            ? 0
+            : getConfigurations().hashCode());
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (object == null) {
+      return false;
+    } else if (object == this) {
+      return true;
+    } else if (object instanceof AbstractKerberosDescriptorContainer) {
+      AbstractKerberosDescriptorContainer descriptor = 
(AbstractKerberosDescriptorContainer) object;
+      return super.equals(object) &&
+          (
+              (getIdentities() == null)
+                  ? (descriptor.getIdentities() == null)
+                  : getIdentities().equals(descriptor.getIdentities())
+          ) &&
+          (
+              (getConfigurations() == null)
+                  ? (descriptor.getConfigurations() == null)
+                  : getConfigurations().equals(descriptor.getConfigurations())
+          );
+    } else {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java
new file mode 100644
index 0000000..19d3f5e
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java
@@ -0,0 +1,115 @@
+/**
+ * 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.ambari.server.state.kerberos;
+
+import java.util.Map;
+
+/**
+ * KerberosComponentDescriptor implements AbstractKerberosDescriptorContainer. 
It contains the data
+ * related to a component which include the following properties:
+ * <ul>
+ * <li>name</li>
+ * <li>identities</li>
+ * <li>configurations</li>
+ * </ul>
+ * Example:
+ * <pre>
+ *  {
+ *    "name" : "COMPONENT_NAME",
+ *    "identities" : { ... },
+ *    "configurations" : { ... }
+ *  }
+ *  </pre>
+ */
+
+/**
+ * KerberosComponentDescriptor is an implementation of an 
AbstractKerberosDescriptorContainer that
+ * encapsulates the details about an Ambari component.
+ * <p/>
+ * A KerberosComponentDescriptor has the following properties:
+ * <ul>
+ * <li>identities ({@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li>
+ * <li>configurations ({@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li>
+ * </ul>
+ * <p/>
+ * The following (pseudo) JSON Schema will yield a valid 
KerberosComponentDescriptor
+ * <pre>
+ *   {
+ *      "$schema": "http://json-schema.org/draft-04/schema#";,
+ *      "title": "KerberosComponentDescriptor",
+ *      "description": "Describes an Ambari component",
+ *      "type": "object",
+ *      "properties": {
+ *        "identities": {
+ *          "description": "A list of Kerberos identity descriptors",
+ *          "type": "array",
+ *          "items": {
+ *            "title": "KerberosIdentityDescriptor"
+ *            "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor}"
+ *          }
+ *        },
+ *        "configurations": {
+ *          "description": "A list of relevant configuration blocks",
+ *          "type": "array",
+ *          "items": {
+ *            "title": "KerberosConfigurationDescriptor"
+ *            "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor}"
+ *          }
+ *        }
+ *      }
+ *   }
+ * </pre>
+ * <p/>
+ * In this implementation,
+ * {@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will 
hold the
+ * KerberosComponentDescriptor#name value
+ */
+public class KerberosComponentDescriptor extends 
AbstractKerberosDescriptorContainer {
+
+  /**
+   * Creates a new KerberosComponentDescriptor
+   * <p/>
+   * See {@link 
org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor} for an
+   * example JSON structure that may be used to generate this map.
+   *
+   * @param data a Map of values use to populate the data for the new instance
+   * @see org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor
+   */
+  public KerberosComponentDescriptor(Map<?, ?> data) {
+    super(data);
+
+    // The name for this KerberosComponentDescriptor is stored in the "name" 
entry in the map
+    // This is not automatically set by the super classes.
+    setName(getStringValue(data, "name"));
+  }
+
+  @Override
+  public int hashCode() {
+    return 35 * super.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    return (object == this) ||
+        (
+            (object != null) &&
+                (object.getClass() == KerberosComponentDescriptor.class) &&
+                super.equals(object)
+        );
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java
new file mode 100644
index 0000000..1e33e68
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosConfigurationDescriptor.java
@@ -0,0 +1,205 @@
+/**
+ * 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.ambari.server.state.kerberos;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * KerberosConfigurationDescriptor is an implementation of an 
AbstractKerberosDescriptor that
+ * encapsulates data related to an Ambari configuration block.
+ * <p/>
+ * A KerberosConfigurationDescriptor has the following properties:
+ * <ul>
+ * <li>type</li>
+ * <li>properties</li>
+ * </ul>
+ * <p/>
+ * The following is an example of a JSON structure that will yield a valid 
KerberosConfigurationDescriptor
+ * <pre>
+ *   {
+ *    "core-site": {
+ *      "hadoop.security.authentication": "kerberos",
+ *      "hadoop.rpc.protection": "authentication; integrity; privacy",
+ *      "hadoop.security.authorization": "true"
+ *    }
+ *   }
+ * </pre>
+ * <p/>
+ * In this implementation,
+ * {@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will 
hold the
+ * KerberosConfigurationDescriptor#type value
+ */
+public class KerberosConfigurationDescriptor extends 
AbstractKerberosDescriptor {
+  /**
+   * A Map of the properties in this KerberosConfigurationDescriptor
+   */
+  private Map<String, String> properties = null;
+
+  /**
+   * Creates a new KerberosConfigurationDescriptor
+   * <p/>
+   * See {@link 
org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor} for an
+   * example JSON structure that may be used to generate this map.
+   *
+   * @param data a Map of values use to populate the data for the new instance
+   * @see 
org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor
+   */
+  public KerberosConfigurationDescriptor(Map<?, ?> data) {
+
+    if (data != null) {
+      Set<?> keySet = data.keySet();
+
+      // Only a single entry is expected...
+      Object key = keySet.iterator().next();
+      if (key != null) {
+        Object object = data.get(key);
+
+        setType(key.toString());
+
+        if (object instanceof Map) {
+          for (Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
+            Object value = entry.getValue();
+            putProperty(entry.getKey().toString(), (value == null) ? null : 
value.toString());
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Returns the type of the configuration data represented by this 
KerberosConfigurationDescriptor
+   *
+   * @return a String declaring the configuration type, i.e., core-site
+   */
+  public String getType() {
+    return getName();
+  }
+
+  /**
+   * Sets the type of the configuration data represented by this 
KerberosConfigurationDescriptor
+   *
+   * @param type a String declaring the configuration group type, i.e., 
core-site
+   */
+  public void setType(String type) {
+    setName(type);
+  }
+
+  /**
+   * Gets the properties of the configuration data represented by this 
KerberosConfigurationDescriptor
+   *
+   * @return a Map of properties
+   */
+  public Map<String, String> getProperties() {
+    return properties;
+  }
+
+  /**
+   * Gets the value of the configuration property with the specified name
+   *
+   * @param name a String declaring the name of the property to retrieve
+   * @return a String or null if the property value is not found
+   */
+  public String getProperty(String name) {
+    return ((name == null) || (properties == null)) ? null : 
properties.get(name);
+  }
+
+  /**
+   * Adds or updates the value of the configuration property with the 
specified name
+   * <p/>
+   * If the property exists, it will be overwritten; else a new entry will be 
created.
+   *
+   * @param name  a String declaring the name of the property to set
+   * @param value a String containing the value of the property
+   */
+  public void putProperty(String name, String value) {
+    if (name == null) {
+      throw new IllegalArgumentException("The property name must not be null");
+    }
+
+    if (properties == null) {
+      properties = new HashMap<String, String>();
+    }
+
+    properties.put(name, value);
+  }
+
+  /**
+   * Updates this KerberosConfigurationDescriptor with data from another 
KerberosConfigurationDescriptor
+   * <p/>
+   * Properties will be updated if the relevant updated values are not null.
+   *
+   * @param updates the KerberosConfigurationDescriptor containing the updated 
values
+   */
+  public void update(KerberosConfigurationDescriptor updates) {
+    if (updates != null) {
+      setType(updates.getType());
+
+      Map<String, String> updatedProperties = updates.getProperties();
+      if (updatedProperties != null) {
+        for (Map.Entry<String, String> entry : updatedProperties.entrySet()) {
+          putProperty(entry.getKey(), entry.getValue());
+        }
+      }
+    }
+  }
+
+  /**
+   * Creates a Map of values that can be used to create a copy of this 
KerberosConfigurationDescriptor
+   * or generate the JSON structure described in
+   * {@link 
org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor}
+   *
+   * @return a Map of values for this KerberosConfigurationDescriptor
+   * @see 
org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor
+   */
+  @Override
+  public Map<String, Object> toMap() {
+    Map<String, Object> map = new HashMap<String, Object>();
+    map.put(getName(), (properties == null) ? null : new HashMap<String, 
Object>(properties));
+    return map;
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() +
+        ((getProperties() == null)
+            ? 0
+            : getProperties().hashCode());
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (object == null) {
+      return false;
+    } else if (object == this) {
+      return true;
+    } else if (object.getClass() == KerberosConfigurationDescriptor.class) {
+      KerberosConfigurationDescriptor descriptor = 
(KerberosConfigurationDescriptor) object;
+      return super.equals(object) &&
+          (
+              (getProperties() == null)
+                  ? (descriptor.getProperties() == null)
+                  : getProperties().equals(descriptor.getProperties())
+          );
+    } else {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
new file mode 100644
index 0000000..14ba19d
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
@@ -0,0 +1,351 @@
+/**
+ * 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.ambari.server.state.kerberos;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * KerberosDescriptor is an implementation of an 
AbstractKerberosDescriptorContainer that
+ * encapsulates an entire Kerberos descriptor hierarchy.
+ * <p/>
+ * A KerberosDescriptor has the following properties:
+ * <ul>
+ * <li>services</li>
+ * <li>properties</li>
+ * <li>identities ({@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li>
+ * <li>configurations ({@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer})</li>
+ * </ul>
+ * <p/>
+ * The following (pseudo) JSON Schema will yield a valid KerberosDescriptor
+ * <pre>
+ *   {
+ *      "$schema": "http://json-schema.org/draft-04/schema#";,
+ *      "title": "KerberosDescriptor",
+ *      "description": "Describes a Kerberos descriptor",
+ *      "type": "object",
+ *      "properties": {
+ *        "services": {
+ *          "description": "A list of service descriptors",
+ *          "type": "array",
+ *          "items": {
+ *            "title": "KerberosServiceDescriptor"
+ *            "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor}"
+ *          }
+ *        },
+ *        "properties": {
+ *          "description": "Global properties that can be used in variable 
replacements",
+ *          "type": "object",
+ *          "patternProperties": {
+ *            "^[\w\.\_]?$": {}"
+ *          }
+ *        }
+ *        "identities": {
+ *          "description": "A list of Kerberos identity descriptors",
+ *          "type": "array",
+ *          "items": {
+ *            "title": "KerberosIdentityDescriptor"
+ *            "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor}"
+ *          }
+ *        },
+ *        "configurations": {
+ *          "description": "A list of relevant configuration blocks",
+ *          "type": "array",
+ *          "items": {
+ *            "title": "KerberosConfigurationDescriptor"
+ *            "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor}"
+ *          }
+ *        }
+ *      }
+ *   }
+ * </pre>
+ * <p/>
+ * In this implementation,
+ * {@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will 
be null
+ */
+public class KerberosDescriptor extends AbstractKerberosDescriptorContainer {
+
+  /**
+   * A Map of the "global" properties contained within this KerberosDescriptor
+   */
+  private Map<String, String> properties = null;
+
+  /**
+   * A Map of the services contained within this KerberosDescriptor
+   */
+  private Map<String, KerberosServiceDescriptor> services = null;
+
+
+  /**
+   * Given a file containing JSON-formatted text, attempts to create a 
KerberosDescriptor
+   *
+   * @param file a File pointing to the file containing JSON-formatted text
+   * @return a newly created KerberosDescriptor
+   * @throws FileNotFoundException if the file does not point to a readable 
file
+   */
+  public static KerberosDescriptor fromFile(File file) throws IOException {
+    return new KerberosDescriptor(parseFile(file));
+  }
+
+  /**
+   * Given a String containing JSON-formatted text, attempts to create a 
KerberosDescriptor
+   *
+   * @param json a File pointing to the file containing JSON-formatted text
+   * @return a newly created KerberosDescriptor
+   */
+  public static KerberosDescriptor fromJSON(String json) {
+    return new KerberosDescriptor(parseJSON(json));
+  }
+
+  /**
+   * Creates an empty KerberosDescriptor
+   */
+  public KerberosDescriptor() {
+    this(null);
+  }
+
+  /**
+   * Creates a new KerberosDescriptor
+   * <p/>
+   * See {@link org.apache.ambari.server.state.kerberos.KerberosDescriptor} 
for the JSON
+   * Schema that may be used to generate this map.
+   *
+   * @param data a Map of values use to populate the data for the new instance
+   * @see org.apache.ambari.server.state.kerberos.KerberosDescriptor
+   */
+  public KerberosDescriptor(Map<?, ?> data) {
+    super(data);
+
+    if (data != null) {
+      Object list = 
data.get(KerberosDescriptorType.SERVICE.getDescriptorPluralName());
+      if (list instanceof Collection) {
+        for (Object item : (Collection) list) {
+          if (item instanceof Map) {
+            putService(new KerberosServiceDescriptor((Map<?, ?>) item));
+          }
+        }
+      }
+
+      Object map = data.get("properties");
+      if (map instanceof Map) {
+        for (Map.Entry<?, ?> entry : ((Map<?, ?>) map).entrySet()) {
+          Object value = entry.getValue();
+          putProperty(entry.getKey().toString(), (value == null) ? null : 
value.toString());
+        }
+      }
+    }
+  }
+
+  /**
+   * Returns a Map of the KerberosServiceDescriptors in this KerberosDescriptor
+   *
+   * @return a Map of String to KerberosServiceDescriptor
+   */
+  public Map<String, KerberosServiceDescriptor> getServices() {
+    return services;
+  }
+
+  /**
+   * Gets the KerberosServiceDescriptor with the specified name
+   *
+   * @param name a String declaring the name of the KerberosServiceDescriptor 
to retrieve
+   * @return the requested KerberosServiceDescriptor or null if not found
+   */
+  public KerberosServiceDescriptor getService(String name) {
+    return ((name == null) || (services == null)) ? null : services.get(name);
+  }
+
+  /**
+   * Adds or replaces a KerberosServiceDescriptor
+   * <p/>
+   * If a KerberosServiceDescriptor with the same name already exists in the 
services Map, it will
+   * be replaced; else a new entry will be made.
+   *
+   * @param service the KerberosServiceDescriptor to put
+   */
+  public void putService(KerberosServiceDescriptor service) {
+    if (service != null) {
+      String name = service.getName();
+
+      if (name == null) {
+        throw new IllegalArgumentException("The service name must not be 
null");
+      }
+
+      if (services == null) {
+        services = new HashMap<String, KerberosServiceDescriptor>();
+      }
+
+      services.put(name, service);
+
+      // Set the service's parent to this KerberosDescriptor
+      service.setParent(this);
+    }
+  }
+
+  /**
+   * Gets the Map of properties for this KerberosDescriptor
+   *
+   * @return a Map of String to String values
+   */
+  public Map<String, String> getProperties() {
+    return properties;
+  }
+
+  /**
+   * Gets the value of the property with the specified name
+   *
+   * @param name a String declaring the name of the property to retrieve
+   * @return a String or null if the property was not found
+   */
+  public String getProperty(String name) {
+    return ((name == null) || (properties == null)) ? null : 
properties.get(name);
+  }
+
+  /**
+   * Adds or updates a property value
+   * <p/>
+   * If a property exists with the specified name, replaces its value; else 
adds a new entry.
+   *
+   * @param name  a String declaring the name of the property to put
+   * @param value a String containing the value of the property to put
+   */
+  public void putProperty(String name, String value) {
+    if (name == null) {
+      throw new IllegalArgumentException("The property name must not be null");
+    }
+
+    if (properties == null) {
+      properties = new HashMap<String, String>();
+    }
+
+    properties.put(name, value);
+  }
+
+  /**
+   * Updates this KerberosDescriptor with data from another KerberosDescriptor
+   * <p/>
+   * Properties will be updated if the relevant updated values are not null.
+   *
+   * @param updates the KerberosDescriptor containing the updated values
+   */
+  public void update(KerberosDescriptor updates) {
+    if (updates != null) {
+      Map<String, KerberosServiceDescriptor> updatedServiceDescriptors = 
updates.getServices();
+      if (updatedServiceDescriptors != null) {
+        for (Map.Entry<String, KerberosServiceDescriptor> entry : 
updatedServiceDescriptors.entrySet()) {
+          KerberosServiceDescriptor existing = getService(entry.getKey());
+          if (existing == null) {
+            putService(entry.getValue());
+          } else {
+            existing.update(entry.getValue());
+          }
+        }
+      }
+
+      Map<String, String> updatedProperties = updates.getProperties();
+      if (updatedProperties != null) {
+        for (Map.Entry<String, String> entry : updatedProperties.entrySet()) {
+          putProperty(entry.getKey(), entry.getValue());
+        }
+      }
+    }
+
+    super.update(updates);
+  }
+
+  /**
+   * Creates a Map of values that can be used to create a copy of this 
KerberosDescriptor
+   * or generate the JSON structure described in
+   * {@link org.apache.ambari.server.state.kerberos.KerberosDescriptor}
+   *
+   * @return a Map of values for this KerberosDescriptor
+   * @see org.apache.ambari.server.state.kerberos.KerberosDescriptor
+   */
+  @Override
+  public Map<String, Object> toMap() {
+    Map<String, Object> map = super.toMap();
+
+    if (services != null) {
+      List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
+      for (KerberosServiceDescriptor service : services.values()) {
+        list.add(service.toMap());
+      }
+      map.put(KerberosDescriptorType.SERVICE.getDescriptorPluralName(), list);
+    }
+
+    if (properties != null) {
+      map.put("properties", new HashMap<String, String>(properties));
+    }
+
+    return map;
+  }
+
+  /**
+   * Sets the parent (or container) of this descriptor
+   * <p/>
+   * This implementation prevents the parent from being externally set by 
always throwing an
+   * UnsupportedOperationException.
+   *
+   * @param parent an AbstractKerberosDescriptor representing the parent (or 
container) of this
+   */
+  @Override
+  public void setParent(AbstractKerberosDescriptor parent) {
+    throw new UnsupportedOperationException("This KerberosDescriptor may not 
have a parent assigned to it.");
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() +
+        ((getProperties() == null)
+            ? 0
+            : getProperties().hashCode()) +
+        ((getServices() == null)
+            ? 0
+            : getServices().hashCode());
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (object == null) {
+      return false;
+    } else if (object == this) {
+      return true;
+    } else if (object.getClass() == KerberosDescriptor.class) {
+      KerberosDescriptor descriptor = (KerberosDescriptor) object;
+      return super.equals(object) &&
+          (
+              (getProperties() == null)
+                  ? (descriptor.getProperties() == null)
+                  : getProperties().equals(descriptor.getProperties())
+          ) &&
+          (
+              (getServices() == null)
+                  ? (descriptor.getServices() == null)
+                  : getServices().equals(descriptor.getServices())
+          );
+    } else {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java
new file mode 100644
index 0000000..94c5046
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorType.java
@@ -0,0 +1,54 @@
+/*
+ * 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.ambari.server.state.kerberos;
+
+public enum KerberosDescriptorType {
+  SERVICE("service", "services"),
+  COMPONENT("component", "components"),
+  IDENTITY("identity", "identities"),
+  PRINCIPAL("principal", "principals"),
+  KEYTAB("keytab", "keytabs"),
+  CONFIGURATION("configuration", "configurations");
+
+  private final String descriptorName;
+  private final String descriptorPluralName;
+
+  private KerberosDescriptorType(String descriptorName, String 
descriptorPluralName) {
+    this.descriptorName = descriptorName;
+    this.descriptorPluralName = descriptorPluralName;
+  }
+
+  /**
+   * Gets the identifying name for this KerberosDescriptorType
+   *
+   * @return a String declaring the identifying name for this 
KerberosDescriptorType
+   */
+  public String getDescriptorName() {
+    return descriptorName;
+  }
+
+  /**
+   * Gets the identifying name for a group of this KerberosDescriptorType
+   *
+   * @return a String declaring the identifying name for a group of this 
KerberosDescriptorType
+   */
+  public String getDescriptorPluralName() {
+    return descriptorPluralName;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e7b8383b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
new file mode 100644
index 0000000..2e5a27d
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
@@ -0,0 +1,278 @@
+/**
+ * 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.ambari.server.state.kerberos;
+
+import java.util.Map;
+
+/**
+ * KerberosIdentityDescriptor is an implementation of an 
AbstractKerberosDescriptor that
+ * encapsulates data related to a Kerberos identity - including its principal 
and keytab file details.
+ * <p/>
+ * A KerberosIdentityDescriptor has the following properties:
+ * <ul>
+ * <li>name</li>
+ * <li>principal</li>
+ * <li>keytab</li>
+ * <li>password</li>
+ * </ul>
+ * <p/>
+ * The following (pseudo) JSON Schema will yield a valid 
KerberosIdentityDescriptor
+ * <pre>
+ *   {
+ *      "$schema": "http://json-schema.org/draft-04/schema#";,
+ *      "title": "KerberosIdentityDescriptor",
+ *      "description": "Describes a Kerberos identity",
+ *      "type": "object",
+ *      "properties": {
+ *        "name": {
+ *          "description": "An identifying name for this identity. The name 
may reference another
+ *                          KerberosIdentityDescriptor by declaring the path 
to it",
+ *          "type": "string"
+ *        },
+ *        "principal": {
+ *          "description": "Details about this identity's principal",
+ *          "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor}",
+ *        }
+ *        "keytab": {
+ *          "description": "Details about this identity's keytab",
+ *          "type": "{@link 
org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor}",
+ *          }
+ *        }
+ *        "password": {
+ *          "description": "The password to use for this identity. If not set 
a secure random
+ *                          password will automatically be generated",
+ *          "type": "string"
+ *        }
+ *      }
+ *   }
+ * </pre>
+ * <p/>
+ * In this implementation,
+ * {@link 
org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor#name} will 
hold the
+ * KerberosIdentityDescriptor#name value
+ */
+public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor {
+
+  /**
+   * The KerberosPrincipalDescriptor containing the principal details for this 
Kerberos identity
+   */
+  private KerberosPrincipalDescriptor principal = null;
+
+  /**
+   * The KerberosKeytabDescriptor containing the keytab details for this 
Kerberos identity
+   */
+  private KerberosKeytabDescriptor keytab = null;
+
+  /**
+   * A String containing the password for this Kerberos identity
+   * <p/>
+   * If this value is null or empty, a random password will be generated as 
necessary.
+   */
+  private String password = null;
+
+  /**
+   * Creates a new KerberosIdentityDescriptor
+   * <p/>
+   * See {@link 
org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor} for the JSON
+   * Schema that may be used to generate this map.
+   *
+   * @param data a Map of values use to populate the data for the new instance
+   * @see org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor
+   */
+  public KerberosIdentityDescriptor(Map<?, ?> data) {
+    // The name for this KerberosIdentityDescriptor is stored in the "name" 
entry in the map
+    // This is not automatically set by the super classes.
+    setName(getStringValue(data, "name"));
+
+    if (data != null) {
+      Object item;
+
+      setPassword(getStringValue(data, "password"));
+
+      item = data.get(KerberosDescriptorType.PRINCIPAL.getDescriptorName());
+      if (item instanceof Map) {
+        setPrincipalDescriptor(new KerberosPrincipalDescriptor((Map<?, ?>) 
item));
+      }
+
+      item = data.get(KerberosDescriptorType.KEYTAB.getDescriptorName());
+      if (item instanceof Map) {
+        setKeytabDescriptor(new KerberosKeytabDescriptor((Map<?, ?>) item));
+      }
+    }
+  }
+
+  /**
+   * Gets the KerberosPrincipalDescriptor related to this 
KerberosIdentityDescriptor
+   *
+   * @return the KerberosPrincipalDescriptor related to this 
KerberosIdentityDescriptor
+   */
+  public KerberosPrincipalDescriptor getPrincipalDescriptor() {
+    return principal;
+  }
+
+  /**
+   * Sets the KerberosPrincipalDescriptor related to this 
KerberosIdentityDescriptor
+   *
+   * @param principal the KerberosPrincipalDescriptor related to this 
KerberosIdentityDescriptor
+   */
+  public void setPrincipalDescriptor(KerberosPrincipalDescriptor principal) {
+    this.principal = principal;
+
+    if (this.principal != null) {
+      this.principal.setParent(this);
+    }
+  }
+
+  /**
+   * Gets the KerberosKeytabDescriptor related to this 
KerberosIdentityDescriptor
+   *
+   * @return the KerberosKeytabDescriptor related to this 
KerberosIdentityDescriptor
+   */
+  public KerberosKeytabDescriptor getKeytabDescriptor() {
+    return keytab;
+  }
+
+  /**
+   * Sets the KerberosKeytabDescriptor related to this 
KerberosIdentityDescriptor
+   *
+   * @param keytab the KerberosKeytabDescriptor related to this 
KerberosIdentityDescriptor
+   */
+  public void setKeytabDescriptor(KerberosKeytabDescriptor keytab) {
+    this.keytab = keytab;
+
+    if (this.keytab != null) {
+      this.keytab.setParent(this);
+    }
+  }
+
+  /**
+   * Gets the password for this this KerberosIdentityDescriptor
+   *
+   * @return A String containing the password for this this 
KerberosIdentityDescriptor
+   * @see #password
+   */
+  public String getPassword() {
+    return password;
+  }
+
+  /**
+   * Sets the password for this this KerberosIdentityDescriptor
+   *
+   * @param password A String containing the password for this this 
KerberosIdentityDescriptor
+   * @see #password
+   */
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  /**
+   * Updates this KerberosIdentityDescriptor with data from another 
KerberosIdentityDescriptor
+   * <p/>
+   * Properties will be updated if the relevant updated values are not null.
+   *
+   * @param updates the KerberosIdentityDescriptor containing the updated 
values
+   */
+  public void update(KerberosIdentityDescriptor updates) {
+    if (updates != null) {
+      setName(updates.getName());
+
+      setPassword(updates.getPassword());
+
+      KerberosPrincipalDescriptor existingPrincipal = getPrincipalDescriptor();
+      if (existingPrincipal == null) {
+        setPrincipalDescriptor(updates.getPrincipalDescriptor());
+      } else {
+        existingPrincipal.update(updates.getPrincipalDescriptor());
+      }
+
+      KerberosKeytabDescriptor existingKeytabDescriptor = 
getKeytabDescriptor();
+      if (existingKeytabDescriptor == null) {
+        setKeytabDescriptor(updates.getKeytabDescriptor());
+      } else {
+        existingKeytabDescriptor.update(updates.getKeytabDescriptor());
+      }
+    }
+  }
+
+  /**
+   * Creates a Map of values that can be used to create a copy of this 
KerberosIdentityDescriptor
+   * or generate the JSON structure described in
+   * {@link org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor}
+   *
+   * @return a Map of values for this KerberosIdentityDescriptor
+   * @see org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor
+   */
+  @Override
+  public Map<String, Object> toMap() {
+    Map<String, Object> dataMap = super.toMap();
+
+    if (principal != null) {
+      dataMap.put(KerberosDescriptorType.PRINCIPAL.getDescriptorName(), 
principal.toMap());
+    }
+
+    if (keytab != null) {
+      dataMap.put(KerberosDescriptorType.KEYTAB.getDescriptorName(), 
keytab.toMap());
+    }
+
+    if (password != null) {
+      dataMap.put("password", password);
+    }
+
+    return dataMap;
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() +
+        ((getPrincipalDescriptor() == null)
+            ? 0
+            : getPrincipalDescriptor().hashCode()) +
+        ((getKeytabDescriptor() == null)
+            ? 0
+            : getKeytabDescriptor().hashCode());
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (object == null) {
+      return false;
+    } else if (object == this) {
+      return true;
+    } else if (object.getClass() == KerberosIdentityDescriptor.class) {
+      KerberosIdentityDescriptor descriptor = (KerberosIdentityDescriptor) 
object;
+      return super.equals(object) &&
+          (
+              (getPrincipalDescriptor() == null)
+                  ? (descriptor.getPrincipalDescriptor() == null)
+                  : 
getPrincipalDescriptor().equals(descriptor.getPrincipalDescriptor())
+          ) &&
+          (
+              (getKeytabDescriptor() == null)
+                  ? (descriptor.getKeytabDescriptor() == null)
+                  : 
getKeytabDescriptor().equals(descriptor.getKeytabDescriptor())
+          ) &&
+          (
+              (getPassword() == null)
+                  ? (descriptor.getPassword() == null)
+                  : getPassword().equals(descriptor.getPassword())
+          );
+    } else {
+      return false;
+    }
+  }
+}

Reply via email to