@@ -22,6 +22,7 @@ package org.sonar.server.pushapi; | |||
import org.sonar.core.platform.Module; | |||
import org.sonar.server.pushapi.scheduler.polling.PushEventPollExecutorServiceImpl; | |||
import org.sonar.server.pushapi.scheduler.polling.PushEventPollScheduler; | |||
import org.sonar.server.pushapi.sonarlint.SonarLintPushEventExecutorServiceImpl; | |||
import org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeExecutorServiceImpl; | |||
import org.sonar.server.pushapi.scheduler.purge.PushEventsPurgeScheduler; | |||
import org.sonar.server.pushapi.sonarlint.SonarLintClientPermissionsValidator; | |||
@@ -37,6 +38,7 @@ public class ServerPushModule extends Module { | |||
SonarLintClientPermissionsValidator.class, | |||
SonarLintClientsRegistry.class, | |||
SonarLintPushAction.class, | |||
SonarLintPushEventExecutorServiceImpl.class, | |||
PushEventPollExecutorServiceImpl.class, | |||
PushEventPollScheduler.class, |
@@ -21,22 +21,18 @@ package org.sonar.server.pushapi.sonarlint; | |||
import java.util.Objects; | |||
import java.util.Set; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import javax.servlet.AsyncContext; | |||
import org.sonar.server.pushapi.ServerPushClient; | |||
public class SonarLintClient extends ServerPushClient { | |||
private static final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); | |||
private final Set<String> languages; | |||
private final Set<String> projectUuids; | |||
private final String userUuid; | |||
public SonarLintClient(AsyncContext asyncContext, Set<String> projectUuids, Set<String> languages, String userUuid) { | |||
super(scheduledExecutorService, asyncContext); | |||
public SonarLintClient(SonarLintPushEventExecutorService executorService, AsyncContext asyncContext, Set<String> projectUuids, Set<String> languages, String userUuid) { | |||
super(executorService, asyncContext); | |||
this.projectUuids = projectUuids; | |||
this.languages = languages; | |||
this.userUuid = userUuid; |
@@ -44,13 +44,15 @@ public class SonarLintPushAction extends ServerPushAction { | |||
private final SonarLintClientPermissionsValidator permissionsValidator; | |||
private final UserSession userSession; | |||
private final DbClient dbClient; | |||
private final SonarLintPushEventExecutorService sonarLintPushEventExecutorService; | |||
public SonarLintPushAction(SonarLintClientsRegistry sonarLintClientRegistry, UserSession userSession, DbClient dbClient, | |||
SonarLintClientPermissionsValidator permissionsValidator) { | |||
SonarLintClientPermissionsValidator permissionsValidator, SonarLintPushEventExecutorService sonarLintPushEventExecutorService) { | |||
this.clientsRegistry = sonarLintClientRegistry; | |||
this.userSession = userSession; | |||
this.dbClient = dbClient; | |||
this.permissionsValidator = permissionsValidator; | |||
this.sonarLintPushEventExecutorService = sonarLintPushEventExecutorService; | |||
} | |||
@Override | |||
@@ -99,7 +101,7 @@ public class SonarLintPushAction extends ServerPushAction { | |||
Set<String> projectUuids = projectDtos.stream().map(ProjectDto::getUuid).collect(Collectors.toSet()); | |||
SonarLintClient sonarLintClient = new SonarLintClient(asyncContext, projectUuids, params.getLanguages(), userSession.getUuid()); | |||
SonarLintClient sonarLintClient = new SonarLintClient(sonarLintPushEventExecutorService, asyncContext, projectUuids, params.getLanguages(), userSession.getUuid()); | |||
clientsRegistry.registerClient(sonarLintClient); | |||
} |
@@ -0,0 +1,27 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 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.sonarlint; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import org.sonar.api.server.ServerSide; | |||
@ServerSide | |||
public interface SonarLintPushEventExecutorService extends ScheduledExecutorService { | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 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.sonarlint; | |||
import java.util.concurrent.Executors; | |||
import java.util.concurrent.ScheduledExecutorService; | |||
import org.sonar.server.util.AbstractStoppableScheduledExecutorServiceImpl; | |||
import static java.lang.Thread.MIN_PRIORITY; | |||
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; | |||
public class SonarLintPushEventExecutorServiceImpl extends | |||
AbstractStoppableScheduledExecutorServiceImpl<ScheduledExecutorService> implements SonarLintPushEventExecutorService { | |||
public SonarLintPushEventExecutorServiceImpl() { | |||
super(newSingleThreadScheduledExecutor(SonarLintPushEventExecutorServiceImpl::createThread)); | |||
} | |||
static Thread createThread(Runnable r) { | |||
Thread thread = Executors.defaultThreadFactory().newThread(r); | |||
thread.setName(String.format("SonarLint-PushEvent-%d", System.nanoTime())); | |||
thread.setPriority(MIN_PRIORITY); | |||
thread.setDaemon(true); | |||
return thread; | |||
} | |||
} |
@@ -32,50 +32,51 @@ public class SonarLintClientTest { | |||
private final AsyncContext secondContext = mock(AsyncContext.class); | |||
private final String USER_UUID = "userUUID"; | |||
private final SonarLintPushEventExecutorService sonarLintPushEventExecutorService = mock(SonarLintPushEventExecutorService.class); | |||
@Test | |||
public void equals_twoClientsWithSameArgumentsAreEqual() { | |||
SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient first = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of(), Set.of(), USER_UUID); | |||
assertThat(first).isEqualTo(second); | |||
} | |||
@Test | |||
public void equals_twoClientsWithDifferentAsyncObjects() { | |||
SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(secondContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient first = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(sonarLintPushEventExecutorService, secondContext, Set.of(), Set.of(), USER_UUID); | |||
assertThat(first).isNotEqualTo(second); | |||
} | |||
@Test | |||
public void equals_twoClientsWithDifferentLanguages() { | |||
SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of("java"), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of("cobol"), USER_UUID); | |||
SonarLintClient first = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of(), Set.of("java"), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of(), Set.of("cobol"), USER_UUID); | |||
assertThat(first).isNotEqualTo(second); | |||
} | |||
@Test | |||
public void equals_twoClientsWithDifferentProjectUuids() { | |||
SonarLintClient first = new SonarLintClient(firstContext, Set.of("project1", "project2"), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(firstContext, Set.of("project1"), Set.of(), USER_UUID); | |||
SonarLintClient first = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of("project1", "project2"), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of("project1"), Set.of(), USER_UUID); | |||
assertThat(first).isNotEqualTo(second); | |||
} | |||
@Test | |||
public void equals_secondClientIsNull() { | |||
SonarLintClient first = new SonarLintClient(firstContext, Set.of("project1", "project2"), Set.of(), USER_UUID); | |||
SonarLintClient first = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of("project1", "project2"), Set.of(), USER_UUID); | |||
assertThat(first).isNotEqualTo(null); | |||
} | |||
@Test | |||
public void hashCode_producesSameHashesForEqualObjects() { | |||
SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient first = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of(), Set.of(), USER_UUID); | |||
SonarLintClient second = new SonarLintClient(sonarLintPushEventExecutorService, firstContext, Set.of(), Set.of(), USER_UUID); | |||
assertThat(first).hasSameHashCodeAs(second); | |||
} |
@@ -25,6 +25,7 @@ import java.util.Set; | |||
import javax.servlet.AsyncContext; | |||
import javax.servlet.ServletOutputStream; | |||
import javax.servlet.ServletResponse; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.ArgumentCaptor; | |||
@@ -55,6 +56,7 @@ public class SonarLintClientsRegistryTest { | |||
private final ServletOutputStream outputStream = mock(ServletOutputStream.class); | |||
private final SonarLintClientPermissionsValidator permissionsValidator = mock(SonarLintClientPermissionsValidator.class); | |||
private final SonarLintPushEventExecutorService sonarLintPushEventExecutorService = new SonarLintPushEventExecutorServiceImpl(); | |||
private SonarLintClientsRegistry underTest; | |||
@@ -63,6 +65,11 @@ public class SonarLintClientsRegistryTest { | |||
underTest = new SonarLintClientsRegistry(permissionsValidator); | |||
} | |||
@After | |||
public void after() { | |||
this.sonarLintPushEventExecutorService.shutdown(); | |||
} | |||
@Test | |||
public void registerClientAndUnregister_changesNumberOfClients_andClosesClient() { | |||
SonarLintClient sonarLintClient = mock(SonarLintClient.class); | |||
@@ -83,7 +90,7 @@ public class SonarLintClientsRegistryTest { | |||
public void registering10Clients_10ClientsAreRegistered() { | |||
for (int i = 0; i < 10; i++) { | |||
AsyncContext newAsyncContext = mock(AsyncContext.class); | |||
SonarLintClient sonarLintClient = new SonarLintClient(newAsyncContext, exampleProjectUuids, languageKeys, USER_UUID); | |||
SonarLintClient sonarLintClient = new SonarLintClient(sonarLintPushEventExecutorService, newAsyncContext, exampleProjectUuids, languageKeys, USER_UUID); | |||
underTest.registerClient(sonarLintClient); | |||
} | |||
@@ -95,7 +102,7 @@ public class SonarLintClientsRegistryTest { | |||
Set<String> javaLanguageKey = Set.of("java"); | |||
when(defaultAsyncContext.getResponse()).thenReturn(response); | |||
when(response.getOutputStream()).thenReturn(outputStream); | |||
SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleProjectUuids, javaLanguageKey, USER_UUID); | |||
SonarLintClient sonarLintClient = new SonarLintClient(sonarLintPushEventExecutorService, defaultAsyncContext, exampleProjectUuids, javaLanguageKey, USER_UUID); | |||
underTest.registerClient(sonarLintClient); | |||
@@ -136,7 +143,7 @@ public class SonarLintClientsRegistryTest { | |||
Set<String> jsLanguageKey = Set.of("js"); | |||
when(defaultAsyncContext.getResponse()).thenReturn(response); | |||
when(response.getOutputStream()).thenReturn(outputStream); | |||
SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleProjectUuids, jsLanguageKey, USER_UUID); | |||
SonarLintClient sonarLintClient = new SonarLintClient(sonarLintPushEventExecutorService, defaultAsyncContext, exampleProjectUuids, jsLanguageKey, USER_UUID); | |||
underTest.registerClient(sonarLintClient); | |||
@@ -151,7 +158,7 @@ public class SonarLintClientsRegistryTest { | |||
public void listen_givenOneClientInterestedInProjA_DontCheckPermissionsForProjB() throws IOException { | |||
when(defaultAsyncContext.getResponse()).thenReturn(response); | |||
when(response.getOutputStream()).thenReturn(outputStream); | |||
SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, Set.of("projA"), Set.of("java"), USER_UUID); | |||
SonarLintClient sonarLintClient = new SonarLintClient(sonarLintPushEventExecutorService, defaultAsyncContext, Set.of("projA"), Set.of("java"), USER_UUID); | |||
underTest.registerClient(sonarLintClient); | |||
@@ -46,8 +46,9 @@ public class SonarLintPushActionTest { | |||
private final DbClient dbClient = mock(DbClient.class); | |||
private final ProjectDao projectDao = mock(ProjectDao.class); | |||
private final SonarLintClientPermissionsValidator permissionsValidator = mock(SonarLintClientPermissionsValidator.class); | |||
private final SonarLintPushEventExecutorService sonarLintPushEventExecutorService = mock(SonarLintPushEventExecutorServiceImpl.class); | |||
private final WsPushActionTester ws = new WsPushActionTester(new SonarLintPushAction(registry, userSession, dbClient, permissionsValidator)); | |||
private final WsPushActionTester ws = new WsPushActionTester(new SonarLintPushAction(registry, userSession, dbClient, permissionsValidator, sonarLintPushEventExecutorService)); | |||
@Before | |||
public void before() { |
@@ -0,0 +1,35 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2024 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.sonarlint; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class SonarLintPushEventExecutorServiceImplTest { | |||
@Test | |||
public void createThread_shouldCreateDaemonWithNameSonarLintPushEvent() { | |||
assertThat(SonarLintPushEventExecutorServiceImpl.createThread(() -> { | |||
})) | |||
.extracting(Thread::getPriority, Thread::isDaemon, thread -> thread.getName().startsWith("SonarLint-PushEvent-")) | |||
.containsExactly(Thread.MIN_PRIORITY, true, true); | |||
} | |||
} |