RANGER-459 Additional validations for service-def

Signed-off-by: Madhan Neethiraj <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/7ac04f9d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/7ac04f9d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/7ac04f9d

Branch: refs/heads/master
Commit: 7ac04f9d3389aa71b6b0ce6804abe6e8d527ca5e
Parents: cdcb1c0
Author: Alok Lal <[email protected]>
Authored: Thu May 7 11:23:47 2015 -0700
Committer: Madhan Neethiraj <[email protected]>
Committed: Fri May 8 12:36:28 2015 -0700

----------------------------------------------------------------------
 .../model/validation/RangerPolicyValidator.java |   9 +-
 .../validation/RangerServiceDefHelper.java      |  43 +-
 .../validation/RangerServiceDefValidator.java   | 444 +++++++++++++------
 .../validation/RangerServiceValidator.java      |   9 +-
 .../model/validation/RangerValidator.java       | 150 +++++++
 .../ranger/plugin/util/RangerObjectFactory.java |  10 +
 .../service-defs/ranger-servicedef-kafka.json   |  12 +
 .../service-defs/ranger-servicedef-kms.json     |  12 +
 .../validation/TestRangerPolicyValidator.java   |  15 +-
 .../validation/TestRangerServiceDefHelper.java  | 109 ++++-
 .../TestRangerServiceDefValidator.java          | 358 ++++++++++-----
 .../validation/TestRangerServiceValidator.java  |  10 +-
 .../model/validation/TestRangerValidator.java   |  71 +++
 .../model/validation/ValidationTestUtils.java   | 118 ++++-
 .../src/test/resources/log4j.properties         |   2 +-
 15 files changed, 1075 insertions(+), 297 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
