Merge branch 'stable-2.14' into stable-2.15

* stable-2.14:
  Replace Apache commons-dbcp with HikariCP
  Periodically remove older database entries
  Do not remove entries from local DB
  Remove entries from deleted projects asynchronously
  Upgrade mockito to latest version
  Convert local variable to field
  Remove unneeded JDBC driver class name
  Remove unneeded annotation
  Use already defined constant
  Inline one-time use variables
  Update bazlets to latest stable-2.14 to use 2.14.11 API
  Fix index creation
  Update bazlets to latest stable-2.14 to use 2.14.10 API
  Add indexes to speed up queries
  Configure DB connection pool
  Simplify sorting of query events result
  Avoid using non portable separator character
  Use Java Files instead of Guava one
  Simplify configuration
  Make default query more readable
  Format build files with buildifier 0.12.0
  Use built-in log formatter
  SQLQueryMaker: add order-by clause to the query
  EventsLogIT: Format with google-java-format 1.6

Change-Id: Ie204612e9e4f37215b51066e91adf6f2ca5f0843
diff --git a/WORKSPACE b/WORKSPACE
index bca990a..b57d59c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "2190d8b6c9690c445b7762aeed57c96726ae9766",
+    commit = "3995849fe742201d93e40f2b109dcff13dd0a4ed",
     #    local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventCleanerQueue.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventCleanerQueue.java
