You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2015/07/30 23:39:30 UTC

[1/7] incubator-ranger git commit: RANGER-580 Coprocessors must explictly declare the CoprocessorService else it won't get registered in all contexts

Repository: incubator-ranger
Updated Branches:
  refs/heads/tag-policy a87442a66 -> fcf0a6efd


RANGER-580 Coprocessors must explictly declare the CoprocessorService else it won't get registered in all contexts

Signed-off-by: Madhan Neethiraj <ma...@apache.org>


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

Branch: refs/heads/tag-policy
Commit: 21fe732a8e31fda55832cd16bcaf96bf7e4b6755
Parents: f34eb3d
Author: Alok Lal <al...@hortonworks.com>
Authored: Sat Jul 25 22:14:50 2015 -0700
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Sun Jul 26 23:06:12 2015 -0700

----------------------------------------------------------------------
 .../authorization/hbase/XaSecureAuthorizationCoprocessor.java    | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/21fe732a/hbase-agent/src/main/java/com/xasecure/authorization/hbase/XaSecureAuthorizationCoprocessor.java
----------------------------------------------------------------------
diff --git a/hbase-agent/src/main/java/com/xasecure/authorization/hbase/XaSecureAuthorizationCoprocessor.java b/hbase-agent/src/main/java/com/xasecure/authorization/hbase/XaSecureAuthorizationCoprocessor.java
index 2a4f440..574a07c 100644
--- a/hbase-agent/src/main/java/com/xasecure/authorization/hbase/XaSecureAuthorizationCoprocessor.java
+++ b/hbase-agent/src/main/java/com/xasecure/authorization/hbase/XaSecureAuthorizationCoprocessor.java
@@ -18,6 +18,8 @@
  */
 package com.xasecure.authorization.hbase;
 
+import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
 import org.apache.ranger.authorization.hbase.RangerAuthorizationCoprocessor;
 
 /**
@@ -28,6 +30,6 @@ import org.apache.ranger.authorization.hbase.RangerAuthorizationCoprocessor;
  * This class is final because if one needs to customize coprocessor it is expected that RangerAuthorizationCoprocessor would be modified/extended as that is
  * the "real" coprocessor!  This class, hence, should NEVER be more than an EMPTY shell!
  */