index 7f0318f..1acf81f 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
@@ -90,12 +90,9 @@ public class RangerPolicyValidator extends RangerValidator {
                                .build());
                        valid = false;
                } else if (getPolicy(id) == null) {
-                       failures.add(new ValidationFailureDetailsBuilder()
-                               .field("id")
-                               .isSemanticallyIncorrect()
-                               .becauseOf("no policy found for id[" + id + "]")
-                               .build());
-                       valid = false;
+                       if (LOG.isDebugEnabled()) {
+                               LOG.debug("No policy found for id[" + id + "]! 
ok!");
+                       }
                }
 
                if(LOG.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java
index 6381dfe..91ff16a 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java
@@ -44,6 +44,15 @@ public class RangerServiceDefHelper {
        final Delegate _delegate;
 
        public RangerServiceDefHelper(RangerServiceDef serviceDef) {
+               this(serviceDef, true);
+       }
+       
+       /**
+        * Intended for use when serviceDef object is not-trusted, e.g. when 
service-def is being created or updated. 
+        * @param serviceDef
+        * @param useCache
+        */
+       public RangerServiceDefHelper(RangerServiceDef serviceDef, boolean 
useCache) {
                // NOTE: we assume serviceDef, its name and update time are can 
never by null.
                
                if(LOG.isDebugEnabled()) {
@@ -54,7 +63,7 @@ public class RangerServiceDefHelper {
                Date serviceDefFreshnessDate = serviceDef.getUpdateTime();
 
                Delegate delegate = null;
-               if (_Cache.containsKey(serviceName)) {
+               if (useCache && _Cache.containsKey(serviceName)) {
                        LOG.debug("RangerServiceDefHelper(): found delegate in 
cache with matching serviceName.  Need to check date");
                        Delegate that = _Cache.get(serviceName);
                        if (Objects.equals(that.getServiceFreshnessDate(), 
serviceDefFreshnessDate)) {
@@ -66,8 +75,10 @@ public class RangerServiceDefHelper {
                }
                if (delegate == null) { // either not found in cache or date 
didn't match
                        delegate = new Delegate(serviceDef);
-                       _Cache.put(serviceName, delegate);
-                       LOG.debug("RangerServiceDefHelper(): Created new 
delegate and put in delegate cache!");
+                       if (useCache) {
+                               LOG.debug("RangerServiceDefHelper(): Created 
new delegate and put in delegate cache!");
+                               _Cache.put(serviceName, delegate);
+                       }
                }
                _delegate = delegate;
        }
@@ -106,13 +117,17 @@ public class RangerServiceDefHelper {
                return result;
        }
        
-       public Set<String> getAllResourceNames(List<RangerResourceDef> 
hierarchy) {
-               Set<String> result = new HashSet<String>(hierarchy.size());
+       public List<String> getAllResourceNames(List<RangerResourceDef> 
hierarchy) {
+               List<String> result = new ArrayList<String>(hierarchy.size());
                for (RangerResourceDef resourceDef : hierarchy) {
                        result.add(resourceDef.getName());
                }
                return result;
        }
+       
+       public boolean isResourceGraphValid() {
+               return _delegate.isResourceGraphValid();
+       }
 
        /**
         * Not designed for public access.  Package level only for testability.
@@ -123,6 +138,7 @@ public class RangerServiceDefHelper {
                final Date _serviceDefFreshnessDate;
                final String _serviceName;
                final Map<String, RangerResourceDef> _resourceMap;
+               final boolean _valid;
                
                public Delegate(RangerServiceDef serviceDef) {
 
@@ -135,7 +151,8 @@ public class RangerServiceDefHelper {
                        _resourceMap = 
Collections.unmodifiableMap(getResourcesAsMap(resourceDefs));
                        
                        DirectedGraph graph = createGraph(resourceDefs);
-                       if (isValid(graph)) {
+                       _valid = isValid(graph); 
+                       if (_valid) {
                                Set<List<String>> hierarchies = 
getHierarchies(graph);
                                _hierarchies = 
Collections.unmodifiableSet(convertHierarchies(hierarchies, _resourceMap));
                        } else {
@@ -163,6 +180,10 @@ public class RangerServiceDefHelper {
                public Date getServiceFreshnessDate() {
                        return _serviceDefFreshnessDate;
                }
+               
+               public boolean isResourceGraphValid() {
+                       return _valid;
+               }
                /**
                 * Builds a directed graph where each resource is node and arc 
goes from parent level to child level
                 * 
@@ -186,12 +207,12 @@ public class RangerServiceDefHelper {
                }
 
                /**
-                * A minimally valid resource graph has - at least one sink AND 
- and least one source.
-                * 
-                * A more rigorous definition would require all of the 
following: - exactly one source (assuming this is required) - At least one sink 
- no cycles - all non-source nodes have an in-degree of
-                * exactly 1 - all non-sink nodes have an out-degree of 1 or 
more (if more than one source is allowed then this will changed)
+                * A valid resource graph is a forest, i.e. a disjoint union of 
trees.  In our case, given that node can have only one "parent" node, we can 
detect this validity simply by ensuring that 
+                * the resource graph has:
+                * - at least one sink AND
+                * - and least one source.
                 * 
-                * Anyhow, we don't need such a rigorous definition at this 
time, hence a more complete validity function is deferred till we need one!
+                * A more direct method would have been ensure that the 
resulting graph does not have any cycles. 
                 * 
                 * @param graph
                 * 

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
index 0ef7ff9..c57e5fc 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import org.apache.commons.collections.CollectionUtils;
@@ -33,8 +35,12 @@ import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef;
+import 
org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
 import org.apache.ranger.plugin.store.ServiceStore;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 public class RangerServiceDefValidator extends RangerValidator {
@@ -84,12 +90,9 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                                .build());
                        valid = false;
                } else if (getServiceDef(id) == null) {
-                       failures.add(new ValidationFailureDetailsBuilder()
-                               .field("id")
-                               .isSemanticallyIncorrect()
-                               .becauseOf("no service def found for id[" + id 
+ "]")
-                               .build());
-                       valid = false;
+                       if (LOG.isDebugEnabled()) {
+                               LOG.debug("No service found for id[" + id + "]! 
ok!");
+                       }
                }
 
                if(LOG.isDebugEnabled()) {
@@ -118,78 +121,103 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                        valid = false;
                } else {
                        Long id = serviceDef.getId();
-                       if (action == Action.UPDATE) { // id is ignored for 
CREATE
-                               if (id == null) {
-                                       String message = "service def id was 
null/empty/blank"; 
-                                       LOG.debug(message);
-                                       failures.add(new 
ValidationFailureDetailsBuilder()
-                                               .field("id")
-                                               .isMissing()
-                                               .becauseOf(message)
-                                               .build());
-                                       valid = false;
-                               } else if (getServiceDef(id) == null) {
-                                       failures.add(new 
ValidationFailureDetailsBuilder()
-                                               .field("id")
-                                               .isSemanticallyIncorrect()
-                                               .becauseOf("no service def 
exists with id[" + id +"]")
-                                               .build());
-                                       valid = false;
-                               }
+                       valid = isValidServiceDefId(id, action, failures) && 
valid;
+                       valid = isValidServiceDefName(serviceDef.getName(), id, 
action, failures) && valid;
+                       valid = isValidAccessTypes(serviceDef.getAccessTypes(), 
failures) && valid;
+                       if (isValidResources(serviceDef, failures)) {
+                               // Semantic check of resource graph can only be 
done if resources are "syntactically" valid
+                               valid = isValidResourceGraph(serviceDef, 
failures) && valid;
+                       } else {
+                               valid = false;
                        }
-                       // validate the service def name
-                       String name = serviceDef.getName();
-                       boolean nameSpecified = StringUtils.isNotBlank(name);
-                       if (!nameSpecified) {
-                               String message = "service def name[" + name + 
"] was null/empty/blank"; 
+                       List<RangerEnumDef> enumDefs = serviceDef.getEnums();
+                       if (isValidEnums(enumDefs, failures)) {
+                               // config def validation requires valid enums
+                               valid = isValidConfigs(serviceDef.getConfigs(), 
enumDefs, failures) && valid;
+                       } else {
+                               valid = false;
+                       }
+                       valid = 
isValidPolicyConditions(serviceDef.getPolicyConditions(), failures) && valid;
+               }
+               
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug("<== RangerServiceDefValidator.isValid(" + 
serviceDef + "): " + valid);
+               }
+               return valid;
+       }
+
+       boolean isValidServiceDefId(Long id, final Action action, final 
List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidServiceDefId(%s, %s, %s)", id, action, 
failures));
+               }
+               boolean valid = true;
+
+               if (action == Action.UPDATE) { // id is ignored for CREATE
+                       if (id == null) {
+                               String message = "service def id was 
null/empty/blank"; 
                                LOG.debug(message);
                                failures.add(new 
ValidationFailureDetailsBuilder()
-                                       .field("name")
+                                       .field("id")
                                        .isMissing()
                                        .becauseOf(message)
                                        .build());
                                valid = false;
-                       } else {
-                               RangerServiceDef otherServiceDef = 
getServiceDef(name);
-                               if (otherServiceDef != null && action == 
Action.CREATE) {
-                                       failures.add(new 
ValidationFailureDetailsBuilder()
-                                               .field("name")
-                                               .isSemanticallyIncorrect()
-                                               .becauseOf("service def with 
the name[" + name + "] already exists")
-                                               .build());
-                                       valid = false;
-                               } else if (otherServiceDef != null && 
otherServiceDef.getId() !=null && otherServiceDef.getId() != id) {
-                                       failures.add(new 
ValidationFailureDetailsBuilder()
-                                               .field("id/name")
-                                               .isSemanticallyIncorrect()
-                                               .becauseOf("id/name conflict: 
another service def already exists with name[" + name + "], its id is [" + 
otherServiceDef.getId() + "]")
-                                               .build());
-                                       valid = false;
-                               }
-                       }
-                       if 
(CollectionUtils.isEmpty(serviceDef.getAccessTypes())) {
+                       } else if (getServiceDef(id) == null) {
                                failures.add(new 
ValidationFailureDetailsBuilder()
-                                       .field("access types")
-                                       .isMissing()
-                                       .becauseOf("access types collection was 
null/empty")
+                                       .field("id")
+                                       .isSemanticallyIncorrect()
+                                       .becauseOf("no service def exists with 
id[" + id +"]")
                                        .build());
                                valid = false;
-                       } else {
-                               valid = 
isValidAccessTypes(serviceDef.getAccessTypes(), failures) && valid;
-                       }
-                       if (CollectionUtils.isEmpty(serviceDef.getEnums())) {
-                               LOG.debug("No enums specified on the service 
def.  Ok!");
-                       } else {
-                               valid = isValidEnums(serviceDef.getEnums(), 
failures) && valid;
                        }
                }
-               
+
                if(LOG.isDebugEnabled()) {
-                       LOG.debug("<== RangerServiceDefValidator.isValid(" + 
serviceDef + "): " + valid);
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidServiceDefId(%s, %s, %s): %s", id, action, 
failures, valid));
                }
                return valid;
        }
+       
+       boolean isValidServiceDefName(String name, Long id, final Action 
action, final List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidServiceDefName(%s, %s, %s, %s)", name, id, 
action, failures));
+               }
+               boolean valid = true;
+
+               if (StringUtils.isBlank(name)) {
+                       String message = "service def name[" + name + "] was 
null/empty/blank"; 
+                       LOG.debug(message);
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field("name")
+                               .isMissing()
+                               .becauseOf(message)
+                               .build());
+                       valid = false;
+               } else {
+                       RangerServiceDef otherServiceDef = getServiceDef(name);
+                       if (otherServiceDef != null && action == Action.CREATE) 
{
+                               failures.add(new 
ValidationFailureDetailsBuilder()
+                                       .field("name")
+                                       .isSemanticallyIncorrect()
+                                       .becauseOf("service def with the name[" 
+ name + "] already exists")
+                                       .build());
+                               valid = false;
+                       } else if (otherServiceDef != null && 
!Objects.equals(id, otherServiceDef.getId())) {
+                               failures.add(new 
ValidationFailureDetailsBuilder()
+                                       .field("id/name")
+                                       .isSemanticallyIncorrect()
+                                       .becauseOf("id/name conflict: another 
service def already exists with name[" + name + "], its id is [" + 
otherServiceDef.getId() + "]")
+                                       .build());
+                               valid = false;
+                       }
+               }
 
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidServiceDefName(%s, %s, %s, %s): %s", name, id, 
action, failures, valid));
+               }
+               return valid;
+       }
+       
        boolean isValidAccessTypes(final List<RangerAccessTypeDef> 
accessTypeDefs, final List<ValidationFailureDetails> failures) {
                if(LOG.isDebugEnabled()) {
                        LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidAccessTypes(%s, %s)", accessTypeDefs, 
failures));
@@ -197,31 +225,20 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                
                boolean valid = true;
                if (CollectionUtils.isEmpty(accessTypeDefs)) {
-                       LOG.debug("access type def collection is empty/null");
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field("access types")
+                               .isMissing()
+                               .becauseOf("access types collection was 
null/empty")
+                               .build());
+                       valid = false;
                } else {
                        List<RangerAccessTypeDef> defsWithImpliedGrants = new 
ArrayList<RangerAccessTypeDef>();
                        Set<String> accessNames = new HashSet<String>();
+                       Set<Long> ids = new HashSet<Long>();
                        for (RangerAccessTypeDef def : accessTypeDefs) {
                                String name = def.getName();
-                               // name can't be null/empty/blank
-                               if (StringUtils.isBlank(name)) {
-                                       failures.add(new 
ValidationFailureDetailsBuilder()
-                                               .field("access type name")
-                                               .isMissing()
-                                               .becauseOf("access type name[" 
+ name + "] is null/empty")
-                                               .build());
-                                       valid = false;
-                               } else if 
(accessNames.contains(name.toLowerCase())) {
-                                       failures.add(new 
ValidationFailureDetailsBuilder()
-                                               .field("access type name")
-                                               .subField(name)
-                                               .isSemanticallyIncorrect()
-                                               .becauseOf("duplicate access 
type names in access types collection: [" + name + "]")
-                                               .build());
-                                       valid = false;
-                               } else {
-                                       accessNames.add(name.toLowerCase()); // 
we have a new unique access type
-                               }
+                               valid = isUnique(name, accessNames, "access 
type name", "access types", failures) && valid;
+                               valid = isUnique(def.getId(), ids, "access type 
id", "access types", failures) && valid;
                                if 
(CollectionUtils.isNotEmpty(def.getImpliedGrants())) {
                                        defsWithImpliedGrants.add(def);
                                }
@@ -259,6 +276,215 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                return valid;
        }
 
+       boolean isValidPolicyConditions(List<RangerPolicyConditionDef> 
policyConditions, List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidPolicyConditions(%s, %s)", policyConditions, 
failures));
+               }
+               boolean valid = true;
+
+               if (CollectionUtils.isEmpty(policyConditions)) {
+                       LOG.debug("Configs collection was null/empty! ok");
+               } else {
+                       Set<Long> ids = new HashSet<Long>();
+                       Set<String> names = new HashSet<String>();
+                       for (RangerPolicyConditionDef conditionDef : 
policyConditions) {
+                               valid = isUnique(conditionDef.getId(), ids, 
"policy condition def id", "policy condition defs", failures) && valid;
+                               String name = conditionDef.getName();
+                               valid = isUnique(name, names, "policy condition 
def name", "policy condition defs", failures) && valid;
+                               if 
(StringUtils.isBlank(conditionDef.getEvaluator())) {
+                                       String reason = 
String.format("evaluator on policy condition definition[%s] was null/empty!", 
name);
+                                       LOG.debug(reason);
+                                       failures.add(new 
ValidationFailureDetailsBuilder()
+                                               .field("policy condition def 
evaluator")
+                                               .subField(name)
+                                               .isMissing()
+                                               .becauseOf(reason)
+                                               .build());
+                                       valid = false;
+                               }
+                       }
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidPolicyConditions(%s, %s): %s", 
policyConditions, failures, valid));
+               }
+               return valid;
+       }
+
+       boolean isValidConfigs(List<RangerServiceConfigDef> configs, 
List<RangerEnumDef> enumDefs, List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidConfigs(%s, %s, %s)", configs, enumDefs, 
failures));
+               }
+               boolean valid = true;
+
+               if (CollectionUtils.isEmpty(configs)) {
+                       LOG.debug("Configs collection was null/empty! ok");
+               } else {
+                       Set<Long> ids = new HashSet<Long>(configs.size());
+                       Set<String> names = new HashSet<String>(configs.size());
+                       for (RangerServiceConfigDef aConfig : configs) {
+                               valid = isUnique(aConfig.getId(), ids, "config 
def id", "config defs", failures) && valid;
+                               String configName = aConfig.getName();
+                               valid = isUnique(configName, names, "config def 
name", "config defs", failures) && valid;
+                               String type = aConfig.getType();
+                               valid = isValidConfigType(type, configName, 
failures) && valid;
+                               if ("enum".equals(type)) {
+                                       valid = 
isValidConfigOfEnumType(aConfig, enumDefs, failures) && valid;
+                               }
+                       }
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidConfigs(%s, %s, %s): %s", configs, enumDefs, 
failures, valid));
+               }
+               return valid;
+       }
+       
+       boolean isValidConfigOfEnumType(RangerServiceConfigDef configDef, 
List<RangerEnumDef> enumDefs, List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidConfigOfEnumType(%s, %s, %s)", configDef, 
enumDefs, failures));
+               }
+               boolean valid = true;
+
+               if (!"enum".equals(configDef.getType())) {
+                       LOG.debug("ConfigDef wasn't of enum type!");
+               } else {
+                       Map<String, RangerEnumDef> enumDefsMap = 
getEnumDefMap(enumDefs);
+                       Set<String> enumTypes = enumDefsMap.keySet();
+                       String subType = configDef.getSubType();
+                       String configName = configDef.getName();
+                       
+                       if (!enumTypes.contains(subType)) {
+                               String reason = String.format("subtype[%s] of 
service def config[%s] was not among defined enums[%s]", subType, configName, 
enumTypes);
+                               failures.add(new 
ValidationFailureDetailsBuilder()
+                                       .field("config def subtype")
+                                       .subField(configName)
+                                       .isSemanticallyIncorrect()
+                                       .becauseOf(reason)
+                                       .build());
+                               valid = false;
+                       } else {
+                               // default value check is possible only if 
sub-type is correctly configured
+                               String defaultValue = 
configDef.getDefaultValue();
+                               if (StringUtils.isNotBlank(defaultValue)) {
+                                       RangerEnumDef enumDef = 
enumDefsMap.get(subType);
+                                       Set<String> enumValues = 
getEnumValues(enumDef);
+                                       if (!enumValues.contains(defaultValue)) 
{
+                                               String reason = 
String.format("default value[%s] of service def config[%s] was not among the 
valid values[%s] of enums[%s]", defaultValue, configName, enumValues, subType);
+                                               failures.add(new 
ValidationFailureDetailsBuilder()
+                                                       .field("config def 
default value")
+                                                       .subField(configName)
+                                                       
.isSemanticallyIncorrect()
+                                                       .becauseOf(reason)
+                                                       .build());
+                                               valid = false;
+                                       }
+                               }
+                       }
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidConfigOfEnumType(%s, %s, %s): %s", configDef, 
enumDefs, failures, valid));
+               }
+               return valid;
+       }
+       
+       boolean isValidConfigType(String type, String configName, 
List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidConfigType(%s, %s, %s)", type, configName, 
failures));
+               }
+               boolean valid = true;
+
+               Set<String> validTypes = ImmutableSet.of("bool", "enum", "int", 
"string", "password", "path");
+               if (StringUtils.isBlank(type)) {
+                       String reason = String.format("type of service def 
config[%s] was null/empty", configName);
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field("config def type")
+                               .subField(configName)
+                               .isMissing()
+                               .becauseOf(reason)
+                               .build());
+                       valid = false;
+               } else if (!validTypes.contains(type)) {
+                       String reason = String.format("type[%s] of service def 
config[%s] is not among valid types: %s", type, configName, validTypes);
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field("config def type")
+                               .subField(configName)
+                               .isSemanticallyIncorrect()
+                               .becauseOf(reason)
+                               .build());
+                       valid = false;
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidConfigType(%s, %s, %s): %s", type, configName, 
failures, valid));
+               }
+               return valid;
+       }
+
+       boolean isValidResources(RangerServiceDef serviceDef, 
List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidResources(%s, %s)", serviceDef, failures));
+               }
+               boolean valid = true;
+
+               List<RangerResourceDef> resources = serviceDef.getResources();
+               if (CollectionUtils.isEmpty(resources)) {
+                       String reason = "service def resources collection was 
null/empty";
+                       failures.add(new ValidationFailureDetailsBuilder()
+                                       .field("resources")
+                                       .isMissing()
+                                       .becauseOf(reason)
+                                       .build());
+                       valid = false;
+               } else {
+                       Set<String> names = new 
HashSet<String>(resources.size());
+                       Set<Long> ids = new HashSet<Long>(resources.size());
+                       for (RangerResourceDef resource : resources) {
+                               /*
+                                * While id is the natural key, name is a 
surrogate key.  At several places code expects resource name to be unique 
within a service.
+                                */
+                               valid = isUnique(resource.getName(), names, 
"resource name", "resources", failures) && valid;
+                               valid = isUnique(resource.getId(), ids, 
"resource id", "resources", failures) && valid;
+                       }
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidResources(%s, %s): %s", serviceDef, failures, 
valid));
+               }
+               return valid;
+       }
+       
+       boolean isValidResourceGraph(RangerServiceDef serviceDef, 
List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidResourceGraph(%s, %s)", serviceDef, failures));
+               }
+               boolean valid = true;
+               // We don't want this helper to get into the cache or to use 
what is in the cache!!
+               RangerServiceDefHelper defHelper = 
_factory.createServiceDefHelper(serviceDef, false);
+               if (!defHelper.isResourceGraphValid()) {
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field("resource graph")
+                               .isSemanticallyIncorrect()
+                               .becauseOf("Resource graph implied by various 
resources, e.g. parent value is invalid.  Valid graph must forest (union of 
disjoint trees).")
+                               .build());
+                       valid = false;
+               }
+               // resource level should be unique within a hierarchy
+               Set<List<RangerResourceDef>> hierarchies = 
defHelper.getResourceHierarchies();
+               for (List<RangerResourceDef> aHierarchy : hierarchies) {
+                       Set<Integer> levels = new 
HashSet<Integer>(aHierarchy.size());
+                       for (RangerResourceDef resourceDef : aHierarchy) {
+                               valid = isUnique(resourceDef.getLevel(), 
levels, "resource level", "resources", failures) && valid;
+                       }
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidResourceGraph(%s, %s): %s", serviceDef, 
failures, valid));
+               }
+               return valid;
+       }
+       
        boolean isValidEnums(List<RangerEnumDef> enumDefs, 
List<ValidationFailureDetails> failures) {
                if(LOG.isDebugEnabled()) {
                        LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidEnums(%s, %s)", enumDefs, failures));
@@ -266,9 +492,10 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                
                boolean valid = true;
                if (CollectionUtils.isEmpty(enumDefs)) {
-                       LOG.debug("enum def collection passed in was 
null/empty");
+                       LOG.debug("enum def collection passed in was 
null/empty. Ok.");
                } else {
-                       Set<String> enumNames = new HashSet<String>();
+                       Set<String> names = new HashSet<String>();
+                       Set<Long> ids = new HashSet<Long>();
                        for (RangerEnumDef enumDef : enumDefs) {
                                if (enumDef == null) {
                                        failures.add(new 
ValidationFailureDetailsBuilder()
@@ -278,26 +505,10 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                                                .build());
                                        valid = false;
                                } else {
-                                       // enum-names must non-blank and be 
unique to a service definition
-                                       String enumName = enumDef.getName();
-                                       if (StringUtils.isBlank(enumName)) {
-                                               failures.add(new 
ValidationFailureDetailsBuilder()
-                                                       .field("enum def name")
-                                                       .isMissing()
-                                                       .becauseOf("enum name 
[" + enumName + "] is null/empty")
-                                                       .build());
-                                               valid = false;
-                                       } else if 
(enumNames.contains(enumName.toLowerCase())) {
-                                               failures.add(new 
ValidationFailureDetailsBuilder()
-                                                       .field("enum def name")
-                                                       .subField(enumName)
-                                                       
.isSemanticallyIncorrect()
-                                                       .becauseOf("dumplicate 
enum name [" + enumName + "] found")
-                                                       .build());
-                                               valid = false;
-                                       } else {
-                                               
enumNames.add(enumName.toLowerCase());
-                                       }
+                                       // enum-names and ids must non-blank 
and be unique to a service definition
+                                       String enumName = enumDef.getName(); 
+                                       valid = isUnique(enumName, names, "enum 
def name", "enum defs", failures) && valid;
+                                       valid = isUnique(enumDef.getId(), ids, 
"enum def id", "enum defs", failures) && valid;          
                                        // enum must contain at least one valid 
value and those values should be non-blank and distinct
                                        if 
(CollectionUtils.isEmpty(enumDef.getElements())) {
                                                failures.add(new 
ValidationFailureDetailsBuilder()
@@ -333,7 +544,7 @@ public class RangerServiceDefValidator extends 
RangerValidator {
 
        boolean isValidEnumElements(List<RangerEnumElementDef> 
enumElementsDefs, List<ValidationFailureDetails> failures, String enumName) {
                if(LOG.isDebugEnabled()) {
-                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidEnums(%s, %s)", enumElementsDefs, failures));
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValidEnumElements(%s, %s)", enumElementsDefs, 
failures));
                }
                
                boolean valid = true;
@@ -342,6 +553,7 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                } else {
                        // enum element names should be valid and distinct
                        Set<String> elementNames = new HashSet<String>();
+                       Set<Long> ids = new HashSet<Long>();
                        for (RangerEnumElementDef elementDef : 
enumElementsDefs) {
                                if (elementDef == null) {
                                        failures.add(new 
ValidationFailureDetailsBuilder()
@@ -352,32 +564,14 @@ public class RangerServiceDefValidator extends 
RangerValidator {
                                                .build());
                                        valid = false;
                                } else {
-                                       String elementName = 
elementDef.getName();
-                                       if (StringUtils.isBlank(elementName)) {
-                                               failures.add(new 
ValidationFailureDetailsBuilder()
-                                                       .field("enum element 
name")
-                                                       .subField(enumName)
-                                                       .isMissing()
-                                                       .becauseOf("Name of an 
element of enum [" + enumName + "] is null/empty[" + elementName + "]")
-                                                       .build());
-                                               valid = false;
-                                       } else if 
(elementNames.contains(elementName.toLowerCase())) {
-                                               failures.add(new 
ValidationFailureDetailsBuilder()
-                                                       .field("enum element 
name")
-                                                       .subField(enumName)
-                                                       
.isSemanticallyIncorrect()
-                                                       .becauseOf("dumplicate 
enum element name [" + elementName + "] found for enum[" + enumName + "]")
-                                                       .build());
-                                               valid = false;
-                                       } else {
-                                               
elementNames.add(elementName.toLowerCase());
-                                       }
+                                       valid = isUnique(elementDef.getName(), 
enumName, elementNames, "enum element name", "enum elements", failures) && 
valid;
+                                       valid = isUnique(elementDef.getId(), 
enumName, ids, "enum element id", "enum elements", failures) && valid;
                                }
                        }
                }
                
                if(LOG.isDebugEnabled()) {
-                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidEnums(%s, %s): %s", enumElementsDefs, 
failures, valid));
+                       LOG.debug(String.format("<== 
RangerServiceDefValidator.isValidEnumElements(%s, %s): %s", enumElementsDefs, 
failures, valid));
                }
                return valid;
        }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
index 2019284..659249e 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
@@ -79,12 +79,9 @@ public class RangerServiceValidator extends RangerValidator {
                                .build());
                        valid = false;
                } else if (getService(id) == null) {
-                       failures.add(new ValidationFailureDetailsBuilder()
-                               .field("id")
-                               .isSemanticallyIncorrect()
-                               .becauseOf("no service found for id[" + id + 
"]")
-                               .build());
-                       valid = false;
+                       if (LOG.isDebugEnabled()) {
+                               LOG.debug("No service found for id[" + id + "]! 
ok!");
+                       }
                }
 
                if(LOG.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
index bc4c7f1..ec16eee 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
@@ -39,6 +39,7 @@ import org.apache.ranger.plugin.model.RangerService;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
 import org.apache.ranger.plugin.store.ServiceStore;
@@ -501,4 +502,153 @@ public abstract class RangerValidator {
                return output;
        }
 
+       boolean isUnique(Long value, Set<Long> alreadySeen, String valueName, 
String collectionName, List<ValidationFailureDetails> failures) {
+               return isUnique(value, null,  alreadySeen, valueName, 
collectionName, failures);
+       }
+
+       /**
+        * NOTE: <code>alreadySeen</code> collection passed in gets updated.
+        * @param value
+        * @param alreadySeen
+        * @param valueName - use-friendly name of the <code>value</code> that 
would be used when generating failure message
+        * @param collectionName - use-friendly name of the <code>value</code> 
collection that would be used when generating failure message
+        * @param failures
+        * @return
+        */
+       boolean isUnique(Long value, String valueContext, Set<Long> 
alreadySeen, String valueName, String collectionName, 
List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValueUnique(%s, %s, %s, %s, %s)", value, 
alreadySeen, valueName, collectionName, failures));
+               }
+               boolean valid = true;
+
+               if (value == null) {  // null/empty/blank value is an error
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field(valueName)
+                               .subField(valueContext)
+                               .isMissing()
+                               .becauseOf(String.format("%s[%s] is 
null/empty", valueName, value))
+                               .build());
+                       valid = false;
+               } else if (alreadySeen.contains(value)) { // it shouldn't have 
been seen already
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field(valueName)
+                               .subField(value.toString())
+                               .isSemanticallyIncorrect()
+                               .becauseOf(String.format("duplicate %s[%s] in 
%s", valueName, value, collectionName))
+                               .build());
+                       valid = false;
+               } else {
+                       alreadySeen.add(value); // we have a new unique access 
type
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValueUnique(%s, %s, %s, %s, %s): %s", value, 
alreadySeen, valueName, collectionName, failures, valid));
+               }
+               return valid;
+       }
+
+       /**
+        * NOTE: <code>alreadySeen</code> collection passed in gets updated.
+        * @param value
+        * @param alreadySeen
+        * @param valueName - use-friendly name of the <code>value</code> that 
would be used when generating failure message
+        * @param collectionName - use-friendly name of the <code>value</code> 
collection that would be used when generating failure message
+        * @param failures
+        * @return
+        */
+       boolean isUnique(Integer value, Set<Integer> alreadySeen, String 
valueName, String collectionName, List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValueUnique(%s, %s, %s, %s, %s)", value, 
alreadySeen, valueName, collectionName, failures));
+               }
+               boolean valid = true;
+
+               if (value == null) {  // null/empty/blank value is an error
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field(valueName)
+                               .isMissing()
+                               .becauseOf(String.format("%s[%s] is 
null/empty", valueName, value))
+                               .build());
+                       valid = false;
+               } else if (alreadySeen.contains(value)) { // it shouldn't have 
been seen already
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field(valueName)
+                               .subField(value.toString())
+                               .isSemanticallyIncorrect()
+                               .becauseOf(String.format("duplicate %s[%s] in 
%s", valueName, value, collectionName))
+                               .build());
+                       valid = false;
+               } else {
+                       alreadySeen.add(value); // we have a new unique access 
type
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValueUnique(%s, %s, %s, %s, %s): %s", value, 
alreadySeen, valueName, collectionName, failures, valid));
+               }
+               return valid;
+       }
+
+       boolean isUnique(final String value, final Set<String> alreadySeen, 
final String valueName, final String collectionName, final 
List<ValidationFailureDetails> failures) {
+               return isUnique(value, null, alreadySeen, valueName, 
collectionName, failures);
+       }
+       /**
+        * NOTE: <code>alreadySeen</code> collection passed in gets updated.
+        * @param value
+        * @param alreadySeen
+        * @param valueName - use-friendly name of the <code>value</code> that 
would be used when generating failure message
+        * @param collectionName - use-friendly name of the <code>value</code> 
collection that would be used when generating failure message
+        * @param failures
+        * @return
+        */
+       boolean isUnique(final String value, final String valueContext, final 
Set<String> alreadySeen, final String valueName, final String collectionName, 
final List<ValidationFailureDetails> failures) {
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValueUnique(%s, %s, %s, %s, %s)", value, 
alreadySeen, valueName, collectionName, failures));
+               }
+               boolean valid = true;
+
+               if (StringUtils.isBlank(value)) {  // null/empty/blank value is 
an error
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field(valueName)
+                               .subField(valueContext)
+                               .isMissing()
+                               .becauseOf(String.format("%s[%s] is 
null/empty", valueName, value))
+                               .build());
+                       valid = false;
+               } else if (alreadySeen.contains(value.toLowerCase())) { // it 
shouldn't have been seen already
+                       failures.add(new ValidationFailureDetailsBuilder()
+                               .field(valueName)
+                               .subField(value)
+                               .isSemanticallyIncorrect()
+                               .becauseOf(String.format("duplicate %s[%s] in 
%s", valueName, value, collectionName))
+                               .build());
+                       valid = false;
+               } else {
+                       alreadySeen.add(value.toLowerCase()); // we have a new 
unique access type
+               }
+
+               if(LOG.isDebugEnabled()) {
+                       LOG.debug(String.format("==> 
RangerServiceDefValidator.isValueUnique(%s, %s, %s, %s, %s): %s", value, 
alreadySeen, valueName, collectionName, failures, valid));
+               }
+               return valid;
+       }
+       
+       Map<String, RangerEnumDef> getEnumDefMap(List<RangerEnumDef> enumDefs) {
+               Map<String, RangerEnumDef> result = new HashMap<String, 
RangerServiceDef.RangerEnumDef>();
+               if (enumDefs != null) {
+                       for (RangerEnumDef enumDef : enumDefs) {
+                               result.put(enumDef.getName(), enumDef);
+                       }
+               }
+               return result;
+       }
+
+       Set<String> getEnumValues(RangerEnumDef enumDef) {
+               Set<String> result = new HashSet<String>();
+               if (enumDef != null) {
+                       for (RangerEnumElementDef element : 
enumDef.getElements()) {
+                               result.add(element.getName());
+                       }
+               }
+               return result;
+       }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerObjectFactory.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerObjectFactory.java
 
b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerObjectFactory.java
index 72f0fd5..4a570b6 100644
--- 
a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerObjectFactory.java
+++ 
b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerObjectFactory.java
@@ -21,9 +21,19 @@ package org.apache.ranger.plugin.util;
 
 import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerPolicyResourceSignature;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper;
 
 public class RangerObjectFactory {
        public RangerPolicyResourceSignature 
createPolicyResourceSignature(RangerPolicy policy) {
                return new RangerPolicyResourceSignature(policy);
        }
+       
+       public RangerServiceDefHelper createServiceDefHelper(RangerServiceDef 
serviceDef) {
+               return new RangerServiceDefHelper(serviceDef);
+       }
+
+       public RangerServiceDefHelper createServiceDefHelper(RangerServiceDef 
serviceDef, boolean useCache) {
+               return new RangerServiceDefHelper(serviceDef, useCache);
+       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json 
b/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
index 8dc29fe..98501a2 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json
@@ -28,34 +28,42 @@
        ],
        "accessTypes":[
                {
+                       "id": 1,
                        "name":"publish",
                        "label":"Publish"
                },
                {
+                       "id": 2,
                        "name":"consume",
                        "label":"Consume"
                },
                {
+                       "id": 3,
                        "name":"create",
                        "label":"Create"
                },
                {
+                       "id": 4,
                        "name":"delete",
                        "label":"Delete"
                },
                {
+                       "id": 5,
                        "name":"configure",
                        "label":"Configure"
                },
                {
+                       "id": 6,
                        "name":"describe",
                        "label":"Describe"
                },
                {
+                       "id": 7,
                        "name":"replicate",
                        "label":"Replicate"
                },
                {
+                       "id": 8,
                        "name":"connect",
                        "label":"Connect"
                }
@@ -63,18 +71,21 @@
        ],
        "configs":[
                {
+                       "id": 1,
                        "name":"username",
                        "type":"string",
                        "mandatory":true,
                        "label":"Username"
                },
                {
+                       "id": 2,
                        "name":"password",
                        "type":"password",
                        "mandatory":true,
                        "label":"Password"
                },
                {
+                       "id": 3,
                        "name":"zookeeper.connect",
                        "type":"string",
                        "mandatory":true,
@@ -82,6 +93,7 @@
                        "label":"Zookeeper Connect String"
                },
                {
+                       "id": 4,
                        "name":"commonNameForCertificate",
                        "type":"string",
                        "mandatory":false,

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json
----------------------------------------------------------------------
diff --git 
a/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json 
b/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json
index b1f6d45..260d351 100755
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json
@@ -28,46 +28,55 @@
        "accessTypes": 
        [
                {
+                       "id": 1,
                        "name": "create",
                        "label": "Create"
                },
 
                {
+                       "id": 2,
                        "name": "delete",
                        "label": "Delete"
                },
 
                {
+                       "id": 3,
                        "name": "rollover",
                        "label": "Rollover"
                },
                
                {
+                       "id": 4,
                        "name": "setkeymaterial",
                        "label": "Set Key Material"
                },      
                
                {
+                       "id": 5,
                        "name": "get",
                        "label": "Get"
                },
                
                {
+                       "id": 6,
                        "name": "getkeys",
                        "label": "Get Keys"
                },
                
                {
+                       "id": 7,
                        "name": "getmetadata",
                        "label": "Get Metadata"
                },
                
                {
+                       "id": 8,
                        "name": "generateeek",
                        "label": "Generate EEK"
                },
                
                {
+                       "id": 9,
                        "name": "decrypteek",
                        "label": "Decrypt EEK"
                }       
@@ -76,6 +85,7 @@
        "configs": 
        [
                {
+                       "id": 1,
                        "name": "provider",
                        "type": "string",
                        "mandatory": true,
@@ -83,6 +93,7 @@
                },
                
                {
+                       "id": 2,
                        "name": "username",
                        "type": "string",
                        "mandatory": true,
@@ -90,6 +101,7 @@
                },
 
                {
+                       "id": 3,
                        "name": "password",
                        "type": "password",
                        "mandatory": true,

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java
index d55cdc5..5828f6f 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java
@@ -152,18 +152,19 @@ public class TestRangerPolicyValidator {
                _failures.clear(); _failures.clear(); 
assertFalse(_validator.isValid((Long)null, Action.DELETE, _failures));
                _utils.checkFailureForMissingValue(_failures, "id");
                
-               // should fail with appropriate error message if policy can't 
be found for the specified id
+               // should not fail if policy can't be found for the specified id
                when(_store.getPolicy(1L)).thenReturn(null);
                when(_store.getPolicy(2L)).thenThrow(new Exception());
                RangerPolicy existingPolicy = mock(RangerPolicy.class);
                when(_store.getPolicy(3L)).thenReturn(existingPolicy);
-               _failures.clear(); assertFalse(_validator.isValid(1L, 
Action.DELETE, _failures));
-               _utils.checkFailureForSemanticError(_failures, "id");
-               _failures.clear(); assertFalse(_validator.isValid(2L, 
Action.DELETE, _failures));
-               _utils.checkFailureForSemanticError(_failures, "id");
+               _failures.clear(); assertTrue(_validator.isValid(1L, 
Action.DELETE, _failures));
+               assertTrue(_failures.isEmpty());
+               _failures.clear(); assertTrue(_validator.isValid(2L, 
Action.DELETE, _failures));
+               assertTrue(_failures.isEmpty());
 
-               // if policy exists then delete validation should pass 
-               assertTrue(_validator.isValid(3L, Action.DELETE, _failures));
+               // if policy exists then delete validation should pass, too! 
+               _failures.clear(); assertTrue(_validator.isValid(3L, 
Action.DELETE, _failures));
+               assertTrue(_failures.isEmpty());
        }
        
        @Test

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java
index a004f84..2703384 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java
@@ -19,6 +19,7 @@
 
 package org.apache.ranger.plugin.model.validation;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -26,19 +27,28 @@ import static org.mockito.Mockito.when;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
-import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper;
 import 
org.apache.ranger.plugin.model.validation.RangerServiceDefHelper.Delegate;
+import org.junit.Before;
 import org.junit.Test;
 
 import com.google.common.collect.Lists;
 
 public class TestRangerServiceDefHelper {
 
+       @Before
+       public void before() {
+               _serviceDef = mock(RangerServiceDef.class); 
+               when(_serviceDef.getName()).thenReturn("a-service-def");
+               // wipe the cache clean
+               RangerServiceDefHelper._Cache.clear();
+       }
+       
        @Test
        public void test_getResourceHierarchies() {
                /*
@@ -65,12 +75,11 @@ public class TestRangerServiceDefHelper {
                // order of resources in list sould not matter
                List<RangerResourceDef> resourceDefs = 
Lists.newArrayList(Column, Database, Table, Table_Atrribute, UDF);
                // stuff this into a service-def
-               RangerServiceDef serviceDef = mock(RangerServiceDef.class);
-               when(serviceDef.getResources()).thenReturn(resourceDefs);
-               when(serviceDef.getName()).thenReturn("a-service-def");
+               when(_serviceDef.getResources()).thenReturn(resourceDefs);
                // now assert the behavior
-               RangerServiceDefHelper serviceDefHelper = new 
RangerServiceDefHelper(serviceDef);
-               Set<List<RangerResourceDef>> hierarchies = 
serviceDefHelper.getResourceHierarchies();
+               _helper = new RangerServiceDefHelper(_serviceDef);
+               assertTrue(_helper.isResourceGraphValid());
+               Set<List<RangerResourceDef>> hierarchies = 
_helper.getResourceHierarchies();
                // there should be 
                List<RangerResourceDef> hierarchy = 
Lists.newArrayList(Database, UDF);
                assertTrue(hierarchies.contains(hierarchy));
@@ -81,6 +90,69 @@ public class TestRangerServiceDefHelper {
        }
        
        @Test
+       public final void test_isResourceGraphValid_detectCycle() {
+               /*
+                * Create a service-def with cycles in resource graph
+                *  A --> B --> C
+                *  ^           |
+                *  |           |
+                *  |---- D <---  
+                */
+               RangerResourceDef A = createResourceDef("A", "D"); // A's 
parent is D, etc.
+               RangerResourceDef B = createResourceDef("B", "C");
+               RangerResourceDef C = createResourceDef("C", "D");
+               RangerResourceDef D = createResourceDef("D", "A");
+               // order of resources in list sould not matter
+               List<RangerResourceDef> resourceDefs = Lists.newArrayList(A, B, 
C, D);
+               when(_serviceDef.getResources()).thenReturn(resourceDefs);
+               _helper = new RangerServiceDefHelper(_serviceDef);
+               assertFalse("Graph was valid!", _helper.isResourceGraphValid());
+       }
+       
+       @Test
+       public final void test_isResourceGraphValid_forest() {
+               /*
+                * Create a service-def which is a forest
+                *   Database -> Table-space
+                *       |
+                *       v
+                *      Table -> Column
+                *      
+                *   Namespace -> package
+                *       |
+                *       v
+                *     function
+                *     
+                * Check that helper corrects reports back all of the 
hierarchies: levels in it and their order.   
+                */
+               RangerResourceDef database = createResourceDef("database", "");
+               RangerResourceDef tableSpace = createResourceDef("table-space", 
"database");
+               RangerResourceDef table = createResourceDef("table", 
"database");
+               RangerResourceDef column = createResourceDef("column", "table");
+               RangerResourceDef namespace = createResourceDef("namespace", 
"");
+               RangerResourceDef function = createResourceDef("function", 
"namespace");
+               RangerResourceDef Package = createResourceDef("package", 
"namespace"); 
+               List<RangerResourceDef> resourceDefs = 
Lists.newArrayList(database, tableSpace, table, column, namespace, function, 
Package);
+               when(_serviceDef.getResources()).thenReturn(resourceDefs);
+               _helper = new RangerServiceDefHelper(_serviceDef);
+               assertTrue(_helper.isResourceGraphValid());
+               Set<List<RangerResourceDef>> hierarchies = 
_helper.getResourceHierarchies();
+
+               Set<List<String>> expectedHierarchies = new 
HashSet<List<String>>(); 
+               expectedHierarchies.add(Lists.newArrayList("database", 
"table-space"));
+               expectedHierarchies.add(Lists.newArrayList("database", "table", 
"column"));
+               expectedHierarchies.add(Lists.newArrayList("namespace", 
"package"));
+               expectedHierarchies.add(Lists.newArrayList("namespace", 
"function"));
+               
+               for (List<RangerResourceDef> aHierarchy : hierarchies) {
+                       List<String> resourceNames = 
_helper.getAllResourceNames(aHierarchy);
+                       assertTrue(expectedHierarchies.contains(resourceNames));
+                       expectedHierarchies.remove(resourceNames);
+               }
+               assertTrue(expectedHierarchies.isEmpty()); // make sure we saw 
got back hierarchies
+       }
+
+       @Test
        public final void test_cacheBehavior() {
                // wipe the cache clean
                RangerServiceDefHelper._Cache.clear();
@@ -93,26 +165,26 @@ public class TestRangerServiceDefHelper {
                RangerServiceDefHelper._Cache.put(serviceName, delegate);
                
                // create a service def with matching date value
-               RangerServiceDef serviceDef = mock(RangerServiceDef.class);
-               when(serviceDef.getName()).thenReturn(serviceName);
-               when(serviceDef.getUpdateTime()).thenReturn(aDate);
+               _serviceDef = mock(RangerServiceDef.class);
+               when(_serviceDef.getName()).thenReturn(serviceName);
+               when(_serviceDef.getUpdateTime()).thenReturn(aDate);
                
                // since cache has it, we should get back the one that we have 
added
-               RangerServiceDefHelper serviceDefHelper = new 
RangerServiceDefHelper(serviceDef);
-               assertTrue("Didn't get back the same object that was put in 
cache", delegate == serviceDefHelper._delegate);
+               _helper = new RangerServiceDefHelper(_serviceDef);
+               assertTrue("Didn't get back the same object that was put in 
cache", delegate == _helper._delegate);
                
                // if we change the date then that should force helper to 
create a new delegate instance
                /*
                 * NOTE:: current logic would replace the cache instance even 
if the one in the cache is newer.  This is not likely to happen but it is 
important to call this out
                 * as in rare cases one may end up creating re creating 
delegate if threads have stale copies of service def.
                 */
-               when(serviceDef.getUpdateTime()).thenReturn(getLastMonth());
-               serviceDefHelper = new RangerServiceDefHelper(serviceDef);
-               assertTrue("Didn't get a delegate different than what was put 
in the cache", delegate != serviceDefHelper._delegate);
+               when(_serviceDef.getUpdateTime()).thenReturn(getLastMonth());
+               _helper = new RangerServiceDefHelper(_serviceDef);
+               assertTrue("Didn't get a delegate different than what was put 
in the cache", delegate != _helper._delegate);
                // now that a new instance was added to the cache let's ensure 
that it got added to the cache 
-               Delegate newDelegate = serviceDefHelper._delegate;
-               serviceDefHelper = new RangerServiceDefHelper(serviceDef);
-               assertTrue("Didn't get a delegate different than what was put 
in the cache", newDelegate == serviceDefHelper._delegate);
+               Delegate newDelegate = _helper._delegate;
+               _helper = new RangerServiceDefHelper(_serviceDef);
+               assertTrue("Didn't get a delegate different than what was put 
in the cache", newDelegate == _helper._delegate);
        }
        
        RangerResourceDef createResourceDef(String name, String parent) {
@@ -134,4 +206,7 @@ public class TestRangerServiceDefHelper {
                Date now = cal.getTime();
                return now;
        }
+       
+       private RangerServiceDef _serviceDef;
+       private RangerServiceDefHelper _helper;
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
index 1409d2c..54bbdf1 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
@@ -26,22 +26,21 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef;
-import org.apache.ranger.plugin.model.validation.RangerServiceDefValidator;
-import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
+import 
org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
 import org.apache.ranger.plugin.model.validation.RangerValidator.Action;
 import org.apache.ranger.plugin.store.ServiceStore;
 import org.junit.Before;
 import org.junit.Test;
 
-import com.google.common.collect.ImmutableMap;
-
 public class TestRangerServiceDefValidator {
 
        @Before
@@ -55,15 +54,15 @@ public class TestRangerServiceDefValidator {
        final Action[] cu = new Action[] { Action.CREATE, Action.UPDATE };
        
        final Object[][] accessTypes_good = new Object[][] {
-                       { "read",  null },                                // 
ok, null implied grants
-                       { "write", new String[] {   } },                  // 
ok, empty implied grants
-                       { "admin", new String[] { "READ",  "write" } }    // 
ok, admin access implies read/write, access types are case-insensitive
+                       { 1L, "read",  null },                                
// ok, null implied grants
+                       { 2L, "write", new String[] {   } },                  
// ok, empty implied grants
+                       { 3L, "admin", new String[] { "READ",  "write" } }    
// ok, admin access implies read/write, access types are case-insensitive
        };
 
-       final Map<String, String[]> enums_good = ImmutableMap.of(
-                       "authentication-type", new String[] { "simple", 
"kerberos" },
-                       "time-unit", new String[] { "day", "hour", "minute" }
-       );
+       final Object[][] enums_good = new Object[][] {
+                       { 1L, "authentication-type", new String[] { "simple", 
"kerberos" } },
+                       { 2L, "time-unit", new String[] { "day", "hour", 
"minute" } }, 
+       };
        
        @Test
        public final void test_isValid_happyPath_create() throws Exception {
@@ -74,26 +73,6 @@ public class TestRangerServiceDefValidator {
                List<RangerEnumDef> enumDefs = 
_utils.createEnumDefs(enums_good);
                when(_serviceDef.getEnums()).thenReturn(enumDefs);
 
-               // create: id is not relevant, name should not conflict 
-               when(_serviceDef.getId()).thenReturn(null); // id is not 
relevant for create
-               when(_serviceDef.getName()).thenReturn("aServiceDef"); // 
service has a name
-               
when(_store.getServiceDefByName("aServiceDef")).thenReturn(null); // no name 
collision
-               assertTrue(_validator.isValid(_serviceDef, Action.CREATE, 
_failures));
-               assertTrue(_failures.isEmpty());
-               
-               // update: id should match existing service, name should not 
point to different service def
-               when(_serviceDef.getId()).thenReturn(5L);
-               RangerServiceDef existingServiceDef = 
mock(RangerServiceDef.class);
-               when(_store.getServiceDef(5L)).thenReturn(existingServiceDef);
-               assertTrue(_validator.isValid(_serviceDef, Action.UPDATE, 
_failures));
-               assertTrue(_failures.isEmpty());
-               
-               // update: if name points to a service that it's id should be 
the same
-               RangerServiceDef anotherExistingServiceDef = 
mock(RangerServiceDef.class);
-               when(anotherExistingServiceDef.getId()).thenReturn(5L);
-               
when(_store.getServiceDefByName("aServiceDef")).thenReturn(anotherExistingServiceDef);
-               assertTrue(_validator.isValid(_serviceDef, Action.UPDATE, 
_failures));
-               assertTrue(_failures.isEmpty());
        }
        
        @Test
@@ -106,11 +85,11 @@ public class TestRangerServiceDefValidator {
                // passing in null id is an error
                _failures.clear(); assertFalse(_validator.isValid((Long)null, 
Action.DELETE, _failures));
                _utils.checkFailureForMissingValue(_failures, "id");
-               // a service def with that id should exist, else it is an error
+               // It is ok for a service def with that id to not exist!
                id = 3L;
                when(_store.getServiceDef(id)).thenReturn(null);
-               _failures.clear(); assertFalse(_validator.isValid(id, 
Action.DELETE, _failures));
-               _utils.checkFailureForSemanticError(_failures, "id");
+               _failures.clear(); assertTrue(_validator.isValid(id, 
Action.DELETE, _failures));
+               assertTrue(_failures.isEmpty());                
                // happypath
                when(_store.getServiceDef(id)).thenReturn(_serviceDef);
                _failures.clear(); assertTrue(_validator.isValid(id, 
Action.DELETE, _failures));
@@ -118,81 +97,116 @@ public class TestRangerServiceDefValidator {
        }
 
        @Test
-       public final void testIsValid_failures_name() throws Exception {
+       public final void testIsValid_failures() throws Exception {
                // null service def and bad service def name
                for (Action action : cu) {
                        // passing in null service def is an error
                        assertFalse(_validator.isValid((RangerServiceDef)null, 
action, _failures));
                        _utils.checkFailureForMissingValue(_failures, "service 
def");
-                       // name should be valid
-                       for (String name : new String[] { null, "", "  " }) {
-                               when(_serviceDef.getName()).thenReturn(name);
-                               _failures.clear(); 
assertFalse(_validator.isValid(_serviceDef, action, _failures));
-                               _utils.checkFailureForMissingValue(_failures, 
"name");
-                       }
                }
        }
        
        @Test
-       public final void testIsValid_failures_id() throws Exception {
+       public final void test_isValidServiceDefId_failures() throws Exception {
+               
                // id is required for update
-               when(_serviceDef.getId()).thenReturn(null);
-               assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, 
_failures));
+               assertFalse(_validator.isValidServiceDefId(null, Action.UPDATE, 
_failures));
                _utils.checkFailureForMissingValue(_failures, "id");
                
                // update: service should exist for the passed in id
-               Long id = 7L;
+               long id = 7;
                when(_serviceDef.getId()).thenReturn(id);
                when(_store.getServiceDef(id)).thenReturn(null);
-               assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, 
_failures));
+               assertFalse(_validator.isValidServiceDefId(id, Action.UPDATE, 
_failures));
                _utils.checkFailureForSemanticError(_failures, "id");
 
                when(_store.getServiceDef(id)).thenThrow(new Exception());
-               assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, 
_failures));
+               assertFalse(_validator.isValidServiceDefId(id, Action.UPDATE, 
_failures));
                _utils.checkFailureForSemanticError(_failures, "id");
        }
        
        @Test
-       public final void testIsValid_failures_nameId_create() throws Exception 
{
-               // service shouldn't exist with the name
+       public final void test_isValidServiceDefId_happyPath() throws Exception 
{
+               
+               // create: null id is ok
+               assertTrue(_validator.isValidServiceDefId(null, Action.CREATE, 
_failures));
+               assertTrue(_failures.isEmpty());
+               
+               // update: a service with same id exist
+               long id = 7;
+               when(_serviceDef.getId()).thenReturn(id);
+               RangerServiceDef serviceDefFromDb = 
mock(RangerServiceDef.class);
+               when(serviceDefFromDb.getId()).thenReturn(id);
+               when(_store.getServiceDef(id)).thenReturn(serviceDefFromDb);
+               assertTrue(_validator.isValidServiceDefId(id, Action.UPDATE, 
_failures));
+               assertTrue(_failures.isEmpty());
+       }
+       
+       @Test
+       public final void test_isValidName() throws Exception {
+               Long id = 7L; // some arbitrary value
+               // name can't be null/empty
+               for (Action action: cu) {
+                       for (String name : new String[] { null, "", "  " }) {
+                               when(_serviceDef.getName()).thenReturn(name);
+                               _failures.clear(); 
assertFalse(_validator.isValidServiceDefName(name, id, action, _failures));
+                               _utils.checkFailureForMissingValue(_failures, 
"name");
+                       }
+               }
+       }
+       
+       @Test
+       public final void test_isValidName_create() throws Exception {
+               Long id = null; // id should be irrelevant for name check for 
create.
+
+               // for create a service shouldn't exist with the name
+               String name = "existing-service";
+               when(_serviceDef.getName()).thenReturn(name);
+               when(_store.getServiceDefByName(name)).thenReturn(null);
+               assertTrue(_validator.isValidServiceDefName(name, id, 
Action.CREATE, _failures));
+               assertTrue(_failures.isEmpty());
+               
                RangerServiceDef existingServiceDef = 
mock(RangerServiceDef.class);
-               
when(_store.getServiceDefByName("existing-service")).thenReturn(existingServiceDef);
-               when(_serviceDef.getName()).thenReturn("existing-service");
-               _failures.clear(); assertFalse(_validator.isValid(_serviceDef, 
Action.CREATE, _failures));
+               
when(_store.getServiceDefByName(name)).thenReturn(existingServiceDef);
+               _failures.clear(); 
assertFalse(_validator.isValidServiceDefName(name, id, Action.CREATE, 
_failures));
                _utils.checkFailureForSemanticError(_failures, "name");
        }
        
        @Test
-       public final void testIsValid_failures_nameId_update() throws Exception 
{
+       public final void test_isValidName_update() throws Exception {
                
                // update: if service exists with the same name then it can't 
point to a different service
-               Long id = 7L;
+               long id = 7;
                when(_serviceDef.getId()).thenReturn(id);
-               RangerServiceDef existingServiceDef = 
mock(RangerServiceDef.class);
-               when(existingServiceDef.getId()).thenReturn(id);
-               when(_store.getServiceDef(id)).thenReturn(existingServiceDef);
-               
                String name = "aServiceDef";
                when(_serviceDef.getName()).thenReturn(name);
-               RangerServiceDef anotherExistingServiceDef = 
mock(RangerServiceDef.class);
-               Long anotherId = 49L;
-               when(anotherExistingServiceDef.getId()).thenReturn(anotherId);
-               
when(_store.getServiceDefByName(name)).thenReturn(anotherExistingServiceDef);
+               when(_store.getServiceDefByName(name)).thenReturn(null); // no 
service with the new name (we are updating the name to a unique value)
+               assertTrue(_validator.isValidServiceDefName(name, id, 
Action.UPDATE, _failures));
+               assertTrue(_failures.isEmpty());
+
+               RangerServiceDef existingServiceDef = 
mock(RangerServiceDef.class);
+               when(existingServiceDef.getId()).thenReturn(id);
+               when(existingServiceDef.getName()).thenReturn(name);
+               
when(_store.getServiceDefByName(name)).thenReturn(existingServiceDef);
+               assertTrue(_validator.isValidServiceDefName(name, id, 
Action.UPDATE, _failures));
+               assertTrue(_failures.isEmpty());
                
-               assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, 
_failures));
+               long anotherId = 49;
+               when(existingServiceDef.getId()).thenReturn(anotherId);
+               assertFalse(_validator.isValidServiceDefName(name, id, 
Action.UPDATE, _failures));
                _utils.checkFailureForSemanticError(_failures, "id/name");
        }
 
        final Object[][] accessTypes_bad_unknownType = new Object[][] {
-                       { "read",  null },                                // 
ok, null implied grants
-                       { "write", new String[] {   } },                  // 
ok, empty implied grants
-                       { "admin", new String[] { "ReaD",  "execute" } }  // 
non-existent access type (execute), read is good (case should not matter)
+                       { 1L, "read",  null },                                
// ok, null implied grants
+                       { 1L, "write", new String[] {   }  },                 
// empty implied grants-ok, duplicate id
+                       { 3L, "admin", new String[] { "ReaD",  "execute" } }  
// non-existent access type (execute), read is good (case should not matter)
        };
 
        final Object[][] accessTypes_bad_selfReference = new Object[][] {
-                       { "read",  null },                                // 
ok, null implied grants
-                       { "write", new String[] {   } },                  // 
ok, empty implied grants
-                       { "admin", new String[] { "write", "admin" } }  // 
non-existent access type (execute)
+                       { 1L, "read",  null },                              // 
ok, null implied grants
+                       { 2L, "write", new String[] {   } },                // 
ok, empty implied grants
+                       { 3L, "admin", new String[] { "write", "admin" } }  // 
non-existent access type (execute)
        };
 
        @Test
@@ -204,16 +218,17 @@ public class TestRangerServiceDefValidator {
        
        @Test
        public final void test_isValidAccessTypes_failures() {
-               // sending in empty null access type defs is ok
-               assertTrue(_validator.isValidAccessTypes(null, _failures));
-               assertTrue(_failures.isEmpty());
+               // null or empty access type defs
+               List<RangerAccessTypeDef> accessTypeDefs = null;
+               _failures.clear(); 
assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
+               _utils.checkFailureForMissingValue(_failures, "access types");
                
-               List<RangerAccessTypeDef> input = new 
ArrayList<RangerAccessTypeDef>();
-               _failures.clear(); 
assertTrue(_validator.isValidAccessTypes(input, _failures));
-               assertTrue(_failures.isEmpty());
+               accessTypeDefs = new ArrayList<RangerAccessTypeDef>();
+               _failures.clear(); 
assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
+               _utils.checkFailureForMissingValue(_failures, "access types");
 
                // null/empty access types
-               List<RangerAccessTypeDef> accessTypeDefs = 
_utils.createAccessTypeDefs(new String[] { null, "", "               " });
+               accessTypeDefs = _utils.createAccessTypeDefs(new String[] { 
null, "", "         " });
                _failures.clear(); 
assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
                _utils.checkFailureForMissingValue(_failures, "access type 
name");
                
@@ -231,6 +246,7 @@ public class TestRangerServiceDefValidator {
                accessTypeDefs = 
_utils.createAccessTypeDefs(accessTypes_bad_unknownType);
                _failures.clear(); 
assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
                _utils.checkFailureForSemanticError(_failures, "implied 
grants", "execute");
+               _utils.checkFailureForSemanticError(_failures, "access type 
id", "1"); // id 1 is duplicated
                
                // access type with implied grant referring to itself
                accessTypeDefs = 
_utils.createAccessTypeDefs(accessTypes_bad_selfReference);
@@ -238,34 +254,33 @@ public class TestRangerServiceDefValidator {
                _utils.checkFailureForSemanticError(_failures, "implied 
grants", "admin");
        }
        
-       final Map<String, String[]> enums_bad_enumName_null = ImmutableMap.of(
-                       "authentication-type", new String[] { "simple", 
"kerberos" },
-                       "time-unit", new String[] { "day", "hour", "minute" },
-                       "null", new String[] { "foo", "bar", "tar" } // null 
enum-name -- "null" is a special value that leads to a null enum name
-       );
-       
-       final Map<String, String[]> enums_bad_enumName_blank = ImmutableMap.of(
-                       "authentication-type", new String[] { "simple", 
"kerberos" },
-                       "time-unit", new String[] { "day", "hour", "minute" },
-                       "  ", new String[] { "foo", "bar", "tar" } // enum name 
is all spaces
-       );
+       final Object[][] enums_bad_enumName_null = new Object[][] {
+               //  { id, enum-name,             enum-values }
+                       { 1L, "authentication-type", new String[] { "simple", 
"kerberos" } },
+                       { 2L, "time-unit", new String[] { "day", "hour", 
"minute" } },
+                       { 3L, null, new String[] { "foo", "bar", "tar" } }, // 
null enum-name
+       };
        
-       final Map<String, String[]> enums_bad_Elements_empty = ImmutableMap.of(
-                       "authentication-type", new String[] { "simple", 
"kerberos" },
-                       "time-unit", new String[] { "day", "hour", "minute" },
-                       "anEnum", new String[] { } // enum elements collection 
is empty
-       );
+       final Object[][] enums_bad_enumName_blank = new Object[][] {
+                       //  { id, enum-name,             enum-values }
+                       { 1L, "authentication-type", new String[] { "simple", 
"kerberos" } },
+                       { 1L, "time-unit", new String[] { "day", "hour", 
"minute" } },
+                       { 2L, "  ", new String[] { "foo", "bar", "tar" } }, // 
enum name is all spaces
+       };
        
-       final Map<String, String[]> enums_bad_enumName_duplicate_exact = 
ImmutableMap.of(
-                       "authentication-type", new String[] { "simple", 
"kerberos" },
-                       "time-unit", new String[] { "day", "hour", "minute" }
-       );
+       final Object[][] enums_bad_Elements_empty = new Object[][] {
+                       //  { id, enum-name,             enum-values }
+                       { null, "authentication-type", new String[] { "simple", 
"kerberos" } }, // null id
+                       { 1L, "time-unit", new String[] { "day", "hour", 
"minute" } },
+                       { 2L, "anEnum", new String[] { } }, // enum elements 
collection is empty
+       };
        
-       final Map<String, String[]> enums_bad_enumName_duplicate_differentCase 
= ImmutableMap.of(
-                       "authentication-type", new String[] { "simple", 
"kerberos" },
-                       "time-unit", new String[] { "day", "hour", "minute" },
-                       "Authentication-Type", new String[] { } // duplicate 
enum-name different in case
-       );
+       final Object[][] enums_bad_enumName_duplicate_differentCase = new 
Object[][] {
+                       //  { id, enum-name,             enum-values }
+                       { 1L, "authentication-type", new String[] { "simple", 
"kerberos" } }, 
+                       { 1L, "time-unit", new String[] { "day", "hour", 
"minute" } },
+                       { 1L, "Authentication-Type", new String[] { } },// 
duplicate enum-name different in case
+       };
        
        @Test
        public final void test_isValidEnums_happyPath() {
@@ -290,6 +305,7 @@ public class TestRangerServiceDefValidator {
                input = _utils.createEnumDefs(enums_bad_enumName_blank);
                _failures.clear(); assertFalse(_validator.isValidEnums(input, 
_failures));
                _utils.checkFailureForMissingValue(_failures, "enum def name");
+               _utils.checkFailureForSemanticError(_failures, "enum def id", 
"1");
                
                // enum elements collection should not be null or empty
                input = _utils.createEnumDefs(enums_good);
@@ -303,6 +319,7 @@ public class TestRangerServiceDefValidator {
                input = _utils.createEnumDefs(enums_bad_Elements_empty);
                _failures.clear(); assertFalse(_validator.isValidEnums(input, 
_failures));
                _utils.checkFailureForMissingValue(_failures, "enum values", 
"anEnum");
+               _utils.checkFailureForMissingValue(_failures, "enum def id");
        
                // enum names should be distinct -- exact match
                input = _utils.createEnumDefs(enums_good);
@@ -359,11 +376,144 @@ public class TestRangerServiceDefValidator {
                // element names should be distinct - case insensitive
                input = _utils.createEnumElementDefs(new String[] { "simple", 
"kerberos", "kerberos" }); // duplicate name - exact match
                _failures.clear(); 
assertFalse(_validator.isValidEnumElements(input, _failures, "anEnum"));
-               _utils.checkFailureForSemanticError(_failures, "enum element 
name", "anEnum");
+               _utils.checkFailureForSemanticError(_failures, "enum element 
name", "kerberos");
                
                input = _utils.createEnumElementDefs(new String[] { "simple", 
"kerberos", "kErbErOs" }); // duplicate name - different case
                _failures.clear(); 
assertFalse(_validator.isValidEnumElements(input, _failures, "anEnum"));
-               _utils.checkFailureForSemanticError(_failures, "enum element 
name", "anEnum");
+               _utils.checkFailureForSemanticError(_failures, "enum element 
name", "kErbErOs");
+       }
+
+       Object[][] invalidResources = new Object[][] {
+               //  { id,   level,      name }
+                       { null, null,       null }, // everything is null
+                       { 1L,     -10, "database" }, // -ve value for level is 
ok
+                       { 1L,      10,    "table" }, // id is duplicate
+                       { 2L,     -10, "DataBase" }, // (in different case) but 
name and level are duplicate
+                       { 3L,      30,       "  " } // Name is all whitespace
+       };
+
+       @Test
+       public final void test_isValidResources() {
+               // null/empty resources are an error
+               when(_serviceDef.getResources()).thenReturn(null);
+               _failures.clear(); 
assertFalse(_validator.isValidResources(_serviceDef, _failures));
+               _utils.checkFailureForMissingValue(_failures, "resources");
+               
+               List<RangerResourceDef> resources = new 
ArrayList<RangerResourceDef>();
+               when(_serviceDef.getResources()).thenReturn(resources);
+               _failures.clear(); 
assertFalse(_validator.isValidResources(_serviceDef, _failures));
+               _utils.checkFailureForMissingValue(_failures, "resources");
+               
+               
resources.addAll(_utils.createResourceDefsWithIds(invalidResources));
+               _failures.clear(); 
assertFalse(_validator.isValidResources(_serviceDef, _failures));
+               _utils.checkFailureForMissingValue(_failures, "resource name");
+               _utils.checkFailureForMissingValue(_failures, "resource id");
+               _utils.checkFailureForSemanticError(_failures, "resource id", 
"1"); // id 1 is duplicate
+               _utils.checkFailureForSemanticError(_failures, "resource name", 
"DataBase");
+       }
+       
+       @Test
+       public final void test_isValidResourceGraph() {
+               Object[][] data_bad = new Object[][] {
+                       //  { name,  excludesSupported, recursiveSupported, 
mandatory, reg-exp, parent-level, level }
+                               { "db",            null, null, null, null, "" , 
            10 },
+                               { "table",         null, null, null, null, 
"db",            20 },   // same as db's level
+                               { "column-family", null, null, null, null, 
"table",         null }, // level is null!
+                               { "column",        null, null, null, null, 
"column-family", 20 },   // level is duplicate for [db->table->column-family-> 
column] hierarchy 
+                               { "udf",           null, null, null, null, 
"db",            10 },   // udf's id conflicts with that of db in the [db->udf] 
hierarchy
+               };
+               
+               List<RangerResourceDef> resourceDefs = 
_utils.createResourceDefs(data_bad);
+               when(_serviceDef.getResources()).thenReturn(resourceDefs);
+               when(_serviceDef.getName()).thenReturn("service-name");
+               when(_serviceDef.getUpdateTime()).thenReturn(new Date());
+               
+               _failures.clear(); 
assertFalse(_validator.isValidResourceGraph(_serviceDef, _failures));
+               _utils.checkFailureForMissingValue(_failures, "resource level");
+               _utils.checkFailureForSemanticError(_failures, "resource 
level", "20"); // level 20 is duplicate for 1 hierarchy
+               _utils.checkFailureForSemanticError(_failures, "resource 
level", "10"); // level 10 is duplicate for another hierarchy
+               
+               Object[][] data_good = new Object[][] {
+                       //  { name,  excludesSupported, recursiveSupported, 
mandatory, reg-exp, parent-level, level }
+                               { "db",     null, null, null, null, "" ,     
-10 }, // -ve level is ok
+                               { "table",  null, null, null, null, "db",    0 
},   // 0 level is ok
+                               { "column", null, null, null, null, "table", 10 
},  // level is null!
+                               { "udf",    null, null, null, null, "db",    0 
},   // should not conflict as it belong to a different hierarchy
+               };
+               resourceDefs = _utils.createResourceDefs(data_good);
+               when(_serviceDef.getResources()).thenReturn(resourceDefs);
+               _failures.clear(); 
assertTrue(_validator.isValidResourceGraph(_serviceDef, _failures));
+               assertTrue(_failures.isEmpty());
+       }
+       
+       @Test
+       public final void test_isValidResources_happyPath() {
+               Object[][] data = new Object[][] {
+                       //  { id,   level,      name }
+                               { 1L,     -10, "database" }, // -ve value for 
level
+                               { 3L,       0,    "table" }, // it is ok for id 
and level to skip values
+                               { 5L,      10,   "column" }, // it is ok for 
level to skip values
+               };
+               List<RangerResourceDef> resources = 
_utils.createResourceDefsWithIds(data);
+               when(_serviceDef.getResources()).thenReturn(resources);
+               assertTrue(_validator.isValidResources(_serviceDef, _failures));
+               assertTrue(_failures.isEmpty());
+       }
+
+       @Test
+       public final void test_isValidConfigs_failures() {
+               assertTrue(_validator.isValidConfigs(null, null, _failures));
+               assertTrue(_failures.isEmpty());
+               
+               Object[][] config_def_data_bad = new Object[][] {
+                       //  { id,  name, type, subtype, default-value } 
+                               { null, null, "" }, // id and name both null, 
type is empty
+                               { 1L, "security", "blah" }, // bad type for 
service def
+                               { 1L, "port", "int" }, // duplicate id
+                               { 2L, "security", "string" }, // duplicate name 
+                               { 3L, "timeout", "enum", "units", null }, // , 
sub-type (units) is not among known enum types 
+                               { 4L, "auth", "enum", "authentication-type", 
"dimple" }, // default value is not among known values for the enum (sub-type) 
+               };
+               
+               List<RangerServiceConfigDef> configs = 
_utils.createServiceDefConfigs(config_def_data_bad);
+               List<RangerEnumDef> enumDefs = 
_utils.createEnumDefs(enums_good);
+               assertFalse(_validator.isValidConfigs(configs, enumDefs, 
_failures));
+               _utils.checkFailureForMissingValue(_failures, "config def 
name");
+               _utils.checkFailureForMissingValue(_failures, "config def id");
+               _utils.checkFailureForMissingValue(_failures, "config def 
type");
+               _utils.checkFailureForSemanticError(_failures, "config def 
name", "security"); // there were two configs with same name as security
+               _utils.checkFailureForSemanticError(_failures, "config def id", 
"1"); // a config with duplicate had id of 1
+               _utils.checkFailureForSemanticError(_failures, "config def 
type", "security"); // type for config security was invalid
+               _utils.checkFailureForSemanticError(_failures, "config def 
subtype", "timeout"); // type for config security was invalid
+               _utils.checkFailureForSemanticError(_failures, "config def 
default value", "auth"); // type for config security was invalid
+       }
+
+       @Test
+       public final void test_isValidPolicyConditions() {
+               // null/empty policy conditions are ok
+               assertTrue(_validator.isValidPolicyConditions(null, _failures));
+               assertTrue(_failures.isEmpty());
+               List<RangerPolicyConditionDef> conditionDefs = new 
ArrayList<RangerServiceDef.RangerPolicyConditionDef>();
+               assertTrue(_validator.isValidPolicyConditions(conditionDefs, 
_failures));
+               assertTrue(_failures.isEmpty());
+               
+               Object[][] policyCondition_data = {
+                       //  { id, name, evaluator }     
+                               { null, null, null}, // everything null!
+                               { 1L, "condition-1", null }, // missing 
evaluator
+                               { 1L, "condition-2", "" }, // duplicate id, 
missing evaluator
+                               { 2L, "condition-1", "com.evaluator" }, // 
duplicate name
+               };
+               
+               
conditionDefs.addAll(_utils.createPolicyConditionDefs(policyCondition_data));
+               _failures.clear(); 
assertFalse(_validator.isValidPolicyConditions(conditionDefs, _failures));
+               _utils.checkFailureForMissingValue(_failures, "policy condition 
def id");
+               _utils.checkFailureForMissingValue(_failures, "policy condition 
def name");
+               _utils.checkFailureForMissingValue(_failures, "policy condition 
def evaluator");
+               _utils.checkFailureForSemanticError(_failures, "policy 
condition def id", "1");
+               _utils.checkFailureForSemanticError(_failures, "policy 
condition def name", "condition-1");
+               _utils.checkFailureForMissingValue(_failures, "policy condition 
def evaluator", "condition-2");
+               _utils.checkFailureForMissingValue(_failures, "policy condition 
def evaluator", "condition-1");
        }
        
        private ValidationTestUtils _utils = new ValidationTestUtils();

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
index dd8485e..6c20f0d 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
@@ -214,14 +214,14 @@ public class TestRangerServiceValidator {
                _validator = new RangerServiceValidator(_store);
                _failures.clear(); assertFalse(_validator.isValid((Long)null, 
Action.DELETE, _failures));
                _utils.checkFailureForMissingValue(_failures, "id");
-               // if service with that id does not exist then that, too, is a 
failure
+               // if service with that id does not exist then that, is ok 
because delete is idempotent
                when(_store.getService(1L)).thenReturn(null);
                when(_store.getService(2L)).thenThrow(new Exception());
-               _failures.clear(); assertFalse(_validator.isValid(1L, 
Action.DELETE, _failures));
-               _utils.checkFailureForSemanticError(_failures, "id");
+               _failures.clear(); assertTrue(_validator.isValid(1L, 
Action.DELETE, _failures));
+               assertTrue(_failures.isEmpty());
 
-               _failures.clear(); assertFalse(_validator.isValid(2L, 
Action.DELETE, _failures));
-               _utils.checkFailureForSemanticError(_failures, "id");
+               _failures.clear(); assertTrue(_validator.isValid(2L, 
Action.DELETE, _failures));
+               assertTrue(_failures.isEmpty());
        }
        
        @Test

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7ac04f9d/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
----------------------------------------------------------------------
diff --git 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
index 46f488e..01c0e6d 100644
--- 
a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
+++ 
b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
@@ -34,6 +34,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -77,6 +78,7 @@ public class TestRangerValidator {
        public void before() {
                _store = mock(ServiceStore.class);
                _validator = new RangerValidatorForTest(_store);
+               _failures = new ArrayList<ValidationFailureDetails>();
        }
 
        @Test
@@ -506,7 +508,76 @@ public class TestRangerValidator {
                assertTrue(result.contains(" d "));
        }
        
+       @Test
+       public void test_isValid_string() {
+               String fieldName = "value-field-Name";
+               String collectionName = "value-collection-Name";
+               Set<String> alreadySeen = new HashSet<String>();
+               // null/empty string value is invalid 
+               for (String value : new String[] { null, "", "  " }) {
+                       assertFalse(_validator.isUnique(value, alreadySeen, 
fieldName, collectionName, _failures));
+                       _utils.checkFailureForMissingValue(_failures, 
fieldName);
+               }
+               // value should not have been seen so far.
+               String value = "blah";
+               _failures.clear(); assertTrue(_validator.isUnique(value, 
alreadySeen, fieldName, collectionName, _failures));
+               assertTrue(_failures.isEmpty());
+               assertTrue(alreadySeen.contains(value));
+
+               // since "blah" has already been seen doing this test again 
should fail
+               _failures.clear(); assertFalse(_validator.isUnique(value, 
alreadySeen, fieldName, collectionName, _failures));
+               _utils.checkFailureForSemanticError(_failures, fieldName, 
value);
+               
+               // not see check is done in a case-insenstive manner
+               value = "bLaH";
+               _failures.clear(); assertFalse(_validator.isUnique(value, 
alreadySeen, fieldName, collectionName, _failures));
+               _utils.checkFailureForSemanticError(_failures, fieldName, 
value);
+       }
+       
+       @Test
+       public void test_isValid_long() {
+               String fieldName = "field-Name";
+               String collectionName = "field-collection-Name";
+               Set<Long> alreadySeen = new HashSet<Long>();
+               Long value = null;
+               // null value is invalid 
+               assertFalse(_validator.isUnique(value, alreadySeen, fieldName, 
collectionName, _failures));
+               _utils.checkFailureForMissingValue(_failures, fieldName);
+
+               // value should not have been seen so far.
+               value = 7L;
+               _failures.clear(); assertTrue(_validator.isUnique(value, 
alreadySeen, fieldName, collectionName, _failures));
+               assertTrue(_failures.isEmpty());
+               assertTrue(alreadySeen.contains(value));
+
+               // since 7L has already been seen doing this test again should 
fail
+               _failures.clear(); assertFalse(_validator.isUnique(value, 
alreadySeen, fieldName, collectionName, _failures));
+               _utils.checkFailureForSemanticError(_failures, fieldName, 
value.toString());
+       }
+       
+       @Test
+       public void test_isValid_integer() {
+               String fieldName = "field-Name";
+               String collectionName = "field-collection-Name";
+               Set<Integer> alreadySeen = new HashSet<Integer>();
+               Integer value = null;
+               // null value is invalid 
+               assertFalse(_validator.isUnique(value, alreadySeen, fieldName, 
collectionName, _failures));
+               _utils.checkFailureForMissingValue(_failures, fieldName);
+
+               // value should not have been seen so far.
+               value = 49;
+               _failures.clear(); assertTrue(_validator.isUnique(value, 
alreadySeen, fieldName, collectionName, _failures));
+               assertTrue(_failures.isEmpty());
+               assertTrue(alreadySeen.contains(value));
+
+               // since 7L has already been seen doing this test again should 
fail
+               _failures.clear(); assertFalse(_validator.isUnique(value, 
alreadySeen, fieldName, collectionName, _failures));
+               _utils.checkFailureForSemanticError(_failures, fieldName, 
value.toString());
+       }
+       
        private RangerValidatorForTest _validator;
        private ServiceStore _store;
        private ValidationTestUtils _utils = new ValidationTestUtils();
+       private List<ValidationFailureDetails> _failures;
 }


Reply via email to