http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c8c88786/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/PersistentSentryStore.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/PersistentSentryStore.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/PersistentSentryStore.java
new file mode 100644
index 0000000..5c5df9e
--- /dev/null
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/PersistentSentryStore.java
@@ -0,0 +1,577 @@
+/**
+ * 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 java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.SentryUserException;
+import org.apache.sentry.provider.db.SentryAccessDeniedException;
+import org.apache.sentry.provider.db.SentryAlreadyExistsException;
+import org.apache.sentry.provider.db.SentryInvalidInputException;
+import org.apache.sentry.provider.db.SentryNoSuchObjectException;
+import org.apache.sentry.provider.db.service.thrift.TSentryActiveRoleSet;
+import org.apache.sentry.provider.db.service.thrift.TSentryAuthorizable;
+import org.apache.sentry.provider.db.service.thrift.TSentryGroup;
+import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege;
+import org.apache.sentry.provider.db.service.thrift.TSentryPrivilegeMap;
+import org.apache.sentry.provider.db.service.thrift.TSentryRole;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreOp;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreRecord;
+import org.apache.sentry.provider.db.service.thrift.TStoreSnapshot;
+
+import com.google.common.collect.Sets;
+
+/**
+ * A Decorator SentryStore that delegates all calls to a provided SentryStore
+ * after wrapping all writes with a PersistenceContext. The PersistenceContext
+ * encapsulates the Persistence strategy. This is determined by a subclass.
+ * The Subclass is responsible for creating the PersistenceContext before a
+ * method is called and after the method, the {@link 
#onSuccess(PersistentContext)} or
+ * {@link #onFailure(PersistentContext)} will be called with the context
+ * based on if the operation was successful or not on the underlying
+ * SentryStore
+ * 
+ * @param <T> A PersistentContext implementation
+ */
+public abstract class PersistentSentryStore
+<T extends PersistentSentryStore.PersistentContext> implements SentryStore {
+
+  /**
+   * Marker interface to be implemented by a subclass. The Context is passed
+   * back to the subclass in the onSuccess or onFailure calls after the
+   * operation is complete.
+   */
+  public static interface PersistentContext { 
+
+  }
+
+  private final SentryStore sentryStore;
+
+  public PersistentSentryStore(SentryStore sentryStore) {
+    this.sentryStore = sentryStore;
+  }
+
+  /**
+   * This Method is called before the write operations. Subclasses would 
ideally
+   * have to return an implementation of a PersistenceContext
+   * @param record A TSentryStoreRecord that encapsulates the operation and
+   *        all arguments
+   * @return A PersistenceContext
+   * @throws IOException
+   */
+  protected abstract T createRecord(TSentryStoreRecord record) throws 
IOException;
+
+  /**
+   * This method is called if the operation has been successfully applied on
+   * the underlying SentryStore
+   * @param context A PersistenceContext
+   */
+  protected abstract void onSuccess(T context);
+
+  /**
+   * This method is called if the underlying SentryStore rejected the write
+   * operation. The default implementation is to persist a NO-OP record.
+   * (This is so that implementing classes that rely on a monotonically
+   * increment by 1 sequence Ids do not have to deal with gaps in the
+   * sequence Ids)
+   * @param context
+   */
+  protected abstract void onFailure(T context);
+
+  protected SentryStore getStore() {
+    return sentryStore;
+  }
+
+  @Override
+  public Configuration getConfiguration() {
+    return sentryStore.getConfiguration();
+  }
+
+  protected void applyRecord(TSentryStoreRecord record) 
+      throws SentryUserException {
+    if ((record.getStoreOp() == TSentryStoreOp.SNAPSHOT) && 
(record.getSnapshot() != null)) {
+      sentryStore.fromSnapshot(record.getSnapshot());
+    } else if (record.getStoreOp() == TSentryStoreOp.CREATE_ROLE) {
+      sentryStore.createSentryRole(record.getRoleName());
+    } else if (record.getStoreOp() == TSentryStoreOp.DROP_ROLE) {
+      sentryStore.dropSentryRole(record.getRoleName());
+    } else if (record.getStoreOp() == TSentryStoreOp.GRANT_PRIVILEGES) {
+      sentryStore.alterSentryRoleGrantPrivileges(
+          record.getGrantorPrincipal(), record.getRoleName(),
+          record.getPrivileges());
+    } else if (record.getStoreOp() == TSentryStoreOp.REVOKE_PRVILEGES) {
+      sentryStore.alterSentryRoleRevokePrivileges(
+          record.getGrantorPrincipal(), record.getRoleName(),
+          record.getPrivileges());
+    } else if (record.getStoreOp() == TSentryStoreOp.ADD_GROUPS) {
+      Set<TSentryGroup> groups = new HashSet<TSentryGroup>();
+      for (String group : record.getGroups()) {
+        groups.add(new TSentryGroup(group));
+      }
+      sentryStore.alterSentryRoleAddGroups(
+          record.getGrantorPrincipal(), record.getRoleName(),
+          groups);
+    } else if (record.getStoreOp() == TSentryStoreOp.DEL_GROUPS) {
+      Set<TSentryGroup> groups = new HashSet<TSentryGroup>();
+      for (String group : record.getGroups()) {
+        groups.add(new TSentryGroup(group));
+      }
+      sentryStore.alterSentryRoleDeleteGroups(record.getRoleName(), groups);
+    } else if (record.getStoreOp() == TSentryStoreOp.DROP_PRIVILEGE) {
+      sentryStore.dropPrivilege(record.getAuthorizable());
+    } else if (record.getStoreOp() == TSentryStoreOp.RENAME_PRIVILEGE) {
+      sentryStore.renamePrivilege(record.getAuthorizable(),
+          record.getNewAuthorizable());
+    } else if (record.getStoreOp() == TSentryStoreOp.SET_VERSION) {
+      sentryStore.setSentryVersion(record.getVersion(),
+          record.getVersionComment());
+    } else if (record.getStoreOp() == TSentryStoreOp.NO_OP) {
+      // NO-OP
+    } else {
+      throw new RuntimeException("Unknown Sentry Store OP [" + 
record.getStoreOp() + "]");
+    }
+  }
+
+  @Override
+  public CommitContext createSentryRole(String roleName)
+      throws SentryAlreadyExistsException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.CREATE_ROLE);
+    record.setRoleName(roleName);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      CommitContext retVal = sentryStore.createSentryRole(roleName);
+      opSuccess = true;
+      onSuccess(pContext);
+      return retVal;
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryAlreadyExistsException) {
+        throw (SentryAlreadyExistsException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public CommitContext dropSentryRole(String roleName)
+      throws SentryNoSuchObjectException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.DROP_ROLE);
+    record.setRoleName(roleName);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      CommitContext retVal = sentryStore.dropSentryRole(roleName);
+      opSuccess = true;
+      onSuccess(pContext);
+      return retVal;
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryNoSuchObjectException) {
+        throw (SentryNoSuchObjectException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public CommitContext alterSentryRoleGrantPrivilege(String grantorPrincipal,
+      String roleName, TSentryPrivilege privilege) throws SentryUserException {
+    return alterSentryRoleGrantPrivileges(grantorPrincipal, roleName,
+        Sets.newHashSet(privilege));
+  }
+
+  @Override
+  public CommitContext alterSentryRoleGrantPrivileges(String grantorPrincipal,
+      String roleName, Set<TSentryPrivilege> privileges)
+      throws SentryUserException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.GRANT_PRIVILEGES);
+    record.setGrantorPrincipal(grantorPrincipal);
+    record.setRoleName(roleName);
+    record.setPrivileges(privileges);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      CommitContext retVal =
+          sentryStore.alterSentryRoleGrantPrivileges(grantorPrincipal,
+          roleName, privileges);
+      opSuccess = true;
+      onSuccess(pContext);
+      return retVal;
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryUserException) {
+        throw (SentryUserException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public CommitContext alterSentryRoleRevokePrivilege(String grantorPrincipal,
+      String roleName, TSentryPrivilege tPrivilege) throws SentryUserException 
{
+    return alterSentryRoleRevokePrivileges(grantorPrincipal, roleName,
+        Sets.newHashSet(tPrivilege));
+  }
+
+  @Override
+  public CommitContext alterSentryRoleRevokePrivileges(String grantorPrincipal,
+      String roleName, Set<TSentryPrivilege> tPrivileges)
+      throws SentryUserException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.REVOKE_PRVILEGES);
+    record.setGrantorPrincipal(grantorPrincipal);
+    record.setRoleName(roleName);
+    record.setPrivileges(tPrivileges);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      CommitContext retVal =
+          sentryStore.alterSentryRoleRevokePrivileges(grantorPrincipal,
+          roleName, tPrivileges);
+      opSuccess = true;
+      onSuccess(pContext);
+      return retVal;
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryUserException) {
+        throw (SentryUserException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public CommitContext alterSentryRoleAddGroups(String grantorPrincipal,
+      String roleName, Set<TSentryGroup> groupNames)
+      throws SentryNoSuchObjectException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.ADD_GROUPS);
+    record.setGrantorPrincipal(grantorPrincipal);
+    record.setRoleName(roleName);
+    Set<String> groups = new HashSet<String>();
+    for (TSentryGroup gr : groupNames) {
+      groups.add(gr.getGroupName());
+    }
+    record.setGroups(groups);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      CommitContext retVal =
+          sentryStore.alterSentryRoleAddGroups(grantorPrincipal,
+          roleName, groupNames);
+      opSuccess = true;
+      onSuccess(pContext);
+      return retVal;
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryNoSuchObjectException) {
+        throw (SentryNoSuchObjectException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public CommitContext alterSentryRoleDeleteGroups(String roleName,
+      Set<TSentryGroup> groupNames) throws SentryNoSuchObjectException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.DEL_GROUPS);
+    record.setRoleName(roleName);
+    Set<String> groups = new HashSet<String>();
+    for (TSentryGroup gr : groupNames) {
+      groups.add(gr.getGroupName());
+    }
+    record.setGroups(groups);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      CommitContext retVal =
+          sentryStore.alterSentryRoleDeleteGroups(roleName, groupNames);
+      opSuccess = true;
+      onSuccess(pContext);
+      return retVal;
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryNoSuchObjectException) {
+        throw (SentryNoSuchObjectException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public void dropPrivilege(TSentryAuthorizable tAuthorizable)
+      throws SentryNoSuchObjectException, SentryInvalidInputException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.DROP_PRIVILEGE);
+    record.setAuthorizable(tAuthorizable);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      sentryStore.dropPrivilege(tAuthorizable);
+      opSuccess = true;
+      onSuccess(pContext);
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryNoSuchObjectException) {
+        throw (SentryNoSuchObjectException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public void renamePrivilege(TSentryAuthorizable tAuthorizable,
+      TSentryAuthorizable newTAuthorizable) throws SentryNoSuchObjectException,
+      SentryInvalidInputException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.RENAME_PRIVILEGE);
+    record.setAuthorizable(tAuthorizable);
+    record.setNewAuthorizable(newTAuthorizable);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      sentryStore.renamePrivilege(tAuthorizable, newTAuthorizable);
+      opSuccess = true;
+      onSuccess(pContext);
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryNoSuchObjectException) {
+        throw (SentryNoSuchObjectException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public void setSentryVersion(String newVersion, String verComment)
+      throws SentryNoSuchObjectException, SentryAccessDeniedException {
+    TSentryStoreRecord record =
+        new TSentryStoreRecord(TSentryStoreOp.SET_VERSION);
+    record.setVersion(newVersion);
+    record.setVersionComment(verComment);
+    T pContext = null;
+    try {
+      pContext = createRecord(record);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Could not write record to Persistent Store [" + record + "]");
+    }
+    boolean opSuccess = false;
+    try {
+      sentryStore.setSentryVersion(newVersion, verComment);
+      opSuccess = true;
+      onSuccess(pContext);
+    } catch (Exception e) {
+      if (!opSuccess) {
+        onFailure(pContext);
+      }
+      if (e instanceof SentryNoSuchObjectException) {
+        throw (SentryNoSuchObjectException)e;
+      } else {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  @Override
+  public TSentryPrivilegeMap listSentryPrivilegesByAuthorizable(
+      Set<String> groups, TSentryActiveRoleSet activeRoles,
+      TSentryAuthorizable authHierarchy, boolean isAdmin)
+      throws SentryInvalidInputException {
+    return sentryStore.listSentryPrivilegesByAuthorizable(groups, activeRoles,
+        authHierarchy, isAdmin);
+  }
+
+  @Override
+  public Set<TSentryPrivilege> getAllTSentryPrivilegesByRoleName(String 
roleName)
+          throws SentryNoSuchObjectException {
+    return sentryStore.getAllTSentryPrivilegesByRoleName(roleName);
+  }
+
+  @Override
+  public Set<TSentryPrivilege> getTSentryPrivileges(Set<String> roleNames,
+      TSentryAuthorizable authHierarchy) throws SentryInvalidInputException {
+    return sentryStore.getTSentryPrivileges(roleNames, authHierarchy);
+  }
+
+  @Override
+  public Set<TSentryRole> getTSentryRolesByGroupName(Set<String> groupNames,
+      boolean checkAllGroups) throws SentryNoSuchObjectException {
+    return sentryStore.getTSentryRolesByGroupName(groupNames, checkAllGroups);
+  }
+
+  @Override
+  public Set<String> getRoleNamesForGroups(Set<String> groups) {
+    return sentryStore.getRoleNamesForGroups(groups);
+  }
+
+  @Override
+  public Set<String> listAllSentryPrivilegesForProvider(Set<String> groups,
+      TSentryActiveRoleSet roleSet) throws SentryInvalidInputException {
+    return sentryStore.listAllSentryPrivilegesForProvider(groups, roleSet);
+  }
+
+  @Override
+  public Set<String> listSentryPrivilegesForProvider(Set<String> groups,
+      TSentryActiveRoleSet roleSet, TSentryAuthorizable authHierarchy)
+      throws SentryInvalidInputException {
+    return sentryStore.listSentryPrivilegesForProvider(groups, roleSet, 
authHierarchy);
+  }
+
+  @Override
+  public boolean hasAnyServerPrivileges(Set<String> groups,
+      TSentryActiveRoleSet roleSet, String server) {
+    return sentryStore.hasAnyServerPrivileges(groups, roleSet, server);
+  }
+
+  @Override
+  public String getSentryVersion() throws SentryNoSuchObjectException,
+      SentryAccessDeniedException {
+    return sentryStore.getSentryVersion();
+  }
+
+  @Override
+  public Map<String, HashMap<String, String>> retrieveFullPrivilegeImage() {
+    return sentryStore.retrieveFullPrivilegeImage();
+  }
+
+  @Override
+  public Map<String, LinkedList<String>> retrieveFullRoleImage() {
+    return sentryStore.retrieveFullRoleImage();
+  }
+
+  @Override
+  public long getRoleCount() {
+    return sentryStore.getRoleCount();
+  }
+
+  @Override
+  public long getPrivilegeCount() {
+    return sentryStore.getPrivilegeCount();
+  }
+
+  @Override
+  public long getGroupCount() {
+    return sentryStore.getGroupCount();
+  }
+
+  @Override
+  public Set<String> getGroupsForRole(String roleName) {
+    return sentryStore.getGroupsForRole(roleName);
+  }
+
+  @Override
+  public void stop() {
+    sentryStore.stop();
+  }
+
+  @Override
+  public TStoreSnapshot toSnapshot() {
+    return sentryStore.toSnapshot();
+  }
+
+  @Override
+  public void fromSnapshot(TStoreSnapshot snapshot) {
+    sentryStore.fromSnapshot(snapshot);
+  }
+
+}

Reply via email to