-public final class XaSecureAuthorizationCoprocessor extends RangerAuthorizationCoprocessor {
+public final class XaSecureAuthorizationCoprocessor extends RangerAuthorizationCoprocessor implements AccessControlProtos.AccessControlService.Interface, CoprocessorService {
 
 }


[6/7] incubator-ranger git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-ranger

Posted by ma...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-ranger


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

Branch: refs/heads/tag-policy
Commit: d8e3225974fb2b7b47ca5d80bc691e3bdd4b09f6
Parents: 39fc361 f80cb1d
Author: rmani <rm...@hortonworks.com>
Authored: Thu Jul 30 14:27:20 2015 -0700
Committer: rmani <rm...@hortonworks.com>
Committed: Thu Jul 30 14:27:20 2015 -0700

----------------------------------------------------------------------
 .../model/validation/RangerPolicyValidator.java | 265 +++++++++++++++----
 .../model/validation/RangerValidator.java       |  42 ++-
 .../validation/ValidationFailureDetails.java    |  38 ++-
 .../ValidationFailureDetailsBuilder.java        |  19 +-
 .../validation/TestRangerPolicyValidator.java   |  22 +-
 .../TestValidationFailureDetails.java           |  58 ++++
 .../hbase/XaSecureAuthorizationCoprocessor.java |   4 +-
 7 files changed, 367 insertions(+), 81 deletions(-)
----------------------------------------------------------------------



[5/7] incubator-ranger git commit: RANGER-590: Escape spaces in the user and group names which are part of rest call uri in UserSync process

Posted by ma...@apache.org.
RANGER-590: Escape spaces in the user and group names which are part of rest call uri in UserSync process


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

Branch: refs/heads/tag-policy
Commit: 39fc361f817da1cfaca1d21ed679134414bfda93
Parents: dcd3ea0
Author: rmani <rm...@hortonworks.com>
Authored: Thu Jul 30 14:20:29 2015 -0700
Committer: rmani <rm...@hortonworks.com>
Committed: Thu Jul 30 14:20:29 2015 -0700

----------------------------------------------------------------------
 .../process/PolicyMgrUserGroupBuilder.java      | 45 +++++++++++++-------
 .../ranger/usersync/util/UserSyncUtil.java      | 39 +++++++++++++++++
 2 files changed, 68 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/39fc361f/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
----------------------------------------------------------------------
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
index 5768f55..78e3e72 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
@@ -63,7 +63,7 @@ import org.apache.ranger.unixusersync.model.XUserGroupInfo;
 import org.apache.ranger.unixusersync.model.XUserInfo;
 import org.apache.ranger.unixusersync.model.UserGroupInfo;
 import org.apache.ranger.usergroupsync.UserGroupSink;
-import org.mortbay.log.Log;
+import org.apache.ranger.usersync.util.UserSyncUtil;
 
 public class PolicyMgrUserGroupBuilder implements UserGroupSink {
 	
@@ -603,25 +603,38 @@ public class PolicyMgrUserGroupBuilder implements UserGroupSink {
 	}
 
 	private void delXUserGroupInfo(XUserInfo aUserInfo, XGroupInfo aGroupInfo) {
-		
-		Client c = getClient() ;
-	    
-	    String uri = PM_DEL_USER_GROUP_LINK_URI.replaceAll(Pattern.quote("${groupName}"), aGroupInfo.getName()).replaceAll(Pattern.quote("${userName}"), aUserInfo.getName()) ;
-	    
-	    WebResource r = c.resource(getURL(uri)) ;
-	    
-	    ClientResponse response = r.delete(ClientResponse.class) ;
-	    
-	    LOG.debug("RESPONSE: [" + response.toString() + "]") ;
+	
+		String groupName = aGroupInfo.getName();
 
-	    
-	    if (response.getStatus() == 200) {
-	    	delUserGroupFromList(aUserInfo, aGroupInfo) ;
-	    }
+		String userName  = aUserInfo.getName();
 		
+		try {
+
+			Client c = getClient() ;
+
+			String uri = PM_DEL_USER_GROUP_LINK_URI.replaceAll(Pattern.quote("${groupName}"), 
+					   UserSyncUtil.encodeURIParam(groupName)).replaceAll(Pattern.quote("${userName}"), UserSyncUtil.encodeURIParam(userName));
+
+			WebResource r = c.resource(getURL(uri)) ;
+
+		    ClientResponse response = r.delete(ClientResponse.class) ;
+
+		    if ( LOG.isDebugEnabled() ) {
+		    	LOG.debug("RESPONSE: [" + response.toString() + "]") ;
+		    }
+
+		    if (response.getStatus() == 200) {
+		    	delUserGroupFromList(aUserInfo, aGroupInfo) ;
+		    }
+ 
+		} catch (Exception e) {
+
+			LOG.warn( "ERROR: Unable to delete GROUP: " + groupName  + " from USER:" + userName , e) ;
+		}
+
 	}
 	
-	
+
 	private MUserInfo addMUser(String aUserName) {
 		
 		MUserInfo ret = null ;

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/39fc361f/ugsync/src/main/java/org/apache/ranger/usersync/util/UserSyncUtil.java
----------------------------------------------------------------------
diff --git a/ugsync/src/main/java/org/apache/ranger/usersync/util/UserSyncUtil.java b/ugsync/src/main/java/org/apache/ranger/usersync/util/UserSyncUtil.java
new file mode 100644
index 0000000..050419d
--- /dev/null
+++ b/ugsync/src/main/java/org/apache/ranger/usersync/util/UserSyncUtil.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.usersync.util;
+
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.httpclient.util.URIUtil;
+
+public class UserSyncUtil {
+
+	public static String encodeURIParam(String s) throws URIException {
+
+		String ret = null;
+
+		try {
+			ret = URIUtil.encodeQuery(s);
+		} catch (URIException e) {
+			throw e;
+		}
+
+		return ret;
+	}
+}


[4/7] incubator-ranger git commit: RANGER-560 Apache license added to test file

Posted by ma...@apache.org.
RANGER-560 Apache license added to test file

Signed-off-by: Madhan Neethiraj <ma...@apache.org>


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

Branch: refs/heads/tag-policy
Commit: f80cb1dd613a507927d7609cfa4fd391773d971d
Parents: e35e1c1
Author: Alok Lal <al...@hortonworks.com>
Authored: Tue Jul 28 23:22:31 2015 -0700
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Tue Jul 28 23:52:33 2015 -0700

----------------------------------------------------------------------
 .../validation/TestValidationFailureDetails.java | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/f80cb1dd/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java
----------------------------------------------------------------------
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java
index 0a758d1..cf929c6 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java
@@ -1,3 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 package org.apache.ranger.plugin.model.validation;
 
 import junit.framework.TestCase;


[7/7] incubator-ranger git commit: Merge branch 'master' into tag-policy

Posted by ma...@apache.org.
Merge branch 'master' into tag-policy


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

Branch: refs/heads/tag-policy
Commit: fcf0a6efd735bb59063c04b13bf67bb2ab9c722f
Parents: a87442a d8e3225
Author: Madhan Neethiraj <ma...@apache.org>
Authored: Thu Jul 30 14:28:26 2015 -0700
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Thu Jul 30 14:28:26 2015 -0700

----------------------------------------------------------------------
 .../model/validation/RangerPolicyValidator.java | 265 +++++++++++++++----
 .../model/validation/RangerValidator.java       |  42 ++-
 .../validation/ValidationFailureDetails.java    |  38 ++-
 .../ValidationFailureDetailsBuilder.java        |  19 +-
 .../validation/TestRangerPolicyValidator.java   |  22 +-
 .../TestValidationFailureDetails.java           |  58 ++++
 .../ranger/unixusersync/model/XUserInfo.java    |  21 +-
 .../process/PolicyMgrUserGroupBuilder.java      |  45 ++--
 .../ranger/usersync/util/UserSyncUtil.java      |  39 +++
 9 files changed, 451 insertions(+), 98 deletions(-)
----------------------------------------------------------------------



[2/7] incubator-ranger git commit: BUG-41359 : UserSync process performance issue due to user group cache inconsistency

Posted by ma...@apache.org.
BUG-41359 : UserSync process performance issue due to user group cache inconsistency


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

Branch: refs/heads/tag-policy
Commit: dcd3ea0d8429516c7ef20eddce095fa9f7f9a9a1
Parents: f34eb3d
Author: rmani <rm...@hortonworks.com>
Authored: Tue Jul 28 10:54:49 2015 -0700
Committer: rmani <rm...@hortonworks.com>
Committed: Tue Jul 28 10:54:49 2015 -0700

----------------------------------------------------------------------
 .../ranger/unixusersync/model/XUserInfo.java    | 21 ++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/dcd3ea0d/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java
----------------------------------------------------------------------
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java
index 5a59fc7..ca22818 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/XUserInfo.java
@@ -27,7 +27,8 @@ public class XUserInfo {
 	private String name ;
 	private String 	description ;
 	
-	private List<String>  groups = new ArrayList<String>() ;
+	private List<String>  	groupNameList = new ArrayList<String>() ;
+	private List<String>    groupIdList   = new ArrayList<String>() ;
 	
 	public String getId() {
 		return id;
@@ -48,8 +49,24 @@ public class XUserInfo {
 		this.description = description;
 	}
 	
+	public void setGroupNameList(List<String> groupNameList) {
+		this.groupNameList = groupNameList;
+	}
+	
+	public List<String> getGroupNameList() {
+		return groupNameList;
+	}
+	
+	public List<String> getGroupIdList() {
+		return groupIdList;
+	}
+	
+	public void setGroupIdList(List<String> groupIdList) {
+		this.groupIdList = groupIdList;
+	}
+
 	public List<String> getGroups() {
-		return groups;
+		return groupNameList;
 	}
 	
 }


[3/7] incubator-ranger git commit: RANGER-560 Policy validation: user friendly error messages about validation failures

Posted by ma...@apache.org.
RANGER-560 Policy validation: user friendly error messages about validation failures

Signed-off-by: Madhan Neethiraj <ma...@apache.org>


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

Branch: refs/heads/tag-policy
Commit: e35e1c1916bc5ed9118d76469e643a0c9c41f1fb
Parents: 21fe732
Author: Alok Lal <al...@hortonworks.com>
Authored: Thu Jun 18 15:30:51 2015 -0700
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Tue Jul 28 22:27:45 2015 -0700

----------------------------------------------------------------------
 .../model/validation/RangerPolicyValidator.java | 265 +++++++++++++++----
 .../model/validation/RangerValidator.java       |  42 ++-
 .../validation/ValidationFailureDetails.java    |  38 ++-
 .../ValidationFailureDetailsBuilder.java        |  19 +-
 .../validation/TestRangerPolicyValidator.java   |  22 +-
 .../TestValidationFailureDetails.java           |  39 +++
 6 files changed, 345 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/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 84f750d..8817049 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
@@ -19,11 +19,7 @@
 
 package org.apache.ranger.plugin.model.validation;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
@@ -75,15 +71,18 @@ public class RangerPolicyValidator extends RangerValidator {
 
 		boolean valid = true;
 		if (action != Action.DELETE) {
-			failures.add(new ValidationFailureDetailsBuilder()
+			failures.add(new RangerPolicyValidationErrorBuilder()
 				.isAnInternalError()
-				.becauseOf("isValid(Long) is only supported for DELETE")
+				.becauseOf("method signature isValid(Long) is only supported for DELETE")
+				.errorCode(ErrorCode.InternalError_InvalidMethodInvocation)
 				.build());
 			valid = false;
 		} else if (id == null) {
-			failures.add(new ValidationFailureDetailsBuilder()
+			failures.add(new RangerPolicyValidationErrorBuilder()
+				.becauseOf("policy id was null/missing")
 				.field("id")
 				.isMissing()
+				.errorCode(ErrorCode.Missing_PolicyId_Delete)
 				.build());
 			valid = false;
 		} else if (getPolicy(id) == null) {
@@ -110,10 +109,11 @@ public class RangerPolicyValidator extends RangerValidator {
 		if (policy == null) {
 			String message = "policy object passed in was null";
 			LOG.debug(message);
-			failures.add(new ValidationFailureDetailsBuilder()
+			failures.add(new RangerPolicyValidationErrorBuilder()
 				.field("policy")
 				.isMissing()
 				.becauseOf(message)
+				.errorCode(ErrorCode.Missing_PolicyObject)
 				.build());
 			valid = false;
 		} else {
@@ -122,17 +122,19 @@ public class RangerPolicyValidator extends RangerValidator {
 				if (id == null) {
 					String message = "policy id was null/empty/blank"; 
 					LOG.debug(message);
-					failures.add(new ValidationFailureDetailsBuilder()
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("id")
 						.isMissing()
 						.becauseOf(message)
+						.errorCode(ErrorCode.Missing_PolicyId_Update)
 						.build());
 					valid = false;
 				} else if (getPolicy(id) == null) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("id")
 						.isSemanticallyIncorrect()
-						.becauseOf("no policy exists with id[" + id +"]")
+						.becauseOf("Invalid policy id provided for update: no policy found for id[" + id + "]")
+						.errorCode(ErrorCode.Invalid_PolicyId)
 						.build());
 					valid = false;
 				}
@@ -140,34 +142,39 @@ public class RangerPolicyValidator extends RangerValidator {
 			String policyName = policy.getName();
 			String serviceName = policy.getService();
 			if (StringUtils.isBlank(policyName)) {
-				String message = "policy name was null/empty/blank[" + policyName + "]"; 
+				String message = "policy name was null/empty/blank[" + policyName + "]";
 				LOG.debug(message);
-				failures.add(new ValidationFailureDetailsBuilder()
+				failures.add(new RangerPolicyValidationErrorBuilder()
 					.field("name")
 					.isMissing()
 					.becauseOf(message)
+					.errorCode(ErrorCode.Missing_PolicyName)
 					.build());
 				valid = false;
 			} else {
 				List<RangerPolicy> policies = getPolicies(serviceName, policyName);
 				if (CollectionUtils.isNotEmpty(policies)) {
 					if (policies.size() > 1) {
-						failures.add(new ValidationFailureDetailsBuilder()
+						failures.add(new RangerPolicyValidationErrorBuilder()
+							.field("name")
 							.isAnInternalError()
 							.becauseOf("multiple policies found with the name[" + policyName + "]")
+							.errorCode(ErrorCode.InternalError_Data_MultiplePoliciesSameName)
 							.build());
 						valid = false;
 					} else if (action == Action.CREATE) { // size == 1
-						failures.add(new ValidationFailureDetailsBuilder()
-							.field("name")
+						failures.add(new RangerPolicyValidationErrorBuilder()
+							.field("policy name")
 							.isSemanticallyIncorrect()
-							.becauseOf("policy already exists with name[" + policyName + "]; its id is[" + policies.iterator().next().getId() + "]")
+							.becauseOf("A policy already exists with name[" + policyName + "] for service[" + serviceName + "]; its id is[" + policies.iterator().next().getId() + "]")
+							.errorCode(ErrorCode.Duplicate_PolicyName_Create)
 							.build());
 						valid = false;
 					} else if (!policies.iterator().next().getId().equals(id)) { // size == 1 && action == UPDATE
-						failures.add(new ValidationFailureDetailsBuilder()
+						failures.add(new RangerPolicyValidationErrorBuilder()
 							.field("id/name")
 							.isSemanticallyIncorrect()
+							.errorCode(ErrorCode.Duplicate_PolicyName_Update)
 							.becauseOf("id/name conflict: another policy already exists with name[" + policyName + "], its id is[" + policies.iterator().next().getId() + "]")
 							.build());
 						valid = false;
@@ -177,19 +184,21 @@ public class RangerPolicyValidator extends RangerValidator {
 			RangerService service = null;
 			boolean serviceNameValid = false;
 			if (StringUtils.isBlank(serviceName)) {
-				failures.add(new ValidationFailureDetailsBuilder()
-				.field("service")
-				.isMissing()
-				.becauseOf("service name was null/empty/blank")
-				.build());
+				failures.add(new RangerPolicyValidationErrorBuilder()
+					.field("service name")
+					.isMissing()
+					.errorCode(ErrorCode.Missing_ServiceName)
+					.becauseOf("service name was null/empty/blank")
+					.build());
 				valid = false;
 			} else {
 				service = getService(serviceName);
 				if (service == null) {
-					failures.add(new ValidationFailureDetailsBuilder()
-						.field("service")
+					failures.add(new RangerPolicyValidationErrorBuilder()
+						.field("service name")
 						.isSemanticallyIncorrect()
-						.becauseOf("service does not exist")
+						.becauseOf("no service found with name[" + serviceName + "]")
+						.errorCode(ErrorCode.Invalid_ServiceName)
 						.build());
 					valid = false;
 				} else {
@@ -201,20 +210,24 @@ public class RangerPolicyValidator extends RangerValidator {
 			RangerServiceDef serviceDef = null;
 			String serviceDefName = null;
 			if (CollectionUtils.isEmpty(policyItems) && !isAuditEnabled) {
-				failures.add(new ValidationFailureDetailsBuilder()
+				failures.add(new RangerPolicyValidationErrorBuilder()
 					.field("policy items")
 					.isMissing()
 					.becauseOf("at least one policy item must be specified if audit isn't enabled")
+					.errorCode(ErrorCode.Missing_PolicyItems)
 					.build());
 				valid = false;
 			} else if (service != null) {
 				serviceDefName = service.getType();
 				serviceDef = getServiceDef(serviceDefName);
 				if (serviceDef == null) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					String message = String.format("Service def[%s] of policy's service[%s] does not exist!", serviceDefName, serviceName);
+					LOG.debug(message);
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("policy service def")
 						.isAnInternalError()
-						.becauseOf("Service def of policies service does not exist")
+						.becauseOf(message)
+						.errorCode(ErrorCode.InternalError_Data_MissingServiceDef)
 						.build());
 					valid = false;
 				} else {
@@ -273,10 +286,13 @@ public class RangerPolicyValidator extends RangerValidator {
 				RangerPolicy matchedPolicy = policies.iterator().next();
 				// there shouldn't be a matching policy for create.  During update only match should be to itself
 				if (action == Action.CREATE || (action == Action.UPDATE && (policies.size() > 1 || !matchedPolicy.getId().equals(policy.getId())))) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					String message = String.format("another policy[%s] with matching resources[%s] exists for service[%s]!",
+							matchedPolicy.getName(), matchedPolicy.getResources(), policy.getService());
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("resources")
 						.isSemanticallyIncorrect()
-						.becauseOf("found another policy[" + matchedPolicy.getName() + "] with matching resources[" + matchedPolicy.getResources() + "]!")
+						.becauseOf(message)
+						.errorCode(ErrorCode.Duplicate_PolicyResource)
 						.build());
 					valid = false;
 				}
@@ -312,12 +328,14 @@ public class RangerPolicyValidator extends RangerValidator {
 			Set<List<RangerResourceDef>> candidateHierarchies = filterHierarchies_hierarchyHasAllPolicyResources(policyResources, hierarchies, defHelper);
 			if (candidateHierarchies.isEmpty()) {
 				// let's build a helpful message for user
-				failures.add(new ValidationFailureDetailsBuilder()
-					.field("resources")
+				String message = String.format("policy resources %s are not compatible with any resource hierarchy for service def[%s]! Valid hierarchies are: %s",
+						policyResources.toString(), serviceDef.getName(), toStringHierarchies_all(hierarchies, defHelper));
+				failures.add(new RangerPolicyValidationErrorBuilder()
+					.field("policy resources")
 					.subField("incompatible")
 					.isSemanticallyIncorrect()
-					.becauseOf(String.format("policy resources [%s] were incompatible with all the hierarchies for this service defs! Valid hierarchies are: %s", 
-							policyResources.toString(), toStringHierarchies_all(hierarchies, defHelper)))
+					.becauseOf(message)
+					.errorCode(ErrorCode.Invalid_PolicyResource_NoCompatibleHierarchy)
 					.build());
 				valid = false;
 			} else {
@@ -331,11 +349,12 @@ public class RangerPolicyValidator extends RangerValidator {
 				 */
 				Set<List<RangerResourceDef>> validHierarchies = filterHierarchies_mandatoryResourcesSpecifiedInPolicy(policyResources, candidateHierarchies, defHelper);
 				if (validHierarchies.isEmpty()) {
-					failures.add(new ValidationFailureDetailsBuilder()
-						.field("resources")
+					failures.add(new RangerPolicyValidationErrorBuilder()
+						.field("policy resources")
 						.subField("missing mandatory")
 						.isSemanticallyIncorrect()
-						.becauseOf("policy is missing required resources. Mandatory fields of potential hierarchies are: " + toStringHierarchies_mandatory(candidateHierarchies, defHelper))
+						.errorCode(ErrorCode.Invalid_PolicyResource_MissingMandatory)
+						.becauseOf("policy is missing required resources. Mandatory resources of potential hierarchies are: " + toStringHierarchies_mandatory(candidateHierarchies, defHelper))
 						.build());
 					valid = false;
 				} else {
@@ -438,16 +457,18 @@ public class RangerPolicyValidator extends RangerValidator {
 			Map<String, RangerPolicyResource> policyResources = getPolicyResourceWithLowerCaseKeys(inputPolicyResources);
 			for (RangerResourceDef resourceDef : resourceDefs) {
 				if (resourceDef == null) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("resource-def")
 						.isAnInternalError()
+						.errorCode(ErrorCode.InternalError_Data_NullResourceDef)
 						.becauseOf("a resource-def on resource def collection of service-def[" + serviceDefName + "] was null")
 						.build());
 					valid = false;
 				} else if (StringUtils.isBlank(resourceDef.getName())) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("resource-def-name")
 						.isAnInternalError()
+						.errorCode(ErrorCode.InternalError_Data_NullResourceDefName)
 						.becauseOf("name of a resource-def on resource def collection of service-def[" + serviceDefName + "] was null")
 						.build());
 					valid = false;
@@ -462,31 +483,34 @@ public class RangerPolicyValidator extends RangerValidator {
 						boolean excludesSupported = Boolean.TRUE.equals(resourceDef.getExcludesSupported()); // could be null
 						boolean policyResourceIsExcludes = Boolean.TRUE.equals(policyResource.getIsExcludes()); // could be null
 						if (policyResourceIsExcludes && !excludesSupported) {
-							failures.add(new ValidationFailureDetailsBuilder()
+							failures.add(new RangerPolicyValidationErrorBuilder()
 								.field("isExcludes")
 								.subField(resourceName)
 								.isSemanticallyIncorrect()
+								.errorCode(ErrorCode.Invalid_Excludes_NotSupported)
 								.becauseOf("isExcludes specified as [" + policyResourceIsExcludes + "] for resource [" + resourceName + "] which doesn't support isExcludes")
 								.build());
 							valid = false;
 						}
 						if (policyResourceIsExcludes && !isAdmin) {
-							failures.add(new ValidationFailureDetailsBuilder()
+							failures.add(new RangerPolicyValidationErrorBuilder()
 								.field("isExcludes")
 								.subField("isAdmin")
 								.isSemanticallyIncorrect()
 								.becauseOf("isExcludes specified as [" + policyResourceIsExcludes + "] for resource [" + resourceName + "].  Insufficient permissions to create excludes policy.")
+								.errorCode(ErrorCode.Invalid_Excludes_RequiresAdmin)
 								.build());
 							valid = false;
 						}
 						boolean recursiveSupported = Boolean.TRUE.equals(resourceDef.getRecursiveSupported());
 						boolean policyIsRecursive = Boolean.TRUE.equals(policyResource.getIsRecursive());
 						if (policyIsRecursive && !recursiveSupported) {
-							failures.add(new ValidationFailureDetailsBuilder()
+							failures.add(new RangerPolicyValidationErrorBuilder()
 								.field("isRecursive")
 								.subField(resourceName)
 								.isSemanticallyIncorrect()
 								.becauseOf("isRecursive specified as [" + policyIsRecursive + "] for resource [" + resourceName + "] which doesn't support isRecursive")
+								.errorCode(ErrorCode.Invalid_Recursive_NotSupported)
 								.build());
 							valid = false;
 						}
@@ -517,11 +541,14 @@ public class RangerPolicyValidator extends RangerValidator {
 					if (StringUtils.isBlank(aValue)) {
 						LOG.debug("resource value was blank");
 					} else if (!aValue.matches(regEx)) {
-						failures.add(new ValidationFailureDetailsBuilder()
+						String message = String.format("Value[%s] of resource[%s] does not conform to the validation regex[%s] defined on the service-def[%s]", aValue, name, regEx, serviceDef.getName());
+						LOG.debug(message);
+						failures.add(new RangerPolicyValidationErrorBuilder()
 							.field("resource-values")
 							.subField(name)
 							.isSemanticallyIncorrect()
-							.becauseOf("resources value[" + aValue + "] does not match validation regex[" + regEx + "] defined on service-def[" + serviceDef.getName() + "]")
+							.becauseOf(message)
+							.errorCode(ErrorCode.Invalid_ResourceValue_RegEx)
 							.build());
 						valid = false;
 					}
@@ -546,10 +573,11 @@ public class RangerPolicyValidator extends RangerValidator {
 		} else {
 			for (RangerPolicyItem policyItem : policyItems) {
 				if (policyItem == null) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("policy item")
 						.isMissing()
 						.becauseOf("policy items object was null")
+						.errorCode(ErrorCode.InternalError_Data_NullPolicyItem)
 						.build());
 					valid = false;
 				} else {
@@ -577,10 +605,11 @@ public class RangerPolicyValidator extends RangerValidator {
 			// access items collection can't be empty (unless delegated admin is true) and should be otherwise valid
 			if (CollectionUtils.isEmpty(policyItem.getAccesses())) {
 				if (!Boolean.TRUE.equals(policyItem.getDelegateAdmin())) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("policy item accesses")
 						.isMissing()
 						.becauseOf("policy items accesses collection was null")
+						.errorCode(ErrorCode.Missing_PolicyItemAccesses)
 						.build());
 					valid = false;
 				} else {
@@ -591,10 +620,11 @@ public class RangerPolicyValidator extends RangerValidator {
 			}
 			// both users and user-groups collections can't be empty
 			if (CollectionUtils.isEmpty(policyItem.getUsers()) && CollectionUtils.isEmpty(policyItem.getGroups())) {
-				failures.add(new ValidationFailureDetailsBuilder()
+				failures.add(new RangerPolicyValidationErrorBuilder()
 					.field("policy item users/user-groups")
 					.isMissing()
 					.becauseOf("both users and user-groups collections on the policy item were null/empty")
+					.errorCode(ErrorCode.Missing_PolicyItemUserGroup)
 					.build());
 				valid = false;
 			}
@@ -618,10 +648,11 @@ public class RangerPolicyValidator extends RangerValidator {
 			Set<String> accessTypes = getAccessTypes(serviceDef);
 			for (RangerPolicyItemAccess access : accesses) {
 				if (access == null) {
-					failures.add(new ValidationFailureDetailsBuilder()
+					failures.add(new RangerPolicyValidationErrorBuilder()
 						.field("policy item access")
 						.isMissing()
 						.becauseOf("policy items access object was null")
+						.errorCode(ErrorCode.InternalError_Data_NullPolicyItemAccess)
 						.build());
 					valid = false;
 				} else {
@@ -650,19 +681,21 @@ public class RangerPolicyValidator extends RangerValidator {
 		} else {
 			String accessType = access.getType();
 			if (StringUtils.isBlank(accessType)) {
-				failures.add(new ValidationFailureDetailsBuilder()
+				failures.add(new RangerPolicyValidationErrorBuilder()
 					.field("policy item access type")
 					.isMissing()
 					.becauseOf("policy items access type's name was null/empty/blank")
+					.errorCode(ErrorCode.Missing_PolicyItemAccessType)
 					.build());
 				valid = false;
 			} else if (!accessTypes.contains(accessType.toLowerCase())) {
 				String message = String.format("access type[%s] not among valid types for service[%s]", accessType, accessTypes);
 				LOG.debug(message);
-				failures.add(new ValidationFailureDetailsBuilder()
+				failures.add(new RangerPolicyValidationErrorBuilder()
 					.field("policy item access type")
 					.isSemanticallyIncorrect()
 					.becauseOf(message)
+					.errorCode(ErrorCode.Invalid_PolicyItemAccessType)
 					.build());
 				valid = false;
 			}
@@ -671,10 +704,11 @@ public class RangerPolicyValidator extends RangerValidator {
 			if (isAllowed != null && isAllowed == false) {
 				String message = "access type is set to deny.  Currently deny access types are not supported.";
 				LOG.debug(message);
-				failures.add(new ValidationFailureDetailsBuilder()
+				failures.add(new RangerPolicyValidationErrorBuilder()
 					.field("policy item access type allowed")
 					.isSemanticallyIncorrect()
 					.becauseOf(message)
+					.errorCode(ErrorCode.Invalid_PolicyItemAccessType_Deny)
 					.build());
 				valid = false;
 			}
@@ -685,4 +719,129 @@ public class RangerPolicyValidator extends RangerValidator {
 		}
 		return valid;
 	}
+
+	static class RangerPolicyValidationErrorBuilder extends ValidationFailureDetailsBuilder {
+
+		@Override
+		ValidationFailureDetails build() {
+			return new RangerPolicyValidationFailure(_errorCode, _fieldName, _subFieldName, _missing, _semanticError, _internalError, _reason);
+		}
+	}
+
+	static class RangerPolicyValidationFailure extends  ValidationFailureDetails {
+
+		public RangerPolicyValidationFailure(int errorCode, String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) {
+			super(errorCode, fieldName, subFieldName, missing, semanticError, internalError, reason);
+		}
+
+		// TODO remove and move to baseclass when all 3 move to new message framework
+		@Override
+		public String toString() {
+			LOG.debug("RangerPolicyValidationFailure.toString");
+			String result = null;
+			if (_ErrorCode2MessageTemplate.containsKey(_errorCode)) {
+				Integer templateId = _ErrorCode2MessageTemplate.get(_errorCode);
+				if (templateId != null && _TemplateId2Template.containsKey(templateId)) {
+					String messageTemplate = _TemplateId2Template.get(templateId);
+					if (StringUtils.isNotBlank(messageTemplate)) {
+						// in the worst case result should be at least same as the messageTemplate which we know is not blank
+						result = substituteVariables(messageTemplate);
+					} else {
+						LOG.warn("Internal error: Message template string for template [" + templateId + "] was empty!");
+					}
+				} else {
+					LOG.warn("Internal error: template id for error code [" + templateId + "] was null or template id to message map did not comtain the templateid");
+				}
+			} else {
+				LOG.warn("Internal error: error code [" + _errorCode + "] not found in errorcode to message template map");
+			}
+			if (result == null) {
+				result = super.toString();
+			}
+			return "Policy validation failure: " + result;
+		}
+	}
+
+	static class ErrorCode {
+		public static final int InternalError_InvalidMethodInvocation 					= 1001;
+		public static final int Missing_PolicyId_Delete 								= 1002;
+		public static final int Missing_PolicyObject 									= 1003;
+		public static final int Missing_PolicyId_Update 								= 1004;
+		public static final int Invalid_PolicyId 										= 1005;
+		public static final int Missing_PolicyName 										= 1006;
+		public static final int InternalError_Data_MultiplePoliciesSameName 			= 1007;
+		public static final int Duplicate_PolicyName_Create 							= 1008;
+		public static final int Duplicate_PolicyName_Update 							= 1009;
+		public static final int Missing_ServiceName 									= 1010;
+		public static final int Invalid_ServiceName 									= 1011;
+		public static final int Missing_PolicyItems 									= 1012;
+		public static final int InternalError_Data_MissingServiceDef					= 1013;
+		public static final int Duplicate_PolicyResource 								= 1014;
+		public static final int Invalid_PolicyResource_NoCompatibleHierarchy 			= 1015;
+		public static final int Invalid_PolicyResource_MissingMandatory					= 1016;
+		public static final int InternalError_Data_NullResourceDef						= 1017;
+		public static final int InternalError_Data_NullResourceDefName					= 1018;
+		public static final int Invalid_Excludes_NotSupported							= 1019;
+		public static final int Invalid_Excludes_RequiresAdmin							= 1020;
+		public static final int Invalid_Recursive_NotSupported							= 1021;
+		public static final int Invalid_ResourceValue_RegEx								= 1022;
+		public static final int InternalError_Data_NullPolicyItem 						= 1023;
+		public static final int Missing_PolicyItemAccesses 								= 1024;
+		public static final int Missing_PolicyItemUserGroup 							= 1025;
+		public static final int InternalError_Data_NullPolicyItemAccess					= 1026;
+		public static final int Missing_PolicyItemAccessType							= 1027;
+		public static final int Invalid_PolicyItemAccessType							= 1028;
+		public static final int Invalid_PolicyItemAccessType_Deny						= 1029;
+	}
+	static class MessageId {
+		public static final int InternalError 					= 1;
+		public static final int MissingField 					= 2;
+		public static final int InternalError_BadData 			= 3;
+		public static final int DuplicateValue 					= 4;
+		public static final int InvalidField 					= 5;
+	}
+
+	static Object[][] MessageTemplateData = new Object[][] {
+			{ MessageId.InternalError,								"Internal error: {reason}."},
+			{ MessageId.InternalError_BadData,						"Internal error: bad data encountered [{field}]: {reason}"},
+			{ MessageId.MissingField,								"Missing Required field [{field}]: {reason}"},
+			{ MessageId.InvalidField,								"Invalid value specified for field [{field}]: {reason}"},
+			{ MessageId.DuplicateValue,								"Duplicate value for [{field}]: {reason}"},
+	};
+	static final Map<Integer, String> _TemplateId2Template = createMap(MessageTemplateData);
+
+	static int[][] ErrorCode2MessageTemplateData = new int[][] {
+			{ ErrorCode.InternalError_InvalidMethodInvocation,					MessageId.InternalError},
+			{ ErrorCode.Missing_PolicyId_Delete,								MessageId.MissingField},
+			{ ErrorCode.Missing_PolicyObject,									MessageId.InternalError},
+			{ ErrorCode.Missing_PolicyId_Update,								MessageId.MissingField},
+			{ ErrorCode.Invalid_PolicyId,										MessageId.InvalidField},
+			{ ErrorCode.Missing_PolicyName,										MessageId.MissingField},
+			{ ErrorCode.InternalError_Data_MultiplePoliciesSameName,			MessageId.InternalError_BadData},
+			{ ErrorCode.Duplicate_PolicyName_Create,							MessageId.DuplicateValue},
+			{ ErrorCode.Duplicate_PolicyName_Update,							MessageId.DuplicateValue},
+			{ ErrorCode.Missing_ServiceName,									MessageId.MissingField},
+			{ ErrorCode.Invalid_ServiceName,									MessageId.InvalidField},
+			{ ErrorCode.Missing_PolicyItems,									MessageId.MissingField},
+			{ ErrorCode.InternalError_Data_MissingServiceDef,                   MessageId.InternalError_BadData},
+			{ ErrorCode.Duplicate_PolicyResource,								MessageId.DuplicateValue},
+			{ ErrorCode.Invalid_PolicyResource_NoCompatibleHierarchy,			MessageId.InvalidField},
+			{ ErrorCode.Invalid_PolicyResource_MissingMandatory,				MessageId.MissingField},
+			{ ErrorCode.InternalError_Data_NullResourceDef,						MessageId.InternalError_BadData},
+			{ ErrorCode.InternalError_Data_NullResourceDefName,					MessageId.InternalError_BadData},
+			{ ErrorCode.Invalid_Excludes_NotSupported,							MessageId.InvalidField},
+			{ ErrorCode.Invalid_Excludes_RequiresAdmin,							MessageId.InvalidField},
+			{ ErrorCode.Invalid_Recursive_NotSupported,							MessageId.InvalidField},
+			{ ErrorCode.Invalid_ResourceValue_RegEx,							MessageId.InvalidField},
+			{ ErrorCode.InternalError_Data_NullPolicyItem,						MessageId.InternalError_BadData},
+			{ ErrorCode.Missing_PolicyItemAccesses,								MessageId.MissingField},
+			{ ErrorCode.Missing_PolicyItemUserGroup,							MessageId.MissingField},
+			{ ErrorCode.InternalError_Data_NullPolicyItemAccess,				MessageId.InternalError_BadData},
+			{ ErrorCode.Missing_PolicyItemAccessType,							MessageId.MissingField},
+			{ ErrorCode.Invalid_PolicyItemAccessType,							MessageId.InvalidField},
+			{ ErrorCode.Invalid_PolicyItemAccessType_Deny,						MessageId.InvalidField},
+
+	};
+	static final Map<Integer, Integer> _ErrorCode2MessageTemplate = createMap(ErrorCode2MessageTemplateData);
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/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 3246138..381864d 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
@@ -106,9 +106,10 @@ public abstract class RangerValidator {
 			LOG.warn("serializeFailures: called while list of failures is null/empty!");
 		} else {
 			StringBuilder builder = new StringBuilder();
-			for (ValidationFailureDetails aFailure : failures) {
-				builder.append(aFailure.toString());
-				builder.append(";");
+			for (int i = 0; i < failures.size(); i++) {
+				builder.append(String.format("(%d)", i));
+				builder.append(failures.get(i).toString());
+				builder.append(" ");
 			}
 			message = builder.toString();
 		}
@@ -651,4 +652,39 @@ public abstract class RangerValidator {
 		return result;
 	}
 
+	static Map<Integer, Integer> createMap(int[][] data) {
+		Map<Integer, Integer> result = new HashMap<>();
+		if (data != null) {
+			for (int[] row : data) {
+				Integer key = row[0];
+				Integer value = row[1];
+				if (result.containsKey(key)) {
+					LOG.warn("createMap: Internal error: duplicate key: multiple rows found for [" + key + "]. Skipped");
+				} else {
+					result.put(key, value);
+				}
+			}
+		}
+		return result;
+	}
+
+	static Map<Integer, String> createMap(Object[][] data) {
+		Map<Integer, String> result = new HashMap<>();
+		if (data != null) {
+			for (Object[] row : data) {
+				Integer key = (Integer)row[0];
+				String value = (String)row[1];
+				if (key == null) {
+					LOG.warn("createMap: error converting key[" + row[0] + "] to Integer! Sipped!");
+				} else if (StringUtils.isEmpty(value)) {
+					LOG.warn("createMap: empty/null value.  Skipped!");
+				} else if (result.containsKey(key)) {
+					LOG.warn("createMap: Internal error: duplicate key.  Multiple rows found for [" + key + "]. Skipped");
+				} else {
+					result.put(key, value);
+				}
+			}
+		}
+		return result;
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java
index 015203a..64e7e50 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java
@@ -19,18 +19,27 @@
 
 package org.apache.ranger.plugin.model.validation;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class ValidationFailureDetails {
 
+	private static final Log LOG = LogFactory.getLog(ValidationFailureDetails.class);
+
 	final String _fieldName;
 	final String _subFieldName;
 	final boolean _missing;
 	final boolean _semanticError;
 	final boolean _internalError;
 	final String _reason;
-	
-	public ValidationFailureDetails(String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) {
+	final int _errorCode;
+
+	public ValidationFailureDetails(int errorCode, String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) {
+		_errorCode = errorCode;
 		_missing = missing;
 		_semanticError = semanticError;
 		_internalError = internalError;
@@ -39,6 +48,11 @@ public class ValidationFailureDetails {
 		_reason = reason;
 	}
 
+	// TODO - legacy signature remove after all 3 are ported over to new message framework
+	public ValidationFailureDetails(String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) {
+		this(-1, fieldName, subFieldName, missing, semanticError, internalError, reason);
+	}
+
 	public String getFieldName() {
 		return _fieldName;
 	}
@@ -61,18 +75,29 @@ public class ValidationFailureDetails {
 	public String getSubFieldName() {
 		return _subFieldName;
 	}
-	
+
+	// matches "{blah}", "{{blah}", "{   }" and yields variables names like "blah", "{blah", "   ", etc. for substitution
+	static final Pattern _Pattern = Pattern.compile("\\{([^\\}]+)\\}");
+
+	public String substituteVariables(String template) {
+		return template.replace("{field}", _fieldName == null ? "" : _fieldName)
+				.replace("{sub-field}", _subFieldName == null ? "" : _subFieldName)
+				.replace("{reason}", _reason == null ? "" : _reason);
+	}
+
+	// TODO legacy implementation.  Remove when all
 	@Override
 	public String toString() {
+		LOG.debug("ValidationFailureDetails.toString()");
 		return String.format("Field[%s]%s is %s: reason[%s]", 
 				_fieldName, 
 				_subFieldName == null ? "" : ", subField[" + _subFieldName + "]",
 				getType(), _reason);
 	}
-	
+
 	@Override
 	public int hashCode() {
-		return Objects.hash(_fieldName, _subFieldName, _missing, _semanticError, _internalError, _reason);
+		return Objects.hash(_fieldName, _subFieldName, _missing, _semanticError, _internalError, _reason, _errorCode);
 	}
 	
 	@Override
@@ -86,6 +111,7 @@ public class ValidationFailureDetails {
 				Objects.equals(_reason, that._reason) && 
 				_internalError == that._internalError &&
 				_missing == that._missing &&
-				_semanticError == that._semanticError;
+				_semanticError == that._semanticError &&
+				_errorCode == that._errorCode;
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java
index 3a57341..ab67f1f 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java
@@ -20,13 +20,14 @@
 package org.apache.ranger.plugin.model.validation;
 
 public class ValidationFailureDetailsBuilder {
-	private String _fieldName;
-	private boolean _missing;
-	private boolean _semanticError;
-	private String _reason;
-	private String _subFieldName;
-	private boolean _internalError;
-	
+	protected String _fieldName;
+	protected boolean _missing;
+	protected boolean _semanticError;
+	protected String _reason;
+	protected String _subFieldName;
+	protected boolean _internalError;
+	protected int _errorCode;
+
 	ValidationFailureDetailsBuilder becauseOf(String aReason) {
 		_reason = aReason;
 		return this;
@@ -61,4 +62,8 @@ public class ValidationFailureDetailsBuilder {
 		return this;
 	}
 
+	ValidationFailureDetailsBuilder errorCode(int errorCode) {
+		_errorCode = errorCode;
+		return this;
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/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 6236d71..e0f3b0e 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
@@ -341,7 +341,7 @@ public class TestRangerPolicyValidator {
 		filter.setParam(SearchFilter.SERVICE_NAME, "service-name");
 		filter.setParam(SearchFilter.POLICY_NAME, "policy-name");
 		when(_store.getPolicies(filter)).thenReturn(existingPolicies);
-		checkFailure_isValid(Action.CREATE, "semantic", "name");
+		checkFailure_isValid(Action.CREATE, "semantic", "policy name");
 		
 		// update : does not exist for id
 		when(_policy.getId()).thenReturn(7L);
@@ -374,11 +374,11 @@ public class TestRangerPolicyValidator {
 			for (boolean isAdmin : new boolean[] { true, false }) {
 				when(_policy.getService()).thenReturn(null);
 				_failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures));
-				_utils.checkFailureForMissingValue(_failures, "service");
+				_utils.checkFailureForMissingValue(_failures, "service name");
 	
 				when(_policy.getService()).thenReturn("");
 				_failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures));
-				_utils.checkFailureForMissingValue(_failures, "service");
+				_utils.checkFailureForMissingValue(_failures, "service name");
 			}
 		}
 		
@@ -389,19 +389,19 @@ public class TestRangerPolicyValidator {
 			for (boolean isAdmin : new boolean[] { true, false }) {
 				when(_policy.getService()).thenReturn(null);
 				_failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures));
-				_utils.checkFailureForMissingValue(_failures, "service");
+				_utils.checkFailureForMissingValue(_failures, "service name");
 	
 				when(_policy.getService()).thenReturn(null);
 				_failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures));
-				_utils.checkFailureForMissingValue(_failures, "service");
+				_utils.checkFailureForMissingValue(_failures, "service name");
 	
 				when(_policy.getService()).thenReturn("service-name");
 				_failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures));
-				_utils.checkFailureForSemanticError(_failures, "service");
+				_utils.checkFailureForSemanticError(_failures, "service name");
 	
 				when(_policy.getService()).thenReturn("another-service-name");
 				_failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures));
-				_utils.checkFailureForSemanticError(_failures, "service");
+				_utils.checkFailureForSemanticError(_failures, "service name");
 			}
 		}
 		
@@ -475,7 +475,7 @@ public class TestRangerPolicyValidator {
 		for (Action action : cu) {
 			for (boolean isAdmin : new boolean[] { true, false }) {
 				_failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures));
-				_utils.checkFailureForSemanticError(_failures, "resources");
+				_utils.checkFailureForSemanticError(_failures, "policy resources");
 			}
 		}
 	}
@@ -766,19 +766,19 @@ public class TestRangerPolicyValidator {
 		Map<String, RangerPolicyResource> policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad);				
 		when(_policy.getResources()).thenReturn(policyResources);
 		assertFalse("Missing required resource and unknown resource", _validator.isValidResourceNames(_policy, _failures, _serviceDef));
-		_utils.checkFailureForSemanticError(_failures, "resources");
+		_utils.checkFailureForSemanticError(_failures, "policy resources");
 		
 		// another bad resource map that straddles multiple hierarchies
 		policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad_multiple_hierarchies);
 		when(_policy.getResources()).thenReturn(policyResources);
 		_failures.clear(); assertFalse("Policy with resources for multiple hierarchies", _validator.isValidResourceNames(_policy, _failures, _serviceDef));
-		_utils.checkFailureForSemanticError(_failures, "resources", "incompatible");
+		_utils.checkFailureForSemanticError(_failures, "policy resources", "incompatible");
 		
 		// another bad policy resource map that could match multiple hierarchies but is short on mandatory resources for all of those matches
 		policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad_multiple_hierarchies_missing_mandatory);
 		when(_policy.getResources()).thenReturn(policyResources);
 		_failures.clear(); assertFalse("Policy with resources for multiple hierarchies missing mandatory resources for all pontential matches", _validator.isValidResourceNames(_policy, _failures, _serviceDef));
-		_utils.checkFailureForSemanticError(_failures, "resources", "missing mandatory");
+		_utils.checkFailureForSemanticError(_failures, "policy resources", "missing mandatory");
 	}
 	
 	

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java
----------------------------------------------------------------------
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java
new file mode 100644
index 0000000..0a758d1
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java
@@ -0,0 +1,39 @@
+package org.apache.ranger.plugin.model.validation;
+
+import junit.framework.TestCase;
+import org.junit.Test;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Created by alal on 6/17/15.
+ */
+public class TestValidationFailureDetails {
+
+    @Test
+    public void test1() {
+        String[] templates = new String[] {
+                "The {field}, was missing and sub-field {sub-field} was mssing, too. Validation failed due to {reason}", // pattern at end.
+                "{field}, was missing and sub-field {sub-field} was mssing, too. Validation failed due to {reason}.",    // pattern at start but not end.
+                "The {field}, was missing and sub-field {sub-field} was mssing, too. Validation failed due to {missing}.",    // unknown substitute
+                "Template does not have field, but had {sub-field} along with a {reason} and a sprious field named {missing}.",    // unknown substitute
+        };
+
+        ValidationFailureDetails failureDetails = new ValidationFailureDetails("id", "subType", false, false, false, "foo-bar");
+
+        String[] results = new String[] {
+                "The id, was missing and sub-field subType was mssing, too. Validation failed due to foo-bar", // pattern at end.
+                "id, was missing and sub-field subType was mssing, too. Validation failed due to foo-bar.",    // pattern at start but not end.
+                "The id, was missing and sub-field subType was mssing, too. Validation failed due to {missing}.",    // unknown substitute
+                "Template does not have field, but had subType along with a foo-bar and a sprious field named {missing}.",    // unknown substitute
+        };
+
+        for (int i = 0; i < templates.length; i++) {
+            String result = failureDetails.substituteVariables(templates[i]);
+            assertEquals(results[i], result);
+        }
+    }
+}
\ No newline at end of file