You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by sp...@apache.org on 2018/05/31 03:32:27 UTC

[49/86] sentry git commit: SENTRY-2208: Refactor out Sentry service into own module from sentry-provider-db (Anthony Young-Garner, reviewed by Sergio Pena, Steve Moist, Na Li)

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/appender/TestRollingFileWithoutDeleteAppender.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/appender/TestRollingFileWithoutDeleteAppender.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/appender/TestRollingFileWithoutDeleteAppender.java
new file mode 100644
index 0000000..ca9062b
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/appender/TestRollingFileWithoutDeleteAppender.java
@@ -0,0 +1,106 @@
+/**
+ * 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.sentry.provider.db.log.appender;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.io.Files;
+
+public class TestRollingFileWithoutDeleteAppender {
+  private Logger sentryLogger = Logger.getRootLogger();
+  private File dataDir;
+
+  @Before
+  public void init() {
+    dataDir = Files.createTempDir();
+  }
+
+  @Test
+  public void testRollOver() throws Throwable {
+    if (dataDir == null) {
+      fail("Excepted temp folder for audit log is created.");
+    }
+    RollingFileWithoutDeleteAppender appender = new RollingFileWithoutDeleteAppender(
+        new PatternLayout("%m%n"), dataDir.getPath() + "/auditLog.log");
+    appender.setMaximumFileSize(100);
+    sentryLogger.addAppender(appender);
+    // Write exactly 10 bytes with each log
+    for (int i = 0; i < 99; i++) {
+      if (i < 10) {
+        sentryLogger.debug("Hello---" + i);
+      } else if (i < 100) {
+        sentryLogger.debug("Hello--" + i);
+      }
+    }
+
+    if (dataDir != null) {
+      File[] files = dataDir.listFiles();
+      if (files != null) {
+        assertEquals(files.length, 10);
+      } else {
+        fail("Excepted 10 log files.");
+      }
+    } else {
+      fail("Excepted 10 log files.");
+    }
+
+  }
+
+  /***
+   * Generate log enough to cause a single rollover. Verify the file name format
+   * @throws Throwable
+   */
+  @Test
+  public void testFileNamePattern() throws Throwable {
+    if (dataDir == null) {
+      fail("Excepted temp folder for audit log is created.");
+    }
+    RollingFileWithoutDeleteAppender appender = new RollingFileWithoutDeleteAppender(
+        new PatternLayout("%m%n"), dataDir.getPath() + "/auditLog.log");
+    appender.setMaximumFileSize(10);
+    sentryLogger.addAppender(appender);
+    sentryLogger.debug("123456789012345");
+    File[] files = dataDir.listFiles();
+    if (files != null) {
+      assertEquals(files.length, 2);
+      assertTrue(files[0].getName().contains("auditLog.log."));
+      assertTrue(files[1].getName().contains("auditLog.log."));
+    } else {
+      fail("Excepted 2 log files.");
+    }
+  }
+
+  @After
+  public void destroy() {
+    if (dataDir != null) {
+      FileUtils.deleteQuietly(dataDir);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestDbAuditMetadataLogEntity.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestDbAuditMetadataLogEntity.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestDbAuditMetadataLogEntity.java
new file mode 100644
index 0000000..3d336af
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestDbAuditMetadataLogEntity.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.sentry.provider.db.log.entity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.apache.sentry.provider.db.log.util.Constants;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.node.ContainerNode;
+import org.junit.Test;
+
+public class TestDbAuditMetadataLogEntity {
+
+  @Test
+  public void testToJsonFormatLog() throws Throwable {
+    DBAuditMetadataLogEntity amle = new DBAuditMetadataLogEntity("serviceName", "userName",
+        "impersonator", "ipAddress", "operation", "eventTime", "operationText", "allowed",
+        "objectType", "component", "databaseName", "tableName", "columnName", "resourcePath");
+    String jsonAuditLog = amle.toJsonFormatLog();
+    ContainerNode rootNode = AuditMetadataLogEntity.parse(jsonAuditLog);
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_SERVICE_NAME, "serviceName");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_USER_NAME, "userName");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_IMPERSONATOR,
+        "impersonator");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_IP_ADDRESS, "ipAddress");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_OPERATION, "operation");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_EVENT_TIME, "eventTime");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_OPERATION_TEXT,
+        "operationText");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_ALLOWED, "allowed");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_DATABASE_NAME,
+        "databaseName");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_TABLE_NAME, "tableName");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_COLUMN_NAME, "columnName");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_RESOURCE_PATH,
+        "resourcePath");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_OBJECT_TYPE, "objectType");
+  }
+
+  void assertEntryEquals(ContainerNode rootNode, String key, String value) {
+    JsonNode node = assertNodeContains(rootNode, key);
+    assertEquals(value, node.getTextValue());
+  }
+
+  private JsonNode assertNodeContains(ContainerNode rootNode, String key) {
+    JsonNode node = rootNode.get(key);
+    if (node == null) {
+      fail("No entry of name \"" + key + "\" found in " + rootNode.toString());
+    }
+    return node;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestGMAuditMetadataLogEntity.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestGMAuditMetadataLogEntity.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestGMAuditMetadataLogEntity.java
new file mode 100644
index 0000000..bbee1b4
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestGMAuditMetadataLogEntity.java
@@ -0,0 +1,74 @@
+/**
+ * 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.sentry.provider.db.log.entity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sentry.provider.db.log.util.Constants;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.node.ContainerNode;
+import org.junit.Test;
+
+public class TestGMAuditMetadataLogEntity {
+  @Test
+  public void testToJsonFormatLog() throws Throwable {
+
+    Map<String, String> privilegesMap = new HashMap<String, String>();
+    privilegesMap.put("resourceType1", "resourceName1");
+    privilegesMap.put("resourceType2", "resourceName2");
+    privilegesMap.put("resourceType3", "resourceName3");
+    privilegesMap.put("resourceType4", "resourceName4");
+    GMAuditMetadataLogEntity gmamle = new GMAuditMetadataLogEntity("serviceName", "userName",
+        "impersonator", "ipAddress", "operation", "eventTime", "operationText", "allowed",
+        "objectType", "component", privilegesMap);
+    String jsonAuditLog = gmamle.toJsonFormatLog();
+    ContainerNode rootNode = AuditMetadataLogEntity.parse(jsonAuditLog);
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_SERVICE_NAME, "serviceName");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_USER_NAME, "userName");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_IMPERSONATOR, "impersonator");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_IP_ADDRESS, "ipAddress");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_OPERATION, "operation");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_EVENT_TIME, "eventTime");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_OPERATION_TEXT, "operationText");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_ALLOWED, "allowed");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_OBJECT_TYPE, "objectType");
+    assertEntryEquals(rootNode, Constants.LOG_FIELD_COMPONENT, "component");
+    assertEntryEquals(rootNode, "resourceType1", "resourceName1");
+    assertEntryEquals(rootNode, "resourceType2", "resourceName2");
+    assertEntryEquals(rootNode, "resourceType3", "resourceName3");
+    assertEntryEquals(rootNode, "resourceType4", "resourceName4");
+  }
+
+  void assertEntryEquals(ContainerNode rootNode, String key, String value) {
+    JsonNode node = assertNodeContains(rootNode, key);
+    assertEquals(value, node.getTextValue());
+  }
+
+  private JsonNode assertNodeContains(ContainerNode rootNode, String key) {
+    JsonNode node = rootNode.get(key);
+    if (node == null) {
+      fail("No entry of name \"" + key + "\" found in " + rootNode.toString());
+    }
+    return node;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactory.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactory.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactory.java
new file mode 100644
index 0000000..307f38e
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactory.java
@@ -0,0 +1,272 @@
+/**
+ * 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.sentry.provider.db.log.entity;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.core.model.db.AccessConstants;
+import org.apache.sentry.provider.db.log.util.Constants;
+import org.apache.sentry.api.common.ApiConstants.PrivilegeScope;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddGroupsRequest;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddGroupsResponse;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteGroupsResponse;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleGrantPrivilegeResponse;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleRevokePrivilegeRequest;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleRevokePrivilegeResponse;
+import org.apache.sentry.api.service.thrift.TCreateSentryRoleRequest;
+import org.apache.sentry.api.service.thrift.TCreateSentryRoleResponse;
+import org.apache.sentry.api.service.thrift.TDropSentryRoleRequest;
+import org.apache.sentry.api.service.thrift.TDropSentryRoleResponse;
+import org.apache.sentry.api.service.thrift.TSentryGroup;
+import org.apache.sentry.api.service.thrift.TSentryPrivilege;
+import org.apache.sentry.core.common.utils.ThriftUtil;
+import org.apache.sentry.service.common.ServiceConstants.ServerConfig;
+import org.apache.sentry.api.common.Status;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+
+public class TestJsonLogEntityFactory {
+
+  private static Configuration conf;
+
+  private static String TEST_IP = "localhost/127.0.0.1";
+  private static String TEST_IMPERSONATOR = "impersonator";
+  private static String TEST_ROLE_NAME = "testRole";
+  private static String TEST_USER_NAME = "requestUser";
+  private static String TEST_DATABASE_NAME = "testDB";
+  private static String TEST_TABLE_NAME = "testTable";
+  private static String TEST_GROUP = "testGroup";
+
+  @BeforeClass
+  public static void init() {
+    conf = new Configuration();
+    conf.set(ServerConfig.SENTRY_SERVICE_NAME,
+        ServerConfig.SENTRY_SERVICE_NAME_DEFAULT);
+    ThriftUtil.setIpAddress(TEST_IP);
+    ThriftUtil.setImpersonator(TEST_IMPERSONATOR);
+  }
+
+  @Test
+  public void testCreateRole() {
+    TCreateSentryRoleRequest request = new TCreateSentryRoleRequest();
+    TCreateSentryRoleResponse response = new TCreateSentryRoleResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    response.setStatus(Status.OK());
+    DBAuditMetadataLogEntity amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory
+        .getInstance().createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_CREATE_ROLE,
+        "CREATE ROLE testRole", null, null, null, Constants.OBJECT_TYPE_ROLE);
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_CREATE_ROLE,
+        "CREATE ROLE testRole", null, null, null, Constants.OBJECT_TYPE_ROLE);
+  }
+
+  @Test
+  public void testDropRole() {
+    TDropSentryRoleRequest request = new TDropSentryRoleRequest();
+    TDropSentryRoleResponse response = new TDropSentryRoleResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    response.setStatus(Status.OK());
+    DBAuditMetadataLogEntity amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory
+        .getInstance().createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_DROP_ROLE,
+        "DROP ROLE testRole", null, null, null, Constants.OBJECT_TYPE_ROLE);
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_DROP_ROLE,
+        "DROP ROLE testRole", null, null, null, Constants.OBJECT_TYPE_ROLE);
+  }
+
+  @Test
+  public void testGrantRole() {
+    TAlterSentryRoleGrantPrivilegeRequest request = new TAlterSentryRoleGrantPrivilegeRequest();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+
+    TAlterSentryRoleGrantPrivilegeResponse response = new TAlterSentryRoleGrantPrivilegeResponse();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.ALL,
+        PrivilegeScope.DATABASE.name(), TEST_DATABASE_NAME, null, null, null);
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    request.setPrivileges(privileges);
+    response.setStatus(Status.OK());
+    DBAuditMetadataLogEntity amle = new DBAuditMetadataLogEntity();
+    Set<JsonLogEntity> amles =  JsonLogEntityFactory
+        .getInstance().createJsonLogEntitys(request, response, conf);
+    assertEquals(amles.size(),1);
+    amle = (DBAuditMetadataLogEntity) amles.iterator().next();
+
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_GRANT_PRIVILEGE,
+        "GRANT ALL ON DATABASE testDB TO ROLE testRole", TEST_DATABASE_NAME,
+        null, null, Constants.OBJECT_TYPE_PRINCIPAL);
+
+    privilege = getPrivilege(AccessConstants.ALL, PrivilegeScope.TABLE.name(),
+        null, TEST_TABLE_NAME, null, null);
+    privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    request.setPrivileges(privileges);
+    response.setStatus(Status.InvalidInput("", null));
+    amles =  JsonLogEntityFactory.getInstance()
+        .createJsonLogEntitys(request, response, conf);
+    assertEquals(amles.size(),1);
+    amle = (DBAuditMetadataLogEntity) amles.iterator().next();
+
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_GRANT_PRIVILEGE,
+        "GRANT ALL ON TABLE testTable TO ROLE testRole", null, TEST_TABLE_NAME,
+        null, Constants.OBJECT_TYPE_PRINCIPAL);
+  }
+
+  @Test
+  public void testRevokeRole() {
+    TAlterSentryRoleRevokePrivilegeRequest request = new TAlterSentryRoleRevokePrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeResponse response = new TAlterSentryRoleRevokePrivilegeResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.ALL,
+        PrivilegeScope.DATABASE.name(), TEST_DATABASE_NAME, null, null, null);
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    request.setPrivileges(privileges);
+    response.setStatus(Status.OK());
+    DBAuditMetadataLogEntity amle = new DBAuditMetadataLogEntity();
+    Set<JsonLogEntity> amles =  JsonLogEntityFactory
+        .getInstance().createJsonLogEntitys(request, response, conf);
+    assertEquals(amles.size(),1);
+    amle = (DBAuditMetadataLogEntity) amles.iterator().next();
+
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_REVOKE_PRIVILEGE,
+        "REVOKE ALL ON DATABASE testDB FROM ROLE testRole", TEST_DATABASE_NAME,
+        null, null, Constants.OBJECT_TYPE_PRINCIPAL);
+
+    privilege = getPrivilege(AccessConstants.ALL, PrivilegeScope.TABLE.name(),
+        null, TEST_TABLE_NAME, null, null);
+    privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    request.setPrivileges(privileges);
+    response.setStatus(Status.InvalidInput("", null));
+    amles =  JsonLogEntityFactory.getInstance()
+        .createJsonLogEntitys(request, response, conf);
+    assertEquals(amles.size(),1);
+    amle = (DBAuditMetadataLogEntity) amles.iterator().next();
+
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_REVOKE_PRIVILEGE,
+        "REVOKE ALL ON TABLE testTable FROM ROLE testRole", null,
+        TEST_TABLE_NAME, null, Constants.OBJECT_TYPE_PRINCIPAL);
+  }
+
+  @Test
+  public void testAddRole() {
+    TAlterSentryRoleAddGroupsRequest request = new TAlterSentryRoleAddGroupsRequest();
+    TAlterSentryRoleAddGroupsResponse response = new TAlterSentryRoleAddGroupsResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    request.setGroups(getGroups());
+    response.setStatus(Status.OK());
+    DBAuditMetadataLogEntity amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory
+        .getInstance().createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_ADD_ROLE,
+        "GRANT ROLE testRole TO GROUP testGroup", null, null, null,
+        Constants.OBJECT_TYPE_ROLE);
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_ADD_ROLE,
+        "GRANT ROLE testRole TO GROUP testGroup", null, null, null,
+        Constants.OBJECT_TYPE_ROLE);
+  }
+
+  @Test
+  public void testDeleteRole() {
+    TAlterSentryRoleDeleteGroupsRequest request = new TAlterSentryRoleDeleteGroupsRequest();
+    TAlterSentryRoleDeleteGroupsResponse response = new TAlterSentryRoleDeleteGroupsResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    request.setGroups(getGroups());
+    response.setStatus(Status.OK());
+    DBAuditMetadataLogEntity amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory
+        .getInstance().createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_DELETE_ROLE,
+        "REVOKE ROLE testRole FROM GROUP testGroup", null, null, null,
+        Constants.OBJECT_TYPE_ROLE);
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (DBAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_DELETE_ROLE,
+        "REVOKE ROLE testRole FROM GROUP testGroup", null, null, null,
+        Constants.OBJECT_TYPE_ROLE);
+  }
+
+  private void assertCommon(DBAuditMetadataLogEntity amle,
+      String allowedExcepted, String operationExcepted,
+      String operationTextExcepted, String databaseNameExcepted,
+      String tableNameExcepted, String resourcePathExcepted,
+      String objectTypeExcepted) {
+    assertEquals(ServerConfig.SENTRY_SERVICE_NAME_DEFAULT,
+        amle.getServiceName());
+    assertEquals(TEST_IP, amle.getIpAddress());
+    assertEquals(TEST_USER_NAME, amle.getUserName());
+    assertEquals(TEST_IMPERSONATOR, amle.getImpersonator());
+    assertEquals(allowedExcepted, amle.getAllowed());
+    assertEquals(operationExcepted, amle.getOperation());
+    assertEquals(operationTextExcepted, amle.getOperationText());
+    assertEquals(tableNameExcepted, amle.getTableName());
+    assertEquals(databaseNameExcepted, amle.getDatabaseName());
+    assertEquals(resourcePathExcepted, amle.getResourcePath());
+    assertEquals(objectTypeExcepted, amle.getObjectType());
+  }
+
+  private TSentryPrivilege getPrivilege(String action, String privilegeScope,
+      String dbName, String tableName, String serverName, String URI) {
+    TSentryPrivilege privilege = new TSentryPrivilege();
+    privilege.setAction(action);
+    privilege.setPrivilegeScope(privilegeScope);
+    privilege.setDbName(dbName);
+    privilege.setTableName(tableName);
+    privilege.setServerName(serverName);
+    privilege.setURI(URI);
+    return privilege;
+  }
+
+  private Set<TSentryGroup> getGroups() {
+    Set<TSentryGroup> groups = new LinkedHashSet<TSentryGroup>();
+    TSentryGroup group = new TSentryGroup();
+    group.setGroupName(TEST_GROUP);
+    groups.add(group);
+    return groups;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactoryGM.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactoryGM.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactoryGM.java
new file mode 100644
index 0000000..8623a09
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/entity/TestJsonLogEntityFactoryGM.java
@@ -0,0 +1,259 @@
+/**
+ * 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.sentry.provider.db.log.entity;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleAddGroupsRequest;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleAddGroupsResponse;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleDeleteGroupsRequest;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleDeleteGroupsResponse;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleGrantPrivilegeRequest;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleGrantPrivilegeResponse;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleRevokePrivilegeRequest;
+import org.apache.sentry.api.generic.thrift.TAlterSentryRoleRevokePrivilegeResponse;
+import org.apache.sentry.api.generic.thrift.TAuthorizable;
+import org.apache.sentry.api.generic.thrift.TCreateSentryRoleRequest;
+import org.apache.sentry.api.generic.thrift.TCreateSentryRoleResponse;
+import org.apache.sentry.api.generic.thrift.TDropSentryRoleRequest;
+import org.apache.sentry.api.generic.thrift.TDropSentryRoleResponse;
+import org.apache.sentry.api.generic.thrift.TSentryPrivilege;
+import org.apache.sentry.provider.db.log.util.Constants;
+import org.apache.sentry.core.common.utils.ThriftUtil;
+import org.apache.sentry.service.common.ServiceConstants.ServerConfig;
+import org.apache.sentry.api.common.Status;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestJsonLogEntityFactoryGM {
+
+  private static Configuration conf;
+  private static String TEST_IP = "localhost/127.0.0.1";
+  private static String TEST_IMPERSONATOR = "impersonator";
+  private static String TEST_ROLE_NAME = "testRole";
+  private static String TEST_USER_NAME = "requestUser";
+  private static String TEST_GROUP = "testGroup";
+  private static String TEST_ACTION = "action";
+  private static String TEST_COMPONENT = "component";
+  private static Map<String, String> TEST_PRIVILEGES_MAP = new HashMap<String, String>();
+
+  @BeforeClass
+  public static void init() {
+    conf = new Configuration();
+    conf.set(ServerConfig.SENTRY_SERVICE_NAME, ServerConfig.SENTRY_SERVICE_NAME_DEFAULT);
+    ThriftUtil.setIpAddress(TEST_IP);
+    ThriftUtil.setImpersonator(TEST_IMPERSONATOR);
+    TEST_PRIVILEGES_MAP.put("resourceType1", "resourceName1");
+    TEST_PRIVILEGES_MAP.put("resourceType2", "resourceName2");
+    TEST_PRIVILEGES_MAP.put("resourceType3", "resourceName3");
+  }
+
+  @Test
+  public void testCreateRole() {
+    TCreateSentryRoleRequest request = new TCreateSentryRoleRequest();
+    TCreateSentryRoleResponse response = new TCreateSentryRoleResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    response.setStatus(Status.OK());
+    GMAuditMetadataLogEntity amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_CREATE_ROLE, "CREATE ROLE testRole",
+        Constants.OBJECT_TYPE_ROLE, new HashMap<String, String>());
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance().createJsonLogEntity(
+        request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_CREATE_ROLE, "CREATE ROLE testRole",
+        Constants.OBJECT_TYPE_ROLE, new HashMap<String, String>());
+  }
+
+  @Test
+  public void testDropRole() {
+    TDropSentryRoleRequest request = new TDropSentryRoleRequest();
+    TDropSentryRoleResponse response = new TDropSentryRoleResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    response.setStatus(Status.OK());
+    GMAuditMetadataLogEntity amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory
+        .getInstance().createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_DROP_ROLE, "DROP ROLE testRole",
+        Constants.OBJECT_TYPE_ROLE, new HashMap<String, String>());
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance().createJsonLogEntity(
+        request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_DROP_ROLE, "DROP ROLE testRole",
+        Constants.OBJECT_TYPE_ROLE, new HashMap<String, String>());
+  }
+
+  @Test
+  public void testGrantRole() {
+    TAlterSentryRoleGrantPrivilegeRequest request = new TAlterSentryRoleGrantPrivilegeRequest();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+
+    TAlterSentryRoleGrantPrivilegeResponse response = new TAlterSentryRoleGrantPrivilegeResponse();
+
+    TSentryPrivilege privilege = getPrivilege();
+    request.setPrivilege(privilege);
+    response.setStatus(Status.OK());
+    GMAuditMetadataLogEntity amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(
+        request, response, conf);
+    assertCommon(
+        amle,
+        Constants.TRUE,
+        Constants.OPERATION_GRANT_PRIVILEGE,
+        "GRANT ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 resourceType3 resourceName3 TO ROLE testRole",
+        Constants.OBJECT_TYPE_PRINCIPAL, TEST_PRIVILEGES_MAP);
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance().createJsonLogEntity(
+        request, response, conf);
+    assertCommon(
+        amle,
+        Constants.FALSE,
+        Constants.OPERATION_GRANT_PRIVILEGE,
+        "GRANT ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 resourceType3 resourceName3 TO ROLE testRole",
+        Constants.OBJECT_TYPE_PRINCIPAL, TEST_PRIVILEGES_MAP);
+  }
+
+  @Test
+  public void testRevokeRole() {
+    TAlterSentryRoleRevokePrivilegeRequest request = new TAlterSentryRoleRevokePrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeResponse response = new TAlterSentryRoleRevokePrivilegeResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+
+    TSentryPrivilege privilege = getPrivilege();
+    request.setPrivilege(privilege);
+    response.setStatus(Status.OK());
+    GMAuditMetadataLogEntity amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(request, response, conf);
+    assertCommon(
+        amle,
+        Constants.TRUE,
+        Constants.OPERATION_REVOKE_PRIVILEGE,
+        "REVOKE ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 resourceType3 resourceName3 FROM ROLE testRole",
+        Constants.OBJECT_TYPE_PRINCIPAL, TEST_PRIVILEGES_MAP);
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance().createJsonLogEntity(
+        request, response, conf);
+
+    assertCommon(
+        amle,
+        Constants.FALSE,
+        Constants.OPERATION_REVOKE_PRIVILEGE,
+        "REVOKE ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 resourceType3 resourceName3 FROM ROLE testRole",
+        Constants.OBJECT_TYPE_PRINCIPAL, TEST_PRIVILEGES_MAP);
+  }
+
+  @Test
+  public void testAddRole() {
+    TAlterSentryRoleAddGroupsRequest request = new TAlterSentryRoleAddGroupsRequest();
+    TAlterSentryRoleAddGroupsResponse response = new TAlterSentryRoleAddGroupsResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    request.setGroups(getGroups());
+    response.setStatus(Status.OK());
+    GMAuditMetadataLogEntity amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance()
+        .createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_ADD_ROLE,
+        "GRANT ROLE testRole TO GROUP testGroup", Constants.OBJECT_TYPE_ROLE,
+        new HashMap<String, String>());
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance().createJsonLogEntity(
+        request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_ADD_ROLE,
+        "GRANT ROLE testRole TO GROUP testGroup", Constants.OBJECT_TYPE_ROLE,
+        new HashMap<String, String>());
+  }
+
+  @Test
+  public void testDeleteRole() {
+    TAlterSentryRoleDeleteGroupsRequest request = new TAlterSentryRoleDeleteGroupsRequest();
+    TAlterSentryRoleDeleteGroupsResponse response = new TAlterSentryRoleDeleteGroupsResponse();
+    request.setRequestorUserName(TEST_USER_NAME);
+    request.setRoleName(TEST_ROLE_NAME);
+    request.setGroups(getGroups());
+    response.setStatus(Status.OK());
+    GMAuditMetadataLogEntity amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory
+        .getInstance().createJsonLogEntity(request, response, conf);
+    assertCommon(amle, Constants.TRUE, Constants.OPERATION_DELETE_ROLE,
+        "REVOKE ROLE testRole FROM GROUP testGroup", Constants.OBJECT_TYPE_ROLE,
+        new HashMap<String, String>());
+
+    response.setStatus(Status.InvalidInput("", null));
+    amle = (GMAuditMetadataLogEntity) JsonLogEntityFactory.getInstance().createJsonLogEntity(
+        request, response, conf);
+    assertCommon(amle, Constants.FALSE, Constants.OPERATION_DELETE_ROLE,
+        "REVOKE ROLE testRole FROM GROUP testGroup", Constants.OBJECT_TYPE_ROLE,
+        new HashMap<String, String>());
+  }
+
+  private void assertCommon(GMAuditMetadataLogEntity amle, String allowedExcepted,
+      String operationExcepted, String operationTextExcepted, String objectTypeExcepted,
+      Map<String, String> privilegesExcepted) {
+    assertEquals(ServerConfig.SENTRY_SERVICE_NAME_DEFAULT, amle.getServiceName());
+    assertEquals(TEST_IP, amle.getIpAddress());
+    assertEquals(TEST_USER_NAME, amle.getUserName());
+    assertEquals(TEST_IMPERSONATOR, amle.getImpersonator());
+    assertEquals(allowedExcepted, amle.getAllowed());
+    assertEquals(operationExcepted, amle.getOperation());
+    assertEquals(operationTextExcepted, amle.getOperationText());
+    assertEquals(objectTypeExcepted, amle.getObjectType());
+    assertPrivilegesMap(privilegesExcepted, amle.getPrivilegesMap());
+  }
+
+  private void assertPrivilegesMap(Map<String, String> privilegesExcepted,
+      Map<String, String> privilegesActual) {
+    assertEquals(privilegesExcepted.size(), privilegesActual.size());
+    for (Map.Entry<String, String> privilege : privilegesExcepted.entrySet()) {
+      assertEquals(privilege.getValue(), privilegesActual.get(privilege.getKey()));
+    }
+  }
+
+  private TSentryPrivilege getPrivilege() {
+    TSentryPrivilege privilege = new TSentryPrivilege();
+    privilege.setAction(TEST_ACTION);
+    privilege.setComponent(TEST_COMPONENT);
+    List<TAuthorizable> authorizables = new ArrayList<TAuthorizable>();
+    authorizables.add(new TAuthorizable("resourceType1", "resourceName1"));
+    authorizables.add(new TAuthorizable("resourceType2", "resourceName2"));
+    authorizables.add(new TAuthorizable("resourceType3", "resourceName3"));
+    privilege.setAuthorizables(authorizables);
+    return privilege;
+  }
+
+  private Set<String> getGroups() {
+    Set<String> groups = new HashSet<String>();
+    groups.add(TEST_GROUP);
+    return groups;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java
new file mode 100644
index 0000000..2b2c411
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java
@@ -0,0 +1,416 @@
+/**
+ * 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.sentry.provider.db.log.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sentry.core.model.db.AccessConstants;
+import org.apache.sentry.api.generic.thrift.TAuthorizable;
+import org.apache.sentry.api.common.ApiConstants.PrivilegeScope;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
+import org.apache.sentry.api.service.thrift.TAlterSentryRoleRevokePrivilegeRequest;
+import org.apache.sentry.api.service.thrift.TSentryGrantOption;
+import org.apache.sentry.api.service.thrift.TSentryPrivilege;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+
+public class TestCommandUtil extends Assert {
+
+  @Test
+  public void testCreateCmdForCreateOrDropRole() {
+    String roleName = "testRole";
+
+    String createRoleCmdResult = CommandUtil.createCmdForCreateOrDropRole(
+        roleName, true);
+    String dropRoleCmdResult = CommandUtil.createCmdForCreateOrDropRole(
+        roleName, false);
+    String createRoleCmdExcepted = "CREATE ROLE testRole";
+    String dropRoleCmdExcepted = "DROP ROLE testRole";
+
+    assertEquals(createRoleCmdExcepted, createRoleCmdResult);
+    assertEquals(dropRoleCmdResult, dropRoleCmdExcepted);
+  }
+
+  @Test
+  public void testCreateCmdForRoleAddOrDeleteGroup1() {
+
+    String createRoleAddGroupCmdResult = CommandUtil.createCmdForRoleAddGroup("testRole",
+        getGroupStr(1));
+    String createRoleAddGroupCmdExcepted = "GRANT ROLE testRole TO GROUP testGroup1";
+    String createRoleDeleteGroupCmdResult = CommandUtil.createCmdForRoleDeleteGroup("testRole",
+        getGroupStr(1));
+    String createRoleDeleteGroupCmdExcepted = "REVOKE ROLE testRole FROM GROUP testGroup1";
+
+    assertEquals(createRoleAddGroupCmdExcepted, createRoleAddGroupCmdResult);
+    assertEquals(createRoleDeleteGroupCmdExcepted,
+        createRoleDeleteGroupCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForRoleAddOrDeleteGroup2() {
+    String createRoleAddGroupCmdResult = CommandUtil.createCmdForRoleAddGroup("testRole",
+        getGroupStr(3));
+    String createRoleAddGroupCmdExcepted = "GRANT ROLE testRole TO GROUP testGroup1, testGroup2, testGroup3";
+    String createRoleDeleteGroupCmdResult = CommandUtil.createCmdForRoleDeleteGroup("testRole",
+        getGroupStr(3));
+    String createRoleDeleteGroupCmdExcepted = "REVOKE ROLE testRole FROM GROUP testGroup1, testGroup2, testGroup3";
+
+    assertEquals(createRoleAddGroupCmdExcepted, createRoleAddGroupCmdResult);
+    assertEquals(createRoleDeleteGroupCmdExcepted,
+        createRoleDeleteGroupCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForRoleAddOrDeleteUser1() {
+    String createRoleAddGroupCmdResult =
+        CommandUtil.createCmdForRoleAddUser("testRole", getUserStr(1));
+    String createRoleAddGroupCmdExcepted = "GRANT ROLE testRole TO USER testUser1";
+    String createRoleDeleteGroupCmdResult =
+        CommandUtil.createCmdForRoleDeleteUser("testRole", getUserStr(1));
+    String createRoleDeleteGroupCmdExcepted = "REVOKE ROLE testRole FROM USER testUser1";
+
+    assertEquals(createRoleAddGroupCmdExcepted, createRoleAddGroupCmdResult);
+    assertEquals(createRoleDeleteGroupCmdExcepted, createRoleDeleteGroupCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForRoleAddOrDeleteUser2() {
+    String createRoleAddGroupCmdResult =
+        CommandUtil.createCmdForRoleAddUser("testRole", getUserStr(3));
+    String createRoleAddGroupCmdExcepted =
+        "GRANT ROLE testRole TO USER testUser1, testUser2, testUser3";
+    String createRoleDeleteGroupCmdResult =
+        CommandUtil.createCmdForRoleDeleteUser("testRole", getUserStr(3));
+    String createRoleDeleteGroupCmdExcepted =
+        "REVOKE ROLE testRole FROM USER testUser1, testUser2, testUser3";
+
+    assertEquals(createRoleAddGroupCmdExcepted, createRoleAddGroupCmdResult);
+    assertEquals(createRoleDeleteGroupCmdExcepted, createRoleDeleteGroupCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege1() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.ALL,
+        PrivilegeScope.DATABASE.name(), "dbTest", "tableTest", "serverTest",
+        "hdfs://namenode:port/path/to/dir");
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil
+        .createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT ALL ON DATABASE dbTest TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE ALL ON DATABASE dbTest FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted,
+        createRevokePrivilegeCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege2() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.INSERT,
+        PrivilegeScope.DATABASE.name(), "dbTest", "tableTest", "serverTest",
+        "hdfs://namenode:port/path/to/dir");
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil
+        .createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT INSERT ON DATABASE dbTest TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE INSERT ON DATABASE dbTest FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted,
+        createRevokePrivilegeCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege3() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.SELECT,
+        PrivilegeScope.DATABASE.name(), "dbTest", "tableTest", "serverTest",
+        "hdfs://namenode:port/path/to/dir");
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil
+        .createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT SELECT ON DATABASE dbTest TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE SELECT ON DATABASE dbTest FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted,
+        createRevokePrivilegeCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege4() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(null,
+        PrivilegeScope.DATABASE.name(), "dbTest", "tableTest", "serverTest",
+        "hdfs://namenode:port/path/to/dir");
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil
+        .createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT null ON DATABASE dbTest TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE null ON DATABASE dbTest FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted,
+        createRevokePrivilegeCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege5() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.SELECT,
+        PrivilegeScope.TABLE.name(), "dbTest", "tableTest", "serverTest",
+        "hdfs://namenode:port/path/to/dir");
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil
+        .createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT SELECT ON TABLE tableTest TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE SELECT ON TABLE tableTest FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted,
+        createRevokePrivilegeCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege6() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.SELECT,
+        PrivilegeScope.SERVER.name(), "dbTest", "tableTest", "serverTest",
+        "hdfs://namenode:port/path/to/dir");
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil
+        .createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT SELECT ON SERVER serverTest TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE SELECT ON SERVER serverTest FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted,
+        createRevokePrivilegeCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege7() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.SELECT,
+        PrivilegeScope.URI.name(), "dbTest", "tableTest", "serverTest",
+        "hdfs://namenode:port/path/to/dir");
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil
+        .createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT SELECT ON URI hdfs://namenode:port/path/to/dir TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE SELECT ON URI hdfs://namenode:port/path/to/dir FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted,
+        createRevokePrivilegeCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForGrantOrRevokePrivilege8() {
+    TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
+    TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
+
+    TSentryPrivilege privilege = getPrivilege(AccessConstants.SELECT, PrivilegeScope.SERVER.name(),
+        "dbTest", "tableTest", "serverTest", "hdfs://namenode:port/path/to/dir");
+    privilege.setGrantOption(TSentryGrantOption.TRUE);
+    Set<TSentryPrivilege> privileges = Sets.newHashSet();
+    privileges.add(privilege);
+    grantRequest.setPrivileges(privileges);
+    revokeRequest.setPrivileges(privileges);
+
+    String createGrantPrivilegeCmdResult = CommandUtil.createCmdForGrantPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT SELECT ON SERVER serverTest TO ROLE testRole WITH GRANT OPTION";
+    String createRevokePrivilegeCmdResult = CommandUtil.createCmdForRevokePrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE SELECT ON SERVER serverTest FROM ROLE testRole WITH GRANT OPTION";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted, createRevokePrivilegeCmdResult);
+  }
+
+  // generate the command without grant option
+  @Test
+  public void testCreateCmdForGrantOrRevokeGMPrivilege1() {
+    org.apache.sentry.api.generic.thrift.TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantGMPrivilegeRequest();
+    org.apache.sentry.api.generic.thrift.TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokeGMPrivilegeRequest();
+    org.apache.sentry.api.generic.thrift.TSentryPrivilege privilege = getGMPrivilege();
+    grantRequest.setPrivilege(privilege);
+    revokeRequest.setPrivilege(privilege);
+
+    String createGrantPrivilegeCmdResult = CommandUtil.createCmdForGrantGMPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 TO ROLE testRole";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokeGMPrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 FROM ROLE testRole";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted, createRevokePrivilegeCmdResult);
+  }
+
+  // generate the command with grant option
+  @Test
+  public void testCreateCmdForGrantOrRevokeGMPrivilege2() {
+    org.apache.sentry.api.generic.thrift.TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantGMPrivilegeRequest();
+    org.apache.sentry.api.generic.thrift.TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokeGMPrivilegeRequest();
+    org.apache.sentry.api.generic.thrift.TSentryPrivilege privilege = getGMPrivilege();
+    privilege
+        .setGrantOption(org.apache.sentry.api.generic.thrift.TSentryGrantOption.TRUE);
+    grantRequest.setPrivilege(privilege);
+    revokeRequest.setPrivilege(privilege);
+
+    String createGrantPrivilegeCmdResult = CommandUtil.createCmdForGrantGMPrivilege(grantRequest);
+    String createGrantPrivilegeCmdExcepted = "GRANT ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 TO ROLE testRole WITH GRANT OPTION";
+    String createRevokePrivilegeCmdResult = CommandUtil
+        .createCmdForRevokeGMPrivilege(revokeRequest);
+    String createRevokePrivilegeCmdExcepted = "REVOKE ACTION ON resourceType1 resourceName1 resourceType2 resourceName2 FROM ROLE testRole WITH GRANT OPTION";
+
+    assertEquals(createGrantPrivilegeCmdExcepted, createGrantPrivilegeCmdResult);
+    assertEquals(createRevokePrivilegeCmdExcepted, createRevokePrivilegeCmdResult);
+  }
+
+  private String getGroupStr(int num) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < num; i++) {
+      if (i > 0) {
+        sb.append(", ");
+      }
+      sb.append("testGroup" + (i + 1));
+    }
+    return sb.toString();
+  }
+
+  private String getUserStr(int num) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < num; i++) {
+      if (i > 0) {
+        sb.append(", ");
+      }
+      sb.append("testUser" + (i + 1));
+    }
+    return sb.toString();
+  }
+
+  private TAlterSentryRoleGrantPrivilegeRequest getGrantPrivilegeRequest() {
+    TAlterSentryRoleGrantPrivilegeRequest request = new TAlterSentryRoleGrantPrivilegeRequest();
+    request.setRoleName("testRole");
+    return request;
+  }
+
+  private TAlterSentryRoleRevokePrivilegeRequest getRevokePrivilegeRequest() {
+    TAlterSentryRoleRevokePrivilegeRequest request = new TAlterSentryRoleRevokePrivilegeRequest();
+    request.setRoleName("testRole");
+    return request;
+  }
+
+  private org.apache.sentry.api.generic.thrift.TAlterSentryRoleGrantPrivilegeRequest getGrantGMPrivilegeRequest() {
+    org.apache.sentry.api.generic.thrift.TAlterSentryRoleGrantPrivilegeRequest request = new org.apache.sentry.api.generic.thrift.TAlterSentryRoleGrantPrivilegeRequest();
+    request.setRoleName("testRole");
+    return request;
+  }
+
+  private org.apache.sentry.api.generic.thrift.TAlterSentryRoleRevokePrivilegeRequest getRevokeGMPrivilegeRequest() {
+    org.apache.sentry.api.generic.thrift.TAlterSentryRoleRevokePrivilegeRequest request = new org.apache.sentry.api.generic.thrift.TAlterSentryRoleRevokePrivilegeRequest();
+    request.setRoleName("testRole");
+    return request;
+  }
+
+  private TSentryPrivilege getPrivilege(String action, String privilegeScope,
+      String dbName, String tableName, String serverName, String URI) {
+    TSentryPrivilege privilege = new TSentryPrivilege();
+    privilege.setAction(action);
+    privilege.setPrivilegeScope(privilegeScope);
+    privilege.setDbName(dbName);
+    privilege.setTableName(tableName);
+    privilege.setServerName(serverName);
+    privilege.setURI(URI);
+    return privilege;
+  }
+
+  private org.apache.sentry.api.generic.thrift.TSentryPrivilege getGMPrivilege() {
+    org.apache.sentry.api.generic.thrift.TSentryPrivilege privilege = new org.apache.sentry.api.generic.thrift.TSentryPrivilege();
+    privilege.setAction("ACTION");
+    privilege.setComponent("COMPONENT");
+    List<TAuthorizable> authorizables = new ArrayList<TAuthorizable>();
+    authorizables.add(new TAuthorizable("resourceType1", "resourceName1"));
+    authorizables.add(new TAuthorizable("resourceType2", "resourceName2"));
+    privilege.setAuthorizables(authorizables);
+    return privilege;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/model/TestMSentryUtil.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/model/TestMSentryUtil.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/model/TestMSentryUtil.java
new file mode 100644
index 0000000..34aa465
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/model/TestMSentryUtil.java
@@ -0,0 +1,106 @@
+/**
+ * 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.sentry.provider.db.service.model;
+
+import org.apache.sentry.hdfs.PathsUpdate;
+import org.apache.sentry.hdfs.PermissionsUpdate;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestMSentryUtil {
+
+  @Test
+  public void testMSentryUtilWithPathChanges() throws Exception {
+    List<MSentryPathChange> changes = new ArrayList<>();
+    PathsUpdate update = new PathsUpdate(1, false);
+
+    changes.add(new MSentryPathChange(1, "u1", update));
+    assertEquals("Collapsed string should match", "[1]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertTrue("List of changes should be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPathChange(2, "u2",update));
+    assertEquals("Collapsed string should match", "[1, 2]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertTrue("List of changes should be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPathChange(4, "u4",update));
+    assertEquals("Collapsed string should match", "[1, 2, 4]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPathChange(5, "u5",update));
+    assertEquals("Collapsed string should match", "[1, 2, 4, 5]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPathChange(6, "u6",update));
+    assertEquals("Collapsed string should match", "[1, 2, 4-6]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPathChange(8, "u8",update));
+    assertEquals("Collapsed string should match", "[1, 2, 4-6, 8]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+  }
+
+  @Test
+  public void testMSentryUtilWithPermChanges() throws Exception {
+    List<MSentryPermChange> changes = new ArrayList<>();
+    PermissionsUpdate update = new PermissionsUpdate(1, false);
+
+    changes.add(new MSentryPermChange(1, update));
+    assertEquals("Collapsed string should match", "[1]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertTrue("List of changes should be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPermChange(2, update));
+    assertEquals("Collapsed string should match", "[1, 2]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertTrue("List of changes should be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPermChange(4, update));
+    assertEquals("Collapsed string should match", "[1, 2, 4]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPermChange(5, update));
+    assertEquals("Collapsed string should match", "[1, 2, 4, 5]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPermChange(6, update));
+    assertEquals("Collapsed string should match", "[1, 2, 4-6]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+
+    changes.add(new MSentryPermChange(8, update));
+    assertEquals("Collapsed string should match", "[1, 2, 4-6, 8]",
+        MSentryUtil.collapseChangeIDsToString(changes));
+    assertFalse("List of changes should not be consecutive", MSentryUtil.isConsecutive(changes));
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/7db84b2f/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestCounterWait.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestCounterWait.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestCounterWait.java
new file mode 100644
index 0000000..8940154
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestCounterWait.java
@@ -0,0 +1,107 @@
+/*
+ * 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.sentry.provider.db.service.persistent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.Test;
+
+/**
+ * Test for CounterWait class
+ */
+public class TestCounterWait {
+  // Used to verify that wakeups happen in the right order
+  private final BlockingDeque<Long> outSyncQueue = new LinkedBlockingDeque<>();
+
+  @Test
+  public void testWaitFor() throws Exception {
+    // Create a thread for each waiter
+    int nthreads = 20;
+    ExecutorService executor = Executors.newFixedThreadPool(nthreads);
+
+    final CounterWait waiter = new CounterWait();
+
+    // Initial value is zero, so this shouldn't block
+    assertEquals(0, waiter.waitFor(0));
+
+    // Create a pair of threads waiting for each value in [1, nthreads / 2]
+    // We use pair of threads per value to verify that both are waken up
+    for (int i = 0; i < nthreads; i++) {
+      int finalI = i + 2;
+      final int val = finalI / 2;
+      executor.execute(new Runnable() {
+                         public void run() {
+                           long r = 0;
+                           try {
+                             r = waiter.waitFor(val); // blocks
+                           } catch (InterruptedException | TimeoutException e) {
+                             e.printStackTrace();
+                           }
+                           outSyncQueue.add(r); // Once we wake up, post result
+                         }
+                       }
+      );
+    }
+
+    // Wait until all threads are asleep.
+    while(waiter.waitersCount() < nthreads) {
+      sleep(20);
+    }
+
+    // All threads should be blocked, so outSyncQueue should be empty
+    assertTrue(outSyncQueue.isEmpty());
+
+    // Post a counter update for each value in [ 1, nthreads / 2 ]
+    // After eac update two threads should be waken up and the corresponding pair of
+    // values should appear in the outSyncQueue.
+    for (int i = 0; i < (nthreads / 2); i++) {
+      waiter.update(i + 1);
+      long r = outSyncQueue.takeFirst();
+      assertEquals(r, i + 1);
+      r = outSyncQueue.takeFirst();
+      assertEquals(r, i + 1);
+      assertTrue(outSyncQueue.isEmpty());
+    }
+
+    // We are done
+    executor.shutdown();
+  }
+
+  // Test for waitFor() timeout throwing TimeoutException
+  @Test(expected = TimeoutException.class)
+  public void testWaitForWithTimeout() throws Exception {
+    CounterWait waiter = new CounterWait(1, TimeUnit.MILLISECONDS);
+    waiter.waitFor(1); // Should throw exception
+  }
+
+  private void sleep(long ms) {
+    try {
+      Thread.sleep(ms);
+    } catch (InterruptedException e) {
+    }
+  }
+}