Browse Source

SONAR-21561 introduce a managed executor service for SL connections (#10712)

tags/10.5.0.89998
Serhat Yenican 2 months ago
parent
commit
8efa6009d8

+ 2
- 0
server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushModule.java View File

@@ -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,

+ 2
- 6
server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java View File

@@ -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;

+ 4
- 2
server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java View File

@@ -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);
}

+ 27
- 0
server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushEventExecutorService.java View File

@@ -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 {
}

+ 43
- 0
server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushEventExecutorServiceImpl.java View File

@@ -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;
}
}

+ 12
- 11
server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientTest.java View File

@@ -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);
}

+ 11
- 4
server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java View File

@@ -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);


+ 2
- 1
server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintPushActionTest.java View File

@@ -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() {

+ 35
- 0
server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintPushEventExecutorServiceImplTest.java View File

@@ -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);
}
}

Loading…
Cancel
Save