index d8e2aff..b457018 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventCleanerQueue.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventCleanerQueue.java
@@ -18,12 +18,12 @@
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledExecutorService;
 
 @Singleton
 public class EventCleanerQueue implements LifecycleListener {
   private final WorkQueue workQueue;
-  private WorkQueue.Executor pool;
+  private ScheduledExecutorService pool;
 
   @Inject
   public EventCleanerQueue(WorkQueue workQueue) {
@@ -38,12 +38,11 @@
   @Override
   public void stop() {
     if (pool != null) {
-      pool.unregisterWorkQueue();
       pool = null;
     }
   }
 
-  public ScheduledThreadPoolExecutor getPool() {
-    return pool;
+  ScheduledExecutorService getPool() {
+    return this.pool;
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventHandler.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventHandler.java
index 262316c..15f830a 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventHandler.java
@@ -18,15 +18,15 @@
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.ProjectEvent;
 import com.google.inject.Inject;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledExecutorService;
 
 /** Listen to Events and store them into the EventStore */
 class EventHandler implements EventListener {
   private final EventStore store;
-  private final ScheduledThreadPoolExecutor pool;
+  private final ScheduledExecutorService pool;
 
   @Inject
-  EventHandler(EventStore store, @EventPool ScheduledThreadPoolExecutor pool) {
+  EventHandler(EventStore store, @EventPool ScheduledExecutorService pool) {
     this.store = store;
     this.pool = pool;
   }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventModule.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventModule.java
index 56f8802..b888425 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventModule.java
@@ -23,7 +23,7 @@
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.internal.UniqueAnnotations;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledExecutorService;
 
 /** Configures handling for an event queue while providing its pool. */
 public class EventModule extends AbstractModule {
@@ -42,13 +42,13 @@
 
   @Provides
   @EventPool
-  ScheduledThreadPoolExecutor provideEventPool(EventQueue queue) {
+  ScheduledExecutorService provideEventPool(EventQueue queue) {
     return queue.getPool();
   }
 
   @Provides
   @EventCleanerPool
-  ScheduledThreadPoolExecutor provideEventCleanerPool(EventCleanerQueue queue) {
+  ScheduledExecutorService provideEventCleanerPool(EventCleanerQueue queue) {
     return queue.getPool();
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventPool.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventPool.java
index 32e9465..e4646b0 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventPool.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventPool.java
@@ -19,7 +19,7 @@
 import com.google.inject.BindingAnnotation;
 import java.lang.annotation.Retention;
 
-/** Annotation applied to a ScheduledThreadPoolExecutor. */
+/** Annotation applied to a ScheduledExecutorService. */
 @Retention(RUNTIME)
 @BindingAnnotation
 public @interface EventPool {}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventQueue.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventQueue.java
index eca051b..72c3c5a 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventQueue.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventQueue.java
@@ -17,12 +17,12 @@
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.inject.Inject;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledExecutorService;
 
 /** A queue for events to store. */
 public class EventQueue implements LifecycleListener {
   private final WorkQueue workQueue;
-  private WorkQueue.Executor pool;
+  private ScheduledExecutorService pool;
 
   @Inject
   EventQueue(WorkQueue workQueue) {
@@ -38,12 +38,11 @@
   @Override
   public void stop() {
     if (pool != null) {
-      pool.unregisterWorkQueue();
       pool = null;
     }
   }
 
-  ScheduledThreadPoolExecutor getPool() {
+  ScheduledExecutorService getPool() {
     return this.pool;
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/EventsLogCleaner.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/EventsLogCleaner.java
index 3c152e8..b092829 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/EventsLogCleaner.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/EventsLogCleaner.java
@@ -22,7 +22,7 @@
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.temporal.ChronoUnit;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
 @Singleton
@@ -32,11 +32,10 @@
 
   private final SQLClient eventsDb;
 
-  private ScheduledThreadPoolExecutor pool;
+  private ScheduledExecutorService pool;
 
   @Inject
-  EventsLogCleaner(
-      @EventsDb SQLClient eventsDb, @EventCleanerPool ScheduledThreadPoolExecutor pool) {
+  EventsLogCleaner(@EventsDb SQLClient eventsDb, @EventCleanerPool ScheduledExecutorService pool) {
     this.eventsDb = eventsDb;
     this.pool = pool;
   }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStore.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStore.java
index 8e2ab52..8a06904 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStore.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStore.java
@@ -24,11 +24,13 @@
 import com.ericsson.gerrit.plugins.eventslog.ServiceUnavailableException;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.events.ProjectEvent;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -41,8 +43,8 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,7 +54,6 @@
   private static final Logger log = LoggerFactory.getLogger(SQLStore.class);
   private static final String H2_DB_SUFFIX = ".h2.db";
 
-  private final ProjectControl.GenericFactory projectControlFactory;
   private final Provider<CurrentUser> userProvider;
   private final EventsLogCleaner eventsLogCleaner;
   private SQLClient eventsDb;
@@ -63,30 +64,31 @@
   private final int connectTime;
   private boolean online = true;
   private boolean copyLocal;
-  private final ScheduledThreadPoolExecutor pool;
+  private final ScheduledExecutorService pool;
+  private final PermissionBackend permissionBackend;
   private ScheduledFuture<?> checkConnTask;
   private Path localPath;
 
   @Inject
   SQLStore(
-      ProjectControl.GenericFactory projectControlFactory,
       Provider<CurrentUser> userProvider,
       EventsLogConfig cfg,
       @EventsDb SQLClient eventsDb,
       @LocalEventsDb SQLClient localEventsDb,
-      @EventPool ScheduledThreadPoolExecutor pool,
+      @EventPool ScheduledExecutorService pool,
+      PermissionBackend permissionBackend,
       EventsLogCleaner eventsLogCleaner) {
     this.maxAge = cfg.getMaxAge();
     this.maxTries = cfg.getMaxTries();
     this.waitTime = cfg.getWaitTime();
     this.connectTime = cfg.getConnectTime();
     this.copyLocal = cfg.getCopyLocal();
-    this.projectControlFactory = projectControlFactory;
     this.userProvider = userProvider;
     this.eventsDb = eventsDb;
     this.localEventsDb = localEventsDb;
     this.eventsLogCleaner = eventsLogCleaner;
     this.pool = pool;
+    this.permissionBackend = permissionBackend;
     this.localPath = cfg.getLocalStorePath();
   }
 
@@ -118,18 +120,15 @@
     for (Entry<String, Collection<SQLEntry>> entry : eventsDb.getEvents(query).asMap().entrySet()) {
       String projectName = entry.getKey();
       try {
-        if (projectControlFactory
-            .controlFor(new Project.NameKey(projectName), userProvider.get())
-            .isVisible()) {
-          entries.addAll(entry.getValue());
-        }
-      } catch (NoSuchProjectException e) {
-        log.warn(
-            "Database contains a non-existing project, {}; removing project from database",
-            projectName);
-        eventsLogCleaner.removeProjectEventsAsync(projectName);
-      } catch (IOException e) {
-        log.warn("Cannot get project visibility info for {} from cache", projectName, e);
+        permissionBackend
+            .user(userProvider)
+            .project(new Project.NameKey(projectName))
+            .check(ProjectPermission.ACCESS);
+        entries.addAll(entry.getValue());
+      } catch (AuthException e) {
+        // Ignore
+      } catch (PermissionBackendException e) {
+        log.warn("Cannot check project access permission", e);
       }
     }
     return entries.stream().sorted().map(SQLEntry::getEvent).collect(toList());
diff --git a/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventHandlerTest.java
index f3eb207..0007ab8 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventHandlerTest.java
@@ -21,6 +21,7 @@
 
 import com.google.gerrit.server.events.ChangeEvent;
 import com.google.gerrit.server.events.Event;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import org.junit.Before;
 import org.junit.Test;
@@ -35,7 +36,7 @@
 
   @Before
   public void setUp() {
-    ScheduledThreadPoolExecutor poolMock = new PoolMock();
+    ScheduledExecutorService poolMock = new PoolMock();
     eventHandler = new EventHandler(storeMock, poolMock);
   }
 
diff --git a/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStoreTest.java b/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStoreTest.java
index bbc4d05..96ce35d 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStoreTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLStoreTest.java
@@ -17,6 +17,7 @@
 import static com.ericsson.gerrit.plugins.eventslog.sql.SQLTable.TABLE_NAME;
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -28,15 +29,14 @@
 import com.ericsson.gerrit.plugins.eventslog.ServiceUnavailableException;
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.Project.NameKey;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.events.ProjectEvent;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gson.Gson;
 import com.google.inject.Provider;
 import com.zaxxer.hikari.HikariConfig;
-import java.io.IOException;
 import java.net.ConnectException;
 import java.sql.Connection;
 import java.sql.DriverManager;
@@ -45,9 +45,11 @@
 import java.sql.Timestamp;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -67,92 +69,68 @@
   private static final String TERM_CONN_MSG = "terminating connection";
   private static final String MSG = "message";
   private static final String GENERIC_QUERY = "SELECT * FROM " + TABLE_NAME;
-  private static final boolean PROJECT_VISIBLE_TO_USER = true;
-  private static final boolean PROJECT_NOT_VISIBLE_TO_USER = false;
 
-  @Mock private ProjectControl.GenericFactory pcFactoryMock;
   @Mock private Provider<CurrentUser> userProviderMock;
   @Mock private EventsLogConfig cfgMock;
+  @Mock private PermissionBackend permissionBackendMock;
+  @Mock private PermissionBackend.ForProject forProjectMock;
+  @Mock private PermissionBackend.WithUser withUserMock;
   @Mock private EventsLogCleaner logCleanerMock;
 
   private SQLClient eventsDb;
   private SQLClient localEventsDb;
   private SQLStore store;
+  private ScheduledExecutorService poolMock;
   private HikariConfig config;
-  private ScheduledThreadPoolExecutor poolMock;
 
   private Statement stat;
+  private MockEvent mockEvent;
 
   @Rule public TemporaryFolder testFolder = new TemporaryFolder();
 
   @Before
   public void setUp() throws SQLException {
     config = new HikariConfig();
+    config.setJdbcUrl(TEST_URL);
     config.addDataSourceProperty("DB_CLOSE_DELAY", "-1");
     config.addDataSourceProperty("DATABASE_TO_UPPER", "false");
     Connection conn = DriverManager.getConnection(TEST_URL + ";" + TEST_OPTIONS);
+    mockEvent = new MockEvent();
     stat = conn.createStatement();
     poolMock = new PoolMock();
     when(cfgMock.getMaxAge()).thenReturn(5);
     when(cfgMock.getLocalStorePath()).thenReturn(testFolder.getRoot().toPath());
   }
 
-  private void tearDown() throws Exception {
-    stat.execute("DROP TABLE " + TABLE_NAME);
+  @After
+  public void tearDown() throws Exception {
+    stat.execute("DROP TABLE IF EXISTS " + TABLE_NAME);
     store.stop();
   }
 
-  private void setUpClient() {
-    config.setJdbcUrl(TEST_URL);
-    eventsDb = new SQLClient(config);
-    config.setJdbcUrl(TEST_LOCAL_URL);
-    localEventsDb = new SQLClient(config);
-    store =
-        new SQLStore(
-            pcFactoryMock,
-            userProviderMock,
-            cfgMock,
-            eventsDb,
-            localEventsDb,
-            poolMock,
-            logCleanerMock);
-    store.start();
-  }
-
-  private void setUpClientMock() throws SQLException {
-    eventsDb = mock(SQLClient.class);
-    localEventsDb = mock(SQLClient.class);
-    when(localEventsDb.dbExists()).thenReturn(true);
-  }
-
   @Test
   public void storeThenQueryVisible() throws Exception {
-    MockEvent mockEvent = setUpMocks(PROJECT_VISIBLE_TO_USER);
+    when(permissionBackendMock.user(userProviderMock)).thenReturn(withUserMock);
+    when(withUserMock.project(any(Project.NameKey.class))).thenReturn(forProjectMock);
+    doNothing().when(forProjectMock).check(ProjectPermission.ACCESS);
+    setUpClient();
     store.storeEvent(mockEvent);
     List<String> events = store.queryChangeEvents(GENERIC_QUERY);
     String json = new Gson().toJson(mockEvent);
     assertThat(events).containsExactly(json).inOrder();
-    tearDown();
   }
 
   @Test
   public void storeThenQueryNotVisible() throws Exception {
-    MockEvent mockEvent = setUpMocks(PROJECT_NOT_VISIBLE_TO_USER);
+    when(permissionBackendMock.user(userProviderMock)).thenReturn(withUserMock);
+    when(withUserMock.project(any(Project.NameKey.class))).thenReturn(forProjectMock);
+    doThrow(new PermissionBackendException(""))
+        .when(forProjectMock)
+        .check(ProjectPermission.ACCESS);
+    setUpClient();
     store.storeEvent(mockEvent);
     List<String> events = store.queryChangeEvents(GENERIC_QUERY);
     assertThat(events).isEmpty();
-    tearDown();
-  }
-
-  private MockEvent setUpMocks(boolean isVisible) throws NoSuchProjectException, IOException {
-    MockEvent mockEvent = new MockEvent();
-    ProjectControl pcMock = mock(ProjectControl.class);
-    CurrentUser userMock = mock(CurrentUser.class);
-    when(userProviderMock.get()).thenReturn(userMock);
-    when(pcFactoryMock.controlFor(mockEvent.getProjectNameKey(), userMock)).thenReturn(pcMock);
-    when(pcMock.isVisible()).thenReturn(isVisible);
-    setUpClient();
-    return mockEvent;
   }
 
   @Test(expected = MalformedQueryException.class)
@@ -163,40 +141,20 @@
   }
 
   @Test
-  public void notReturnEventOfNonExistingProject() throws Exception {
-    MockEvent mockEvent = new MockEvent();
-    Project.NameKey projectMock = mock(Project.NameKey.class);
-    CurrentUser userMock = mock(CurrentUser.class);
-    when(userProviderMock.get()).thenReturn(userMock);
-    NameKey projectNameKey = mockEvent.getProjectNameKey();
-    doThrow(new NoSuchProjectException(projectMock))
-        .when(pcFactoryMock)
-        .controlFor(projectNameKey, userMock);
-    setUpClient();
-    store.storeEvent(mockEvent);
-    List<String> events = store.queryChangeEvents(GENERIC_QUERY);
-    assertThat(events).isEmpty();
-    verify(logCleanerMock).removeProjectEventsAsync(mockEvent.project);
-    tearDown();
-  }
-
-  @Test
   public void notReturnEventWithNoVisibilityInfo() throws Exception {
-    MockEvent mockEvent = new MockEvent();
-    CurrentUser userMock = mock(CurrentUser.class);
-    when(userProviderMock.get()).thenReturn(userMock);
-    NameKey projectNameKey = mockEvent.getProjectNameKey();
-    doThrow(new IOException()).when(pcFactoryMock).controlFor(projectNameKey, userMock);
+    when(permissionBackendMock.user(userProviderMock)).thenReturn(withUserMock);
+    when(withUserMock.project(any(Project.NameKey.class))).thenReturn(forProjectMock);
+    doThrow(new PermissionBackendException(""))
+        .when(forProjectMock)
+        .check(ProjectPermission.ACCESS);
     setUpClient();
     store.storeEvent(mockEvent);
     List<String> events = store.queryChangeEvents(GENERIC_QUERY);
     assertThat(events).isEmpty();
-    tearDown();
   }
 
   @Test
   public void retryOnConnectException() throws Exception {
-    MockEvent mockEvent = new MockEvent();
     when(cfgMock.getMaxTries()).thenReturn(3);
     Throwable[] exceptions = new Throwable[3];
     Arrays.fill(exceptions, new SQLException(new ConnectException()));
@@ -205,13 +163,14 @@
     doThrow(exceptions).doNothing().when(eventsDb).queryOne();
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     store.storeEvent(mockEvent);
     verify(eventsDb, times(3)).storeEvent(mockEvent);
@@ -220,7 +179,6 @@
 
   @Test
   public void retryOnMessage() throws Exception {
-    MockEvent mockEvent = new MockEvent();
     when(cfgMock.getMaxTries()).thenReturn(3);
     Throwable[] exceptions = new Throwable[3];
     Arrays.fill(exceptions, new SQLException(TERM_CONN_MSG));
@@ -229,13 +187,14 @@
     doThrow(exceptions).doNothing().when(eventsDb).queryOne();
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     store.storeEvent(mockEvent);
     verify(eventsDb, times(3)).storeEvent(mockEvent);
@@ -244,19 +203,19 @@
 
   @Test
   public void noRetryOnMessage() throws Exception {
-    MockEvent mockEvent = new MockEvent();
     when(cfgMock.getMaxTries()).thenReturn(3);
     setUpClientMock();
     doThrow(new SQLException(MSG)).when(eventsDb).storeEvent(mockEvent);
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     store.storeEvent(mockEvent);
     verify(eventsDb, times(1)).storeEvent(mockEvent);
@@ -264,7 +223,6 @@
 
   @Test
   public void noRetryOnZeroMaxTries() throws Exception {
-    MockEvent mockEvent = new MockEvent();
     when(cfgMock.getMaxTries()).thenReturn(0);
     Throwable[] exceptions = new Throwable[3];
     Arrays.fill(exceptions, new SQLException(new ConnectException()));
@@ -273,13 +231,14 @@
     doThrow(exceptions).doNothing().when(eventsDb).queryOne();
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     store.storeEvent(mockEvent);
     verify(eventsDb, times(1)).storeEvent(mockEvent);
@@ -287,38 +246,31 @@
 
   @Test(expected = ServiceUnavailableException.class)
   public void throwSQLExceptionIfNotOnline() throws Exception {
-    MockEvent mockEvent = new MockEvent();
     setUpClientMock();
     doThrow(new SQLException(new ConnectException())).when(eventsDb).createDBIfNotCreated();
     doThrow(new SQLException()).when(eventsDb).queryOne();
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     store.storeEvent(mockEvent);
     store.queryChangeEvents(GENERIC_QUERY);
   }
 
   @Test
-  public void restoreFromLocalAndRemoveUnfoundProjectEvents() throws Exception {
+  public void restoreEventsFromLocalDb() throws Exception {
     MockEvent mockEvent = new MockEvent();
     MockEvent mockEvent2 = new MockEvent("proj");
-    MockEvent mockEvent3 = new MockEvent("unfound");
-
-    ProjectControl pc = mock(ProjectControl.class);
-    NoSuchProjectException e = mock(NoSuchProjectException.class);
-    CurrentUser userMock = mock(CurrentUser.class);
-    when(userProviderMock.get()).thenReturn(userMock);
-    when(pcFactoryMock.controlFor((mockEvent.getProjectNameKey()), userMock)).thenReturn(pc);
-    when(pcFactoryMock.controlFor((mockEvent2.getProjectNameKey()), userMock)).thenReturn(pc);
-    when(pc.isVisible()).thenReturn(true);
-    doThrow(e).when(pcFactoryMock).controlFor((mockEvent3.getProjectNameKey()), userMock);
+    when(permissionBackendMock.user(userProviderMock)).thenReturn(withUserMock);
+    when(withUserMock.project(any(Project.NameKey.class))).thenReturn(forProjectMock);
+    doNothing().when(forProjectMock).check(ProjectPermission.ACCESS);
 
     config.setJdbcUrl(TEST_URL);
     eventsDb = new SQLClient(config);
@@ -326,25 +278,24 @@
     localEventsDb = new SQLClient(config);
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
 
     localEventsDb.createDBIfNotCreated();
     localEventsDb.storeEvent(mockEvent);
     localEventsDb.storeEvent(mockEvent2);
-    localEventsDb.storeEvent(mockEvent3);
     store.start();
+
     List<String> events = store.queryChangeEvents(GENERIC_QUERY);
     Gson gson = new Gson();
     String json = gson.toJson(mockEvent);
     String json2 = gson.toJson(mockEvent2);
     assertThat(events).containsExactly(json, json2).inOrder();
-    tearDown();
   }
 
   @Test
@@ -354,32 +305,33 @@
     doThrow(new SQLException()).when(eventsDb).queryOne();
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     verify(localEventsDb).createDBIfNotCreated();
   }
 
   @Test
   public void storeLocalOffline() throws Exception {
-    MockEvent mockEvent = new MockEvent();
     setUpClientMock();
     doThrow(new SQLException(new ConnectException())).when(eventsDb).createDBIfNotCreated();
     doThrow(new SQLException()).when(eventsDb).queryOne();
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     store.storeEvent(mockEvent);
     verify(localEventsDb).storeEvent(mockEvent);
@@ -387,25 +339,46 @@
 
   @Test
   public void storeLocalOfflineAfterNoRetry() throws Exception {
-    MockEvent mockEvent = new MockEvent();
     setUpClientMock();
     when(cfgMock.getMaxTries()).thenReturn(0);
     doThrow(new SQLException(new ConnectException())).when(eventsDb).createDBIfNotCreated();
     doThrow(new SQLException()).when(eventsDb).queryOne();
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     store.storeEvent(mockEvent);
     verify(localEventsDb).storeEvent(mockEvent);
   }
 
+  private void setUpClient() {
+    eventsDb = new SQLClient(config);
+    localEventsDb = new SQLClient(config);
+    store =
+        new SQLStore(
+            userProviderMock,
+            cfgMock,
+            eventsDb,
+            localEventsDb,
+            poolMock,
+            permissionBackendMock,
+            logCleanerMock);
+    store.start();
+  }
+
+  private void setUpClientMock() throws SQLException {
+    eventsDb = mock(SQLClient.class);
+    localEventsDb = mock(SQLClient.class);
+    when(localEventsDb.dbExists()).thenReturn(true);
+  }
+
   /**
    * For this test we expect that if we can connect to main database, then we should come back
    * online and try setting up again. We just want to make sure that restoreEventsFromLocal gets
@@ -420,13 +393,14 @@
     when(localEventsDb.getAll()).thenReturn(ImmutableList.of(mock(SQLEntry.class)));
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     poolMock.scheduleWithFixedDelay(store.new CheckConnectionTask(), 0, 0, TimeUnit.MILLISECONDS);
     verify(localEventsDb, times(2)).removeOldEvents(0);
@@ -443,7 +417,6 @@
   }
 
   private void checkConnectionAndRestore(boolean copy) throws Exception {
-    MockEvent mockEvent = new MockEvent();
     eventsDb = mock(SQLClient.class);
     config.setJdbcUrl(TEST_LOCAL_URL);
     localEventsDb = new SQLClient(config);
@@ -460,13 +433,14 @@
 
     store =
         new SQLStore(
-            pcFactoryMock,
             userProviderMock,
             cfgMock,
             eventsDb,
             localEventsDb,
             poolMock,
+            permissionBackendMock,
             logCleanerMock);
+
     store.start();
     verify(eventsDb).queryOne();
     verify(eventsDb).storeEvent(any(String.class), any(Timestamp.class), any(String.class));