import org.sonar.server.pushapi.scheduler.polling.PushEventPollExecutorServiceImpl;
import org.sonar.server.pushapi.scheduler.polling.PushEventPollScheduler;
import org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeExecutorServiceImpl;
-import org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeInitializer;
-import org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeSchedulerImpl;
+import org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeScheduler;
import org.sonar.server.pushapi.sonarlint.SonarLintClientPermissionsValidator;
import org.sonar.server.pushapi.sonarlint.SonarLintClientsRegistry;
import org.sonar.server.pushapi.sonarlint.SonarLintPushAction;
PushEventPollExecutorServiceImpl.class,
PushEventPollScheduler.class,
// Push Events Purge
- PushEventsPurgeSchedulerImpl.class,
- PushEventsPurgeExecutorServiceImpl.class,
- PushEventsPurgeInitializer.class);
+ PushEventsPurgeScheduler.class,
+ PushEventsPurgeExecutorServiceImpl.class);
}
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.pushapi.scheduler.purge;
-
-import javax.annotation.Nullable;
-import org.sonar.api.platform.Server;
-import org.sonar.api.platform.ServerStartHandler;
-
-public class PushEventsPurgeInitializer implements ServerStartHandler {
-
- private final PushEventsPurgeScheduler pushEventsPurgeScheduler;
-
- public PushEventsPurgeInitializer(@Nullable PushEventsPurgeScheduler pushEventsPurgeScheduler) {
- this.pushEventsPurgeScheduler = pushEventsPurgeScheduler;
- }
-
- @Override
- public void onServerStart(Server server) {
- if (pushEventsPurgeScheduler != null) {
- pushEventsPurgeScheduler.startScheduling();
- }
- }
-
-}
*/
package org.sonar.server.pushapi.scheduler.purge;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Set;
+import org.sonar.api.Startable;
+import org.sonar.api.config.Configuration;
import org.sonar.api.server.ServerSide;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.server.util.GlobalLockManager;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
@ServerSide
-public interface PushEventsPurgeScheduler {
- void startScheduling();
+public class PushEventsPurgeScheduler implements Startable {
+ private static final Logger LOG = Loggers.get(PushEventsPurgeScheduler.class);
+ private static final String LOCK_NAME = "PushEventsPurgeCheck";
+ private static final String INITIAL_DELAY_IN_SECONDS = "sonar.push.events.purge.initial.delay";
+ private static final String ENQUEUE_DELAY_IN_SECONDS = "sonar.push.events.purge.enqueue.delay";
+ private static final int ENQUEUE_LOCK_DELAY_IN_SECONDS = 60;
+
+ private final DbClient dbClient;
+ private final Configuration config;
+ private final GlobalLockManager lockManager;
+ private final PushEventsPurgeExecutorService executorService;
+ private final System2 system;
+
+ public PushEventsPurgeScheduler(DbClient dbClient, Configuration config, GlobalLockManager lockManager,
+ PushEventsPurgeExecutorService executorService, System2 system) {
+ this.dbClient = dbClient;
+ this.executorService = executorService;
+ this.config = config;
+ this.lockManager = lockManager;
+ this.system = system;
+ }
+
+ @Override
+ public void start() {
+ executorService.scheduleAtFixedRate(this::checks, getInitialDelay(),
+ getEnqueueDelay(), SECONDS);
+ }
+
+ private void checks() {
+ try {
+ // Avoid enqueueing push events purge task multiple times
+ if (!lockManager.tryLock(LOCK_NAME, ENQUEUE_LOCK_DELAY_IN_SECONDS)) {
+ return;
+ }
+ purgeExpiredPushEvents();
+ } catch (Exception e) {
+ LOG.error("Error in Push Events Purge scheduler", e);
+ }
+ }
+
+ private void purgeExpiredPushEvents() {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Set<String> uuids = dbClient.pushEventDao().selectUuidsOfExpiredEvents(dbSession, getExpiredTimestamp());
+ LOG.debug(String.format("%s push events to be deleted...", uuids.size()));
+ dbClient.pushEventDao().deleteByUuids(dbSession, uuids);
+ dbSession.commit();
+ }
+ }
+
+ public long getInitialDelay() {
+ return config.getLong(INITIAL_DELAY_IN_SECONDS).orElse(60 * 60L);
+ }
+
+ public long getEnqueueDelay() {
+ return config.getLong(ENQUEUE_DELAY_IN_SECONDS).orElse(60 * 60L);
+ }
+
+ private long getExpiredTimestamp() {
+ return Instant.ofEpochMilli(system.now())
+ .minus(1, ChronoUnit.HOURS)
+ .toEpochMilli();
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.pushapi.scheduler.purge;
-
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-import java.util.Set;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.server.util.GlobalLockManager;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-public class PushEventsPurgeSchedulerImpl implements PushEventsPurgeScheduler {
- private static final Logger LOG = Loggers.get(PushEventsPurgeSchedulerImpl.class);
- private static final String LOCK_NAME = "PushEventsPurgeCheck";
- private static final String INITIAL_DELAY_IN_SECONDS = "sonar.push.events.purge.initial.delay";
- private static final String ENQUEUE_DELAY_IN_SECONDS = "sonar.push.events.purge.enqueue.delay";
- private static final int ENQUEUE_LOCK_DELAY_IN_SECONDS = 60;
-
- private final DbClient dbClient;
- private final Configuration config;
- private final GlobalLockManager lockManager;
- private final PushEventsPurgeExecutorService executorService;
- private final System2 system;
-
-
- public PushEventsPurgeSchedulerImpl(DbClient dbClient, Configuration config, GlobalLockManager lockManager,
- PushEventsPurgeExecutorService executorService, System2 system) {
- this.dbClient = dbClient;
- this.executorService = executorService;
- this.config = config;
- this.lockManager = lockManager;
- this.system = system;
- }
-
- @Override
- public void startScheduling() {
- executorService.scheduleAtFixedRate(this::checks, getInitialDelay(),
- getEnqueueDelay(), SECONDS);
- }
-
- private void checks() {
- try {
- // Avoid enqueueing push events purge task multiple times
- if (!lockManager.tryLock(LOCK_NAME, ENQUEUE_LOCK_DELAY_IN_SECONDS)) {
- return;
- }
- purgeExpiredPushEvents();
- } catch (Exception e) {
- LOG.error("Error in Push Events Purge scheduler", e);
- }
- }
-
- private void purgeExpiredPushEvents() {
- try (DbSession dbSession = dbClient.openSession(false)) {
- Set<String> uuids = dbClient.pushEventDao().selectUuidsOfExpiredEvents(dbSession, getExpiredTimestamp());
- LOG.debug(String.format("%s push events to be deleted...", uuids.size()));
- dbClient.pushEventDao().deleteByUuids(dbSession, uuids);
- dbSession.commit();
- }
- }
-
- public long getInitialDelay() {
- return config.getLong(INITIAL_DELAY_IN_SECONDS).orElse(60 * 60L);
- }
-
- public long getEnqueueDelay() {
- return config.getLong(ENQUEUE_DELAY_IN_SECONDS).orElse(60 * 60L);
- }
-
- private long getExpiredTimestamp() {
- return Instant.ofEpochMilli(system.now())
- .minus(1, ChronoUnit.HOURS)
- .toEpochMilli();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.pushapi.scheduler.purge;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-public class PushEventsPurgeInitializerTest {
-
- private PushEventsPurgeScheduler pushEventsPurgeScheduler;
-
- @Before
- public void before() {
- pushEventsPurgeScheduler = mock(PushEventsPurgeScheduler.class);
- }
-
- @Test
- public void onServerStart_givenProjectReportScheduler_startScheduling() {
- PushEventsPurgeInitializer pushEventsPurgeInitializer = new PushEventsPurgeInitializer(pushEventsPurgeScheduler);
-
- pushEventsPurgeInitializer.onServerStart(null);
-
- verify(pushEventsPurgeScheduler, times(1)).startScheduling();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.pushapi.scheduler.purge;
-
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.pushevent.PushEventDao;
-import org.sonar.server.util.AbstractStoppableExecutorService;
-import org.sonar.server.util.GlobalLockManager;
-import org.sonar.server.util.GlobalLockManagerImpl;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-
-public class PushEventsPurgeSchedulerImplTest {
- private final DbClient dbClient = mock(DbClient.class);
- private final DbSession dbSession = mock(DbSession.class);
- private final GlobalLockManager lockManager = mock(GlobalLockManagerImpl.class);
- private final PushEventDao pushEventDao = mock(PushEventDao.class);
- private final PushEventsPurgeExecutorServiceImpl executorService = new PushEventsPurgeExecutorServiceImpl();
- private final Configuration configuration = mock(Configuration.class);
- private final System2 system2 = mock(System2.class);
- private final PushEventsPurgeSchedulerImpl underTest = new PushEventsPurgeSchedulerImpl(dbClient, configuration,
- lockManager, executorService, system2);
-
- @Before
- public void prepare() {
- when(lockManager.tryLock(any(), anyInt())).thenReturn(true);
- }
-
- @Test
- public void doNothingIfLocked() {
- when(lockManager.tryLock(any(), anyInt())).thenReturn(false);
-
- underTest.startScheduling();
-
- executorService.runCommand();
-
- verifyNoInteractions(dbClient);
- }
-
- @Test
- public void doNothingIfExceptionIsThrown() {
- when(lockManager.tryLock(any(), anyInt())).thenThrow(new IllegalArgumentException("Oops"));
-
- underTest.startScheduling();
-
- executorService.runCommand();
-
- verifyNoInteractions(dbClient);
- }
-
- @Test
- public void schedulePurgeTaskWhenNotLocked() {
- when(system2.now()).thenReturn(100000000L);
- when(dbClient.pushEventDao()).thenReturn(pushEventDao);
- when(dbClient.openSession(false)).thenReturn(dbSession);
- when(dbClient.pushEventDao().selectUuidsOfExpiredEvents(any(), anyLong())).thenReturn(Set.of("1", "2"));
-
- underTest.startScheduling();
-
- executorService.runCommand();
-
- ArgumentCaptor<Set> uuidsCaptor = ArgumentCaptor.forClass(Set.class);
-
- verify(pushEventDao).deleteByUuids(any(), uuidsCaptor.capture());
- Set<String> uuids = uuidsCaptor.getValue();
- assertThat(uuids).containsExactlyInAnyOrder("1", "2");
- }
-
- private static class PushEventsPurgeExecutorServiceImpl extends AbstractStoppableExecutorService<ScheduledExecutorService>
- implements PushEventsPurgeExecutorService {
-
- private Runnable command;
-
- public PushEventsPurgeExecutorServiceImpl() {
- super(null);
- }
-
- public void runCommand() {
- command.run();
- }
-
- @Override
- public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
- this.command = command;
- return null;
- }
-
- @Override
- public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
- return null;
- }
-
- @Override
- public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
- return null;
- }
-
- @Override
- public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
- return null;
- }
-
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.pushapi.scheduler.purge;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.pushevent.PushEventDao;
+import org.sonar.server.util.AbstractStoppableExecutorService;
+import org.sonar.server.util.GlobalLockManager;
+import org.sonar.server.util.GlobalLockManagerImpl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+public class PushEventsPurgeSchedulerTest {
+ private final DbClient dbClient = mock(DbClient.class);
+ private final DbSession dbSession = mock(DbSession.class);
+ private final GlobalLockManager lockManager = mock(GlobalLockManagerImpl.class);
+ private final PushEventDao pushEventDao = mock(PushEventDao.class);
+ private final PushEventsPurgeExecutorServiceImpl executorService = new PushEventsPurgeExecutorServiceImpl();
+ private final Configuration configuration = mock(Configuration.class);
+ private final System2 system2 = mock(System2.class);
+ private final PushEventsPurgeScheduler underTest = new PushEventsPurgeScheduler(dbClient, configuration,
+ lockManager, executorService, system2);
+
+ @Before
+ public void prepare() {
+ when(lockManager.tryLock(any(), anyInt())).thenReturn(true);
+ }
+
+ @Test
+ public void doNothingIfLocked() {
+ when(lockManager.tryLock(any(), anyInt())).thenReturn(false);
+
+ underTest.start();
+
+ executorService.runCommand();
+
+ verifyNoInteractions(dbClient);
+ }
+
+ @Test
+ public void doNothingIfExceptionIsThrown() {
+ when(lockManager.tryLock(any(), anyInt())).thenThrow(new IllegalArgumentException("Oops"));
+
+ underTest.start();
+
+ executorService.runCommand();
+
+ verifyNoInteractions(dbClient);
+ }
+
+ @Test
+ public void schedulePurgeTaskWhenNotLocked() {
+ when(system2.now()).thenReturn(100000000L);
+ when(dbClient.pushEventDao()).thenReturn(pushEventDao);
+ when(dbClient.openSession(false)).thenReturn(dbSession);
+ when(dbClient.pushEventDao().selectUuidsOfExpiredEvents(any(), anyLong())).thenReturn(Set.of("1", "2"));
+
+ underTest.start();
+
+ executorService.runCommand();
+
+ ArgumentCaptor<Set> uuidsCaptor = ArgumentCaptor.forClass(Set.class);
+
+ verify(pushEventDao).deleteByUuids(any(), uuidsCaptor.capture());
+ Set<String> uuids = uuidsCaptor.getValue();
+ assertThat(uuids).containsExactlyInAnyOrder("1", "2");
+ }
+
+ private static class PushEventsPurgeExecutorServiceImpl extends AbstractStoppableExecutorService<ScheduledExecutorService>
+ implements PushEventsPurgeExecutorService {
+
+ private Runnable command;
+
+ public PushEventsPurgeExecutorServiceImpl() {
+ super(null);
+ }
+
+ public void runCommand() {
+ command.run();
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
+ this.command = command;
+ return null;
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return null;
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return null;
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return null;
+ }
+
+ }
+}