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/SentryStoreWithFileLog.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithFileLog.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithFileLog.java
new file mode 100644
index 0000000..8f640a0
--- /dev/null
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithFileLog.java
@@ -0,0 +1,128 @@
+/**
+ * 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.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.sentry.SentryUserException;
+import org.apache.sentry.provider.db.service.persistent.FileLog.Entry;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreOp;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreRecord;
+import org.apache.thrift.TException;
+
+/**
+ * An implementation of the {@link PersistentSentryStore}. The Persistence
+ * strategy used by this class is to log all write operations to a local file
+ * using the {@link FileLog} log abstraction. Each write operation is stamp
+ * with a monotonically +1 incrementing sequence Id. This guarantees that, 
after
+ * a restart, the Sentry Store can read the log in the same order it was
+ * written and would return to the same state it was before it was brought 
down.
+ * The logging is also write-behind (operations are logged only after it has
+ * been accepted by the underlying store) to ensure that erroneous operations
+ * that would bring down the Store are not logged.
+ * 
+ * To limit the size of the log file, it requests the underlying SentryStore
+ * to provide it with a snapshot of the store after a configurable number of
+ * operations, which it writes to a new log file and and truncates the old 
one. 
+ *
+ */
+public class SentryStoreWithFileLog extends
+    PersistentSentryStore<SentryStoreWithFileLog.FileLogContext> {
+
+  public static final int SENTRY_STORE_FILE_LOG_SNAPSHOT_THRESHOLD_DEF = 100;
+  public static final String SENTRY_STORE_FILE_LOG_SNAPSHOT_THRESHOLD =
+      "sentry.store.file.log.snapshot.threshold";
+
+  /**
+   * An implementation of the {@link PersistentContext} that is created prior
+   * to the operation and stores the write record.
+   */
+  public static class FileLogContext implements
+      PersistentSentryStore.PersistentContext {
+    final long seqId;
+    final TSentryStoreRecord record;
+
+    FileLogContext(long seqId, TSentryStoreRecord record) {
+      this.seqId = seqId;
+      this.record = record;
+    }
+  }
+
+  protected final FileLog fileLog;
+  protected final AtomicLong lastSeenSeqId = new AtomicLong(0);
+  protected final int snapshotThreshold;
+
+  public SentryStoreWithFileLog(SentryStore sentryStore)
+      throws FileNotFoundException, IOException, TException, 
SentryUserException {
+    super(sentryStore);
+    snapshotThreshold =
+        getConfiguration().getInt(
+            SENTRY_STORE_FILE_LOG_SNAPSHOT_THRESHOLD,
+            SENTRY_STORE_FILE_LOG_SNAPSHOT_THRESHOLD_DEF);
+    fileLog = new FileLog(getConfiguration());
+    Entry ent = null;
+    while (fileLog.hasNext()) {
+      ent = fileLog.next();
+      applyRecord(ent.record);
+    }
+    if (ent != null) {
+      lastSeenSeqId.set(ent.seqId);
+    }
+  }
+
+  @Override
+  protected FileLogContext createRecord(TSentryStoreRecord record) {
+    return new FileLogContext(lastSeenSeqId.incrementAndGet(), record);
+  }
+
+  @Override
+  protected void onSuccess(FileLogContext context) {
+    fileLog.log(context.seqId,
+        getSnapshotIfRequired(context.seqId, context.record));
+  }
+
+  @Override
+  protected void onFailure(FileLogContext context) {
+    fileLog.log(context.seqId,
+        getSnapshotIfRequired(context.seqId,
+            new TSentryStoreRecord(TSentryStoreOp.NO_OP)));
+  }
+
+  protected TSentryStoreRecord getSnapshotIfRequired(long seqId, 
TSentryStoreRecord record) {
+    if ((seqId > 0) && (seqId % snapshotThreshold == 0)) {
+      if (record.getStoreOp() == TSentryStoreOp.SNAPSHOT) {
+        return record;
+      }
+      TSentryStoreRecord snapshotRecord = new 
TSentryStoreRecord(TSentryStoreOp.SNAPSHOT);
+      snapshotRecord.setSnapshot(getStore().toSnapshot());
+      return snapshotRecord;
+    } else {
+      return record;
+    }
+  }
+
+  @Override
+  public void stop() {
+    super.stop();
+    fileLog.close();
+  }
+
+}

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/SentryStoreWithLocalLock.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithLocalLock.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithLocalLock.java
new file mode 100644
index 0000000..42f4e7f
--- /dev/null
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithLocalLock.java
@@ -0,0 +1,65 @@
+/**
+ * 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.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * An implementation of {@link LockingSentryStore} that implements the
+ * Locking strategy using the standard
+ * {@link java.util.concurrent.locks.ReentrantReadWriteLock}
+ *
+ */
+public class SentryStoreWithLocalLock extends
+    LockingSentryStore<SentryStoreWithLocalLock.ThreadSafeContext> {
+
+  public static class ThreadSafeContext implements
+      LockingSentryStore.LockContext {
+    final Lock lock;
+
+    public ThreadSafeContext(Lock lock) {
+      this.lock = lock;
+    }
+
+    @Override
+    public void unlock() {
+      lock.unlock();
+    }
+  }
+
+  private final ReadWriteLock rwLock;
+
+  public SentryStoreWithLocalLock(SentryStore sentryStore) {
+    super(sentryStore);
+    this.rwLock = new ReentrantReadWriteLock();
+  }
+
+  @Override
+  protected ThreadSafeContext writeLock() {
+    return new ThreadSafeContext(rwLock.writeLock());
+  }
+
+  @Override
+  protected ThreadSafeContext readLock() {
+    return new ThreadSafeContext(rwLock.readLock());
+  }
+
+}

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/SentryStoreWithReplicatedLog.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithReplicatedLog.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithReplicatedLog.java
new file mode 100644
index 0000000..0ecf024
--- /dev/null
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreWithReplicatedLog.java
@@ -0,0 +1,377 @@
+/**
+ * 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.FileNotFoundException;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hive.com.esotericsoftware.minlog.Log;
+import org.apache.sentry.SentryUserException;
+import org.apache.sentry.provider.db.service.persistent.FileLog.Entry;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreOp;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreRecord;
+import org.apache.thrift.TDeserializer;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.IAtomicLong;
+import com.hazelcast.core.IQueue;
+import com.hazelcast.core.ITopic;
+import com.hazelcast.core.Member;
+import com.hazelcast.core.Message;
+import com.hazelcast.core.MessageListener;
+
+/**
+ * A special subclass of the {@link SentryStoreWithFileLog} that uses the
+ * same persistence strategy but in addition to logging to a local
+ * {@link FileLog} will also publish the record to a distributed topic to be
+ * consumed by other peer Stores in a Sentry cluster.
+ * 
+ * The class uses the Hazelcast library for :
+ * 1) Distributed counter for sequence Id
+ * 2) Distributed Topic with global ordering to replicate log entries to peer
+ *    SentryStores
+ *
+ * Consistency Guarantees:
+ * Hazelcast counters are globally consistent, but since it is optimized for
+ * availability rather than consistency it can suffer from split-brain issues
+ * in the event of a network partition. This should not be too much of an issue
+ * for small deployments of 2-3 instances deployed in the same availability
+ * zone, But strict consistency can be guaranteed by either:
+ * 1) Wrapping this store with a {@link SentryStoreWithDistributedLock} which
+ *    uses the Curator library's distributed read write lock which is based
+ *    on Zookeeper. Zookeper has a stricter consistency model since all writes
+ *    go thru a master.
+ * 2) Deploying SentryService in active-standy mode which ensures writes are
+ *    handled only by the active node. This can be trivially
+ *    accomplished by setting the serivce discovery policy in the
+ *    SentrtyPolicyServiceClient to 'STICKY' rather than the default
+ *    'ROUND-ROBIN'
+ *
+ * Other Considerations:
+ * This implementation also supports new peers joining an existing cluster.
+ * The new peer will be brought upto speed with the other members of the
+ * cluster in the following manner:
+ * 1) All members listen to a special 'catchupRequest' topic.
+ * 2) When a new member starts-up, it publishes a 'CatchupRequest' to the
+ *    'catchUpRequest' topic. The CatchupRequest contains the last seen
+ *    record's sequence number from its local FileLog.
+ * 3) The new member also creates a uniquely named Distributed Queue and waits
+ *    for messages on it. This queue name is also included in the
+ *    CatchupRequest.
+ * 4) This request is received by all the existing members of the cluster, it
+ *    is ignored by all members EXCEPT the oldest member of the cluster which
+ *    responds to the request by pushing the required records to the
+ *    Distributed queue.
+ */
+
+public class SentryStoreWithReplicatedLog extends SentryStoreWithFileLog {
+
+  private static final Logger LOGGER = LoggerFactory
+      .getLogger(SentryStoreWithReplicatedLog.class);
+
+  private static final int WAIT_SLEEP_MS = 500;
+
+  /**
+   * Only successful events are sent to the topic (failed records are sent as
+   * no-op so that no gaps exist in the seqId)
+   * Basically write behind logging.. If the publisher crashes before
+   * writing to log, the update is not applied to the remote nodes.
+   * Think this should be fine (It is similar to the case when Sentry
+   * Service receives a message but dies while/before opening a db transaction)
+   */
+  public static class LogEntry implements Serializable {
+    private static final long serialVersionUID = 8798360797372277777L;
+
+    long seqId = -1;
+    long nodeId = -1;
+    byte[] recordBytes;
+
+    public LogEntry() {}
+
+    public LogEntry(long seqId, long nodeId, byte[] recordBytes) {
+      this.seqId = seqId;
+      this.nodeId = nodeId;
+      this.recordBytes = recordBytes;
+    }
+  }
+
+  public static class CatchUpRequest implements Serializable {
+    private static final long serialVersionUID = -3345746198249394847L;
+
+    long startSeqId = -1;
+    long endSeqId = -1;
+    long nodeId = -1;
+
+    public CatchUpRequest() {}
+
+    public CatchUpRequest(long startSeqId, long endSeqId, long nodeId) {
+      this.startSeqId = startSeqId;
+      this.endSeqId = endSeqId;
+      this.nodeId = nodeId;
+    }
+  }
+
+  class LogEntryWorker implements Runnable{
+    @Override
+    public void run() {
+      while(true) {
+        try {
+          processLogEntry(entryQueue.take());
+        } catch (InterruptedException e) {
+          Thread.currentThread().interrupt();
+        } catch (SentryUserException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    }
+  }
+
+  private final TSerializer serializer;
+  private final TDeserializer deserializer;
+  private final BlockingQueue<LogEntry> entryQueue =
+      new LinkedBlockingQueue<LogEntry>();
+  private Thread entryWorker;
+
+  // Distributed stuff
+  private final HazelcastInstance hInst;
+  private final long nodeId; 
+  private final IAtomicLong globalSeqId;
+  private final ITopic<LogEntry> dTopic;
+  private final ITopic<CatchUpRequest> catchUpReqTopic;
+
+  public SentryStoreWithReplicatedLog(SentryStore sentryStore)
+      throws FileNotFoundException, IOException, TException,
+      SentryUserException {
+    this(sentryStore,
+        DistributedUtils.getHazelcastInstance(sentryStore.getConfiguration(),
+            true));
+  }
+
+  public SentryStoreWithReplicatedLog(SentryStore sentryStore,
+      final HazelcastInstance hInst) throws FileNotFoundException, IOException,
+      TException, SentryUserException {
+    super(sentryStore);
+    this.hInst = hInst;
+    TProtocolFactory protoFactory = new TCompactProtocol.Factory();
+    serializer = new TSerializer(protoFactory);
+    deserializer = new TDeserializer(protoFactory);
+
+    nodeId = 
hInst.getIdGenerator(DistributedUtils.SENTRY_STORE_NODEID).newId();
+    globalSeqId = hInst.getAtomicLong(DistributedUtils.SENTRY_STORE_SEQID);
+    dTopic = hInst.getTopic(DistributedUtils.SENTRY_DISTRIBUTED_TOPIC);
+    dTopic.addMessageListener(new 
MessageListener<SentryStoreWithReplicatedLog.LogEntry>() {
+      @Override
+      public void onMessage(Message<LogEntry> msg) {
+        LogEntry ent = msg.getMessageObject();
+        try {
+          entryQueue.put(ent);
+        } catch (InterruptedException e) {
+          Thread.currentThread().interrupt();
+        }
+      }
+    });
+
+    catchUpReqTopic = 
hInst.getTopic(DistributedUtils.SENTRY_CATCHUP_REQUEST_TOPIC);
+    requestCatchUpIfRequired();
+    catchUpReqTopic.addMessageListener(new MessageListener<CatchUpRequest>() {
+      @Override
+      public void onMessage(final Message<CatchUpRequest> req) {
+        LOGGER.info("Catchup request received["
+            + req.getMessageObject().startSeqId + ", "
+            + req.getMessageObject().endSeqId + ", "
+            + req.getMessageObject().nodeId + "]");
+        handleCatchupRequest(req.getMessageObject(), hInst);
+      }
+    });
+
+    entryWorker = new Thread(new LogEntryWorker(), "Log Entry Worker");
+    entryWorker.start();
+  }
+
+  private void handleCatchupRequest(CatchUpRequest req, HazelcastInstance 
hInst) {
+    Member oldestMember = hInst.getCluster().getMembers().iterator().next();
+    // Am I oldest member ?
+    if (oldestMember.localMember()) {
+      long myLastSeen = lastSeenSeqId.get();
+      if (req.endSeqId > myLastSeen) {
+        LOGGER.info("Waiting for seq Id[" + myLastSeen + ", " + req.endSeqId + 
"]");
+        try {
+          Thread.sleep(getConfiguration().getInt(
+              DistributedUtils.SENTRY_CATCHUP_WAIT_TIME,
+              DistributedUtils.SENTRY_CATCHUP_WAIT_TIME_DEF));
+        } catch (InterruptedException e) {
+          Thread.currentThread().interrupt();
+        }
+      }
+      // Still not updated ?
+      if (req.endSeqId > myLastSeen) {
+        throw new RuntimeException(
+            "Havnt recieved latest updated.. cannot respond to catchup request 
!!");
+      }
+      FileLog tempLog = null;
+      try {
+        tempLog = new FileLog(getStore().getConfiguration());
+      } catch (Exception e) {
+        throw new RuntimeException("Could not open FileLog to send catchup 
entries !!");
+      }
+
+      IQueue<LogEntry> respQueue =
+          hInst.getQueue(DistributedUtils.SENTRY_CATCHUP_RESPONSE_QUEUE + 
req.nodeId);
+      while (tempLog.hasNext()) {
+        Entry entry = tempLog.next();
+        if ((entry.seqId >= req.startSeqId)&&(entry.seqId <= req.endSeqId)) {
+          try {
+            respQueue.offer(new LogEntry(entry.seqId, nodeId, 
serializer.serialize(entry.record)));
+            LOGGER.info("Sent Catchup entry [" + entry.seqId + ","
+                + nodeId + ", " + req.nodeId + "]");
+          } catch (TException e) {
+            throw new RuntimeException("Could not send catchup entry !!", e);
+          }
+        }
+      }
+    }
+  }
+
+  private void requestCatchUpIfRequired() throws SentryUserException {
+    long startSeqId = lastSeenSeqId.get() + 1;
+    long endSeqId = globalSeqId.get();
+    if (startSeqId <= endSeqId) {
+      LOGGER.info("Sending Catchup request ["
+          + startSeqId + ", "
+          + endSeqId + ", "
+          + nodeId + "]");
+      // Send request for catchup entries
+      IQueue<LogEntry> respQueue =
+          hInst.getQueue(DistributedUtils.SENTRY_CATCHUP_RESPONSE_QUEUE + 
nodeId);
+      catchUpReqTopic.publish(
+          new CatchUpRequest(startSeqId, endSeqId, nodeId));
+      receiveCatchUpEntries(respQueue, endSeqId);
+    }
+  }
+
+  private void receiveCatchUpEntries(IQueue<LogEntry> catchUpQueue,
+      long endSeqId) throws SentryUserException {
+    long currSeqId = -1;
+    while (currSeqId < endSeqId) {
+      try {
+        LogEntry entry =
+            catchUpQueue.poll(
+                getConfiguration().getInt(
+                    DistributedUtils.SENTRY_CATCHUP_WAIT_TIME,
+                    DistributedUtils.SENTRY_CATCHUP_WAIT_TIME_DEF),
+                TimeUnit.MILLISECONDS);
+        if (entry == null) {
+          String msg =
+              "Havnt received all catchup entries [" + currSeqId + ", "
+                  + endSeqId + "]!!";
+          LOGGER.error(msg);
+          throw new RuntimeException(msg);
+        }
+        LOGGER.info("Received catchup [" + entry.seqId + ", " + entry.nodeId + 
"]");
+        currSeqId = entry.seqId;
+        processLogEntry(entry);
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+      }
+    }
+    catchUpQueue.destroy();
+  }
+
+  private void processLogEntry(LogEntry ent) throws SentryUserException {
+    TSentryStoreRecord record = new TSentryStoreRecord();
+    try {
+      deserializer.deserialize(record, ent.recordBytes);
+    } catch (TException e) {
+      String msg = "Could not de-serialize record [" + ent.seqId + "]!!";
+      Log.error(msg, e);
+      throw new RuntimeException(msg, e);
+    }
+    // No need to update the publisher (Thats already done)
+    if (ent.nodeId != SentryStoreWithReplicatedLog.this.nodeId) {
+      try {
+        applyRecord(record);
+        fileLog.log(ent.seqId, getSnapshotIfRequired(ent.seqId, record));
+        lastSeenSeqId.set(ent.seqId);
+      } catch (SentryUserException e) {
+        String msg = "Could not apply de-serialized record [" + ent.seqId + 
"]!!";
+        Log.error(msg, e);
+        throw new RuntimeException(msg, e);
+      }
+    }
+  }
+
+  @Override
+  protected FileLogContext createRecord(TSentryStoreRecord record) {
+    return new FileLogContext(globalSeqId.incrementAndGet(), record);
+  }
+
+  @Override
+  protected void onSuccess(FileLogContext context) {
+    super.onSuccess(context);
+    lastSeenSeqId.set(context.seqId);
+    try {
+      dTopic.publish(new LogEntry(context.seqId, nodeId, serializer
+          .serialize(context.record)));
+    } catch (TException e) {
+      throw new RuntimeException("Could not serialize Sentry record !!");
+    }
+  }
+
+  @Override
+  protected void onFailure(FileLogContext context) {
+    // Publish a NO-OP record (since we dont want any gaps in the seqId)
+    super.onFailure(context);
+    lastSeenSeqId.set(context.seqId);
+    try {
+      dTopic.publish(new LogEntry(context.seqId, nodeId, serializer
+          .serialize(new TSentryStoreRecord(TSentryStoreOp.NO_OP))));
+    } catch (TException e) {
+      throw new RuntimeException("Could not serialize Sentry record !!");
+    }
+  }
+
+  public boolean waitForReplicattionToComplete(long timeInMs) {
+    long totalWait = 0;
+    while (true) {
+      if (lastSeenSeqId.get() == globalSeqId.get()) {
+        return true;
+      }
+      try {
+        Thread.sleep(WAIT_SLEEP_MS);
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+        return false;
+      }
+      totalWait += WAIT_SLEEP_MS;
+      if (totalWait >= timeInMs) {
+        return false;
+      }
+    }
+  }
+}

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/StoreUtils.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/StoreUtils.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/StoreUtils.java
new file mode 100644
index 0000000..d23685b
--- /dev/null
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/StoreUtils.java
@@ -0,0 +1,102 @@
+/**
+ * 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.apache.sentry.provider.common.ProviderConstants.AUTHORIZABLE_JOINER;
+import static org.apache.sentry.provider.common.ProviderConstants.KV_JOINER;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sentry.core.model.db.AccessConstants;
+import org.apache.sentry.core.model.db.DBModelAuthorizable.AuthorizableType;
+import org.apache.sentry.provider.common.ProviderConstants;
+import org.apache.sentry.provider.db.service.model.MSentryPrivilege;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+
+public class StoreUtils {
+
+  public static String NULL_COL = "__NULL__";
+
+  @VisibleForTesting
+  public static String toAuthorizable(MSentryPrivilege privilege) {
+    return toAuthorizable(privilege.getServerName(), privilege.getDbName(),
+        privilege.getURI(), privilege.getTableName(), 
privilege.getColumnName(),
+        privilege.getAction());
+  }
+
+  @VisibleForTesting
+  public static String toAuthorizable(String serverName, String dbName,
+      String uri, String tableName, String columnName, String action) {
+    List<String> authorizable = new ArrayList<String>(4);
+    
authorizable.add(KV_JOINER.join(AuthorizableType.Server.name().toLowerCase(),
+        serverName));
+    if (isNULL(uri)) {
+      if (!isNULL(dbName)) {
+        
authorizable.add(KV_JOINER.join(AuthorizableType.Db.name().toLowerCase(),
+            dbName));
+        if (!isNULL(tableName)) {
+          
authorizable.add(KV_JOINER.join(AuthorizableType.Table.name().toLowerCase(),
+              tableName));
+          if (!isNULL(columnName)) {
+            
authorizable.add(KV_JOINER.join(AuthorizableType.Column.name().toLowerCase(),
+                columnName));
+          }
+        }
+      }
+    } else {
+      
authorizable.add(KV_JOINER.join(AuthorizableType.URI.name().toLowerCase(),
+          uri));
+    }
+    if (!isNULL(action)
+        && !action.equalsIgnoreCase(AccessConstants.ALL)) {
+      authorizable
+      .add(KV_JOINER.join(ProviderConstants.PRIVILEGE_NAME.toLowerCase(),
+          action));
+    }
+    return AUTHORIZABLE_JOINER.join(authorizable);
+  }
+
+  @VisibleForTesting
+  public static Set<String> toTrimedLower(Set<String> s) {
+    if (null == s) return new HashSet<String>();
+    Set<String> result = Sets.newHashSet();
+    for (String v : s) {
+      result.add(v.trim().toLowerCase());
+    }
+    return result;
+  }
+  
+  public static String toNULLCol(String s) {
+    return Strings.isNullOrEmpty(s) ? NULL_COL : s;
+  }
+
+  public static String fromNULLCol(String s) {
+    return isNULL(s) ? "" : s;
+  }
+
+  public static boolean isNULL(String s) {
+    return Strings.isNullOrEmpty(s) || s.equals(NULL_COL);
+  }
+}

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/thrift/SentryMetrics.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryMetrics.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryMetrics.java
index 55bec0b..0dd10f6 100644
--- 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryMetrics.java
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryMetrics.java
@@ -28,6 +28,8 @@ import com.codahale.metrics.jvm.BufferPoolMetricSet;
 import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
 import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
 import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
+
+import org.apache.sentry.provider.db.service.persistent.DbSentryStore;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
 
 import java.lang.management.ManagementFactory;
@@ -84,15 +86,36 @@ public class SentryMetrics {
     return sentryMetrics;
   }
 
-  public void addSentryStoreGauges(SentryStore sentryStore) {
+  public void addSentryStoreGauges(final SentryStore sentryStore) {
     if(!gaugesAdded) {
-      addGauge(SentryStore.class, "role_count", 
sentryStore.getRoleCountGauge());
-      addGauge(SentryStore.class, "privilege_count", 
sentryStore.getPrivilegeCountGauge());
-      addGauge(SentryStore.class, "group_count", 
sentryStore.getGroupCountGauge());
+      addGauge(SentryStore.class, "role_count", new Gauge<Long>() {
+        @Override
+        public Long getValue() {
+          return sentryStore.getRoleCount();
+        }});
+      addGauge(SentryStore.class, "privilege_count", new Gauge<Long>() {
+        @Override
+        public Long getValue() {
+          return sentryStore.getPrivilegeCount();
+        }});
+      addGauge(SentryStore.class, "group_count", new Gauge<Long>() {
+        @Override
+        public Long getValue() {
+          return sentryStore.getGroupCount();
+        }});
       gaugesAdded = true;
     }
   }
 
+//@Override
+//public Gauge<Long> getGroupCountGauge() {
+//  return new Gauge< Long >() {
+//    @Override
+//    public Long getValue() {
+//      return getCount(MSentryGroup.class);
+//    }
+//  };
+//}
 
   /* Should be only called once to initialize the reporters
    */

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/thrift/SentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
index 29e3131..1ffc496 100644
--- 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
@@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import com.codahale.metrics.Timer;
+
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
@@ -52,8 +53,10 @@ import 
org.apache.sentry.provider.db.log.entity.JsonLogEntityFactory;
 import org.apache.sentry.provider.db.log.util.Constants;
 import org.apache.sentry.provider.db.service.persistent.CommitContext;
 import org.apache.sentry.provider.db.service.persistent.HAContext;
-import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.provider.db.service.persistent.ServiceRegister;
+import org.apache.sentry.provider.db.service.persistent.DbSentryStore;
+import org.apache.sentry.provider.db.service.persistent.SentryStore;
+import org.apache.sentry.provider.db.service.persistent.SentryStoreFactory;
 import 
org.apache.sentry.provider.db.service.thrift.PolicyStoreConstants.PolicyStoreServerConfig;
 import org.apache.sentry.service.thrift.ServiceConstants.ConfUtilties;
 import org.apache.sentry.service.thrift.ServiceConstants.ClientConfig;
@@ -103,13 +106,13 @@ public class SentryPolicyStoreProcessor implements 
SentryPolicyService.Iface {
     isReady = false;
     if(conf.getBoolean(ServerConfig.SENTRY_HA_ENABLED,
         ServerConfig.SENTRY_HA_ENABLED_DEFAULT)){
-      haContext = new HAContext(conf);
-      sentryStore = new SentryStore(conf);
+      haContext = HAContext.get(conf);
+      sentryStore = SentryStoreFactory.createSentryStore(conf);
       ServiceRegister reg = new ServiceRegister(haContext);
       reg.regService(conf.get(ServerConfig.RPC_ADDRESS),
           conf.getInt(ServerConfig.RPC_PORT,ServerConfig.RPC_PORT_DEFAULT));
     } else {
-      sentryStore = new SentryStore(conf);
+      sentryStore = SentryStoreFactory.createSentryStore(conf);
     }
     isReady = true;
     adminGroups = 
ImmutableSet.copyOf(toTrimedLower(Sets.newHashSet(conf.getStrings(

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c8c88786/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HAClientInvocationHandler.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HAClientInvocationHandler.java
 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HAClientInvocationHandler.java
index c6e265f..4e371f4 100644
--- 
a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HAClientInvocationHandler.java
+++ 
b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HAClientInvocationHandler.java
@@ -51,7 +51,7 @@ public class HAClientInvocationHandler implements 
InvocationHandler {
 
   public HAClientInvocationHandler(Configuration conf) throws Exception {
     this.conf = conf;
-    manager = new ServiceManager(new HAContext(conf));
+    manager = new ServiceManager(HAContext.get(conf));
     checkClientConf();
     renewSentryClient();
   }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c8c88786/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
 
b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
index 993ea46..23f09d7 100644
--- 
a/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
+++ 
b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
@@ -52,11 +52,65 @@ struct TSentryPrivilege {
 10: optional string columnName = "",
 }
 
+struct TSentryAuthorizable {
+1: required string server,
+2: optional string uri,
+3: optional string db,
+4: optional string table,
+5: optional string column,
+}
+
+enum TSentryStoreOp {
+  CREATE_ROLE = 0,
+  DROP_ROLE = 1,
+  GRANT_PRIVILEGES = 2,
+  REVOKE_PRVILEGES = 3,
+  ADD_GROUPS = 4,
+  DEL_GROUPS = 5,
+  SET_VERSION = 6,
+  DROP_PRIVILEGE = 7,
+  RENAME_PRIVILEGE = 8,
+  SNAPSHOT = 9,
+  NO_OP = 100
+}
+
+struct TStorePrivilege {
+1: required TSentryGrantOption grantOption,
+2: required i16 privilege 
+}
+
+struct TStoreAuthorizable {
+1: required string name,
+2: required string type,
+3: optional map<string, TStorePrivilege> privileges,
+4: optional set<i32> children 
+}
+
+struct TStoreSnapshot {
+1: required map<string, TStoreAuthorizable> rootAuthorizable,
+2: required map<string, set<string>> roleToGroups,
+3: required map<i32, TStoreAuthorizable> objIds
+}
+
 # TODO can this be deleted? it's not adding value to 
TAlterSentryRoleAddGroupsRequest
 struct TSentryGroup {
 1: required string groupName
 }
 
+# Represents a Privilege in transport from the client to the server
+struct TSentryStoreRecord {
+1: required TSentryStoreOp storeOp,
+2: optional string roleName,
+3: optional string grantorPrincipal,
+4: optional set<TSentryPrivilege> privileges,
+5: optional set<string> groups,
+6: optional TSentryAuthorizable authorizable,
+7: optional TSentryAuthorizable newAuthorizable,
+8: optional string version,
+9: optional string versionComment,
+10: optional TStoreSnapshot snapshot
+}
+
 # CREATE ROLE r1
 struct TCreateSentryRoleRequest {
 1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
@@ -143,14 +197,6 @@ struct TListSentryRolesResponse {
 2: optional set<TSentryRole> roles
 }
 
-struct TSentryAuthorizable {
-1: required string server,
-2: optional string uri,
-3: optional string db,
-4: optional string table,
-5: optional string column,
-}
-
 # SHOW GRANT
 struct TListSentryPrivilegesRequest {
 1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c8c88786/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLog.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLog.java
 
b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLog.java
new file mode 100644
index 0000000..95acd1c
--- /dev/null
+++ 
b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLog.java
@@ -0,0 +1,77 @@
+/**
+ * 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.File;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreOp;
+import org.apache.sentry.provider.db.service.thrift.TSentryStoreRecord;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.io.Files;
+
+public class TestFileLog {
+
+  private String logDir;
+
+  @Before
+  public void setup() {
+    logDir = Files.createTempDir().getAbsolutePath();
+    System.out.println("Creating dir : [" + logDir + "]");
+  }
+
+  @After
+  public void tearDown() {
+    File l = new File(logDir);
+    for (File f : l.listFiles()) {
+      f.delete();
+    }
+    l.delete();
+  }
+
+  @Test
+  public void testReadWriteLog() throws Exception {
+    Configuration conf = new Configuration(false);
+    conf.set(FileLog.SENTRY_FILE_LOG_STORE_LOCATION, logDir);
+    FileLog fileLog = new FileLog(conf);
+    fileLog.log(1, new TSentryStoreRecord(TSentryStoreOp.CREATE_ROLE));
+    fileLog.log(2, new TSentryStoreRecord(TSentryStoreOp.GRANT_PRIVILEGES));
+    fileLog.log(3, new TSentryStoreRecord(TSentryStoreOp.REVOKE_PRVILEGES));
+    fileLog.log(4, new TSentryStoreRecord(TSentryStoreOp.ADD_GROUPS));
+    fileLog.log(5, new TSentryStoreRecord(TSentryStoreOp.DEL_GROUPS));
+    fileLog.close();
+
+    fileLog = new FileLog(conf);
+    Assert.assertTrue(fileLog.hasNext());
+    Assert.assertEquals(TSentryStoreOp.CREATE_ROLE, 
fileLog.next().record.getStoreOp());
+    Assert.assertTrue(fileLog.hasNext());
+    Assert.assertEquals(TSentryStoreOp.GRANT_PRIVILEGES, 
fileLog.next().record.getStoreOp());
+    Assert.assertTrue(fileLog.hasNext());
+    Assert.assertEquals(TSentryStoreOp.REVOKE_PRVILEGES, 
fileLog.next().record.getStoreOp());
+    Assert.assertTrue(fileLog.hasNext());
+    Assert.assertEquals(TSentryStoreOp.ADD_GROUPS, 
fileLog.next().record.getStoreOp());
+    Assert.assertTrue(fileLog.hasNext());
+    Assert.assertEquals(TSentryStoreOp.DEL_GROUPS, 
fileLog.next().record.getStoreOp());
+    fileLog.close();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c8c88786/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLoggingSentryStore.java
----------------------------------------------------------------------
diff --git 
a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLoggingSentryStore.java
 
b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLoggingSentryStore.java
new file mode 100644
index 0000000..13f459e
--- /dev/null
+++ 
b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestFileLoggingSentryStore.java
@@ -0,0 +1,166 @@
+/**
+ * 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 junit.framework.Assert.assertEquals;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.core.model.db.AccessConstants;
+import org.apache.sentry.provider.db.SentryInvalidInputException;
+import org.apache.sentry.provider.db.service.thrift.TSentryActiveRoleSet;
+import org.apache.sentry.provider.db.service.thrift.TSentryGroup;
+import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+public class TestFileLoggingSentryStore extends TestInMemSentryStore{
+  
+  private String logDir;
+
+  @Override
+  public void setup() throws Exception {
+    super.setup();
+    logDir = Files.createTempDir().getAbsolutePath();
+    Configuration conf = new Configuration(false);
+    conf.set(FileLog.SENTRY_FILE_LOG_STORE_LOCATION, logDir);
+    sentryStore = new SentryStoreWithLocalLock(new 
SentryStoreWithFileLog(sentryStore));
+  }
+
+  @Test
+  public void testPersistence() throws Exception {
+    String roleName1 = "list-privs-r1", roleName2 = "list-privs-r2";
+    String groupName1 = "list-privs-g1", groupName2 = "list-privs-g2";
+    String grantor = "g1";
+    long seqId = sentryStore.createSentryRole(roleName1).getSequenceId();
+    assertEquals(seqId + 1, 
sentryStore.createSentryRole(roleName2).getSequenceId());
+    TSentryPrivilege privilege1 = new TSentryPrivilege();
+    privilege1.setPrivilegeScope("TABLE");
+    privilege1.setServerName("server1");
+    privilege1.setDbName("db1");
+    privilege1.setTableName("tbl1");
+    privilege1.setAction("SELECT");
+    privilege1.setCreateTime(System.currentTimeMillis());
+    assertEquals(seqId + 2, sentryStore.alterSentryRoleGrantPrivilege(grantor, 
roleName1, privilege1)
+        .getSequenceId());
+    assertEquals(seqId + 3, sentryStore.alterSentryRoleGrantPrivilege(grantor, 
roleName2, privilege1)
+        .getSequenceId());
+    TSentryPrivilege privilege2 = new TSentryPrivilege();
+    privilege2.setPrivilegeScope("SERVER");
+    privilege2.setServerName("server1");
+    privilege2.setAction(AccessConstants.ALL);
+    privilege2.setCreateTime(System.currentTimeMillis());
+    assertEquals(seqId + 4, sentryStore.alterSentryRoleGrantPrivilege(grantor, 
roleName2, privilege2)
+        .getSequenceId());
+    Set<TSentryGroup> groups = Sets.newHashSet();
+    TSentryGroup group = new TSentryGroup();
+    group.setGroupName(groupName1);
+    groups.add(group);
+    assertEquals(seqId + 5, sentryStore.alterSentryRoleAddGroups(grantor,
+        roleName1, groups).getSequenceId());
+    groups.clear();
+    group = new TSentryGroup();
+    group.setGroupName(groupName2);
+    groups.add(group);
+    // group 2 has both roles 1 and 2
+    assertEquals(seqId + 6, sentryStore.alterSentryRoleAddGroups(grantor,
+        roleName1, groups).getSequenceId());
+    assertEquals(seqId + 7, sentryStore.alterSentryRoleAddGroups(grantor,
+        roleName2, groups).getSequenceId());
+    verifyStore(roleName1, roleName2, groupName1, groupName2);
+
+    // KILL The store and restart using same directory..
+    Configuration conf = new Configuration(false);
+    conf.set(FileLog.SENTRY_FILE_LOG_STORE_LOCATION, logDir);
+    sentryStore = new SentryStoreWithLocalLock(new 
SentryStoreWithFileLog(sentryStore));
+
+    verifyStore(roleName1, roleName2, groupName1, groupName2);
+  }
+
+  private void verifyStore(String roleName1, String roleName2,
+      String groupName1, String groupName2) throws SentryInvalidInputException 
{
+    // group1 all roles
+    
assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1),
+            new TSentryActiveRoleSet(true, new HashSet<String>()))));
+    // one active role
+    
assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1),
+            new TSentryActiveRoleSet(false, Sets.newHashSet(roleName1)))));
+    // unknown active role
+    assertEquals(Sets.newHashSet(),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1),
+            new TSentryActiveRoleSet(false, Sets.newHashSet("not a role")))));
+    // no active roles
+    assertEquals(Sets.newHashSet(),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1),
+            new TSentryActiveRoleSet(false, new HashSet<String>()))));
+
+    // group2 all roles
+    
assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select",
 "server=server1"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
+            new TSentryActiveRoleSet(true, new HashSet<String>()))));
+    // one active role
+    
assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
+            new TSentryActiveRoleSet(false, Sets.newHashSet(roleName1)))));
+    assertEquals(Sets.newHashSet(
+        "server=server1->db=db1->table=tbl1->action=select", "server=server1"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
+            new TSentryActiveRoleSet(false, Sets.newHashSet(roleName2)))));
+    // unknown active role
+    assertEquals(Sets.newHashSet(),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
+            new TSentryActiveRoleSet(false, Sets.newHashSet("not a role")))));
+    // no active roles
+    assertEquals(Sets.newHashSet(),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
+            new TSentryActiveRoleSet(false, new HashSet<String>()))));
+
+    // both groups, all active roles
+    
assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select",
 "server=server1"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.
+            newHashSet(groupName1, groupName2),
+            new TSentryActiveRoleSet(true, new HashSet<String>()))));
+    // one active role
+    
assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.
+            newHashSet(groupName1, groupName2),
+            new TSentryActiveRoleSet(false, Sets.newHashSet(roleName1)))));
+    assertEquals(Sets.newHashSet(
+        "server=server1->db=db1->table=tbl1->action=select", "server=server1"),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.
+            newHashSet(groupName1, groupName2),
+            new TSentryActiveRoleSet(false, Sets.newHashSet(roleName2)))));
+    // unknown active role
+    assertEquals(Sets.newHashSet(),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.
+            newHashSet(groupName1, groupName2),
+            new TSentryActiveRoleSet(false, Sets.newHashSet("not a role")))));
+//    // no active roles
+    assertEquals(Sets.newHashSet(),
+        
StoreUtils.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.
+            newHashSet(groupName1, groupName2),
+            new TSentryActiveRoleSet(false, new HashSet<String>()))));
+  }
+}

Reply via email to