]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15918 improve connections management of SonarLintClients
authorBelen Pruvost <belen.pruvost@sonarsource.com>
Wed, 16 Feb 2022 17:06:56 +0000 (14:06 -0300)
committersonartech <sonartech@sonarsource.com>
Fri, 18 Feb 2022 15:48:04 +0000 (15:48 +0000)
server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java
server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java
server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/ServerPushClientTest.java
server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java

index 6d8b552e24a225f0bee8dc5eeffa822ba96dd135..a2004270b233001e512d6d4b26a0c9c1e730bcea 100644 (file)
@@ -85,7 +85,7 @@ public abstract class ServerPushClient {
   private void handleIOException(IOException e) {
     String remoteAddr = asyncContext.getRequest().getRemoteAddr();
     LOG.info(String.format("The server push client %s gone without notice, closing the connection (%s)", remoteAddr, e.getMessage()));
-    close();
+    throw new IllegalStateException(e.getMessage());
   }
 
   public synchronized void close() {
index 008fdb0c3f9cbe1f3a6738c1fc74c8988be445ad..6bdc3b7b58099f6e04472c652e73b58b4979b6e3 100644 (file)
@@ -37,8 +37,8 @@ import org.sonar.core.util.ParamChange;
 import org.sonar.core.util.RuleActivationListener;
 import org.sonar.core.util.RuleChange;
 import org.sonar.core.util.RuleSetChangeEvent;
-import org.sonar.server.pushapi.qualityprofile.RuleActivatorEventsDistributor;
 import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.pushapi.qualityprofile.RuleActivatorEventsDistributor;
 
 import static java.util.Arrays.asList;
 
@@ -47,21 +47,19 @@ public class SonarLintClientsRegistry implements RuleActivationListener {
 
   private static final Logger LOG = Loggers.get(SonarLintClientsRegistry.class);
 
-  private final RuleActivatorEventsDistributor ruleActivatorEventsDistributor;
   private final SonarLintClientPermissionsValidator sonarLintClientPermissionsValidator;
   private final List<SonarLintClient> clients = new CopyOnWriteArrayList<>();
 
   public SonarLintClientsRegistry(RuleActivatorEventsDistributor ruleActivatorEventsDistributor, SonarLintClientPermissionsValidator permissionsValidator) {
-    this.ruleActivatorEventsDistributor = ruleActivatorEventsDistributor;
     this.sonarLintClientPermissionsValidator = permissionsValidator;
+
+    ruleActivatorEventsDistributor.subscribe(this);
   }
 
   public void registerClient(SonarLintClient sonarLintClient) {
     clients.add(sonarLintClient);
     sonarLintClient.scheduleHeartbeat();
     sonarLintClient.addListener(new SonarLintClientEventsListener(sonarLintClient));
-    ruleActivatorEventsDistributor.subscribe(this);
-
     LOG.debug("Registering new SonarLint client");
   }
 
@@ -77,7 +75,6 @@ public class SonarLintClientsRegistry implements RuleActivationListener {
 
   @Override
   public void listen(RuleSetChangeEvent ruleChangeEvent) {
-    LOG.info("Generating a RuleSetChangeEvent");
     broadcastMessage(ruleChangeEvent, getFilterForEvent(ruleChangeEvent));
   }
 
@@ -101,9 +98,11 @@ public class SonarLintClientsRegistry implements RuleActivationListener {
         String jsonString = getJSONString(personalizedEvent);
         c.writeAndFlush(jsonString);
       } catch (ForbiddenException forbiddenException) {
+        LOG.debug("Client is no longer authenticated: " + forbiddenException.getMessage());
         unregisterClient(c);
-      } catch (IOException e) {
+      } catch (IllegalStateException | IOException e) {
         LOG.error("Unable to send message to a client: " + e.getMessage());
+        unregisterClient(c);
       }
     });
   }
index e7eca1cf790a087a54a6554537fb9b0c9a841c2c..462d4a849fe99862cc7caa713d780bdf69fc580d 100644 (file)
@@ -34,6 +34,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doThrow;
@@ -123,20 +124,18 @@ public class ServerPushClientTest {
     when(executorService.schedule(any(HeartbeatTask.class), anyLong(), any(TimeUnit.class))).thenReturn(task);
     underTest.scheduleHeartbeat();
 
-    underTest.write('a');
-
-    verify(asyncContext).complete();
+    assertThatThrownBy(() -> underTest.write('a'))
+      .isInstanceOf(IllegalStateException.class);
   }
 
   @Test
-  public void flush_exceptionCausesConnectionToClose() throws IOException {
+  public void flush_exceptionIsPropagated() throws IOException {
     when(servletResponse.getOutputStream()).thenThrow(new IOException("mock exception"));
     when(executorService.schedule(any(HeartbeatTask.class), anyLong(), any(TimeUnit.class))).thenReturn(task);
     underTest.scheduleHeartbeat();
 
-    underTest.flush();
-
-    verify(asyncContext).complete();
+    assertThatThrownBy(underTest::flush)
+      .isInstanceOf(IllegalStateException.class);
   }
 
   @Test
index 351ff62fb3ee56af53ddf6e312c59169f173f1dd..208fa45715f2a10256ad6cb61f1c5f287ebaaaa6 100644 (file)
@@ -39,6 +39,7 @@ import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -64,8 +65,8 @@ public class SonarLintClientsRegistryTest {
   }
 
   @Test
-  public void registerClientAndUnregister_changesNumberOfClients() {
-    SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, languageKeys, USER_UUID);
+  public void registerClientAndUnregister_changesNumberOfClients_andClosesClient() {
+    SonarLintClient sonarLintClient = mock(SonarLintClient.class);
 
     underTest.registerClient(sonarLintClient);
 
@@ -74,6 +75,7 @@ public class SonarLintClientsRegistryTest {
     underTest.unregisterClient(sonarLintClient);
 
     assertThat(underTest.countConnectedClients()).isZero();
+    verify(sonarLintClient).close();
   }
 
   @Test
@@ -168,6 +170,27 @@ public class SonarLintClientsRegistryTest {
     verify(sonarLintClient).close();
   }
 
+  @Test
+  public void listen_givenUnregisteredClient_closeConnection() throws IOException {
+    RuleChange javaRuleChange = createRuleChange();
+    RuleChange[] activatedRules = {};
+    RuleChange[] deactivatedRules = {javaRuleChange};
+    RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules);
+
+    SonarLintClient sonarLintClient = createSampleSLClient();
+    underTest.registerClient(sonarLintClient);
+    doThrow(new IOException("Broken pipe")).when(sonarLintClient).writeAndFlush(anyString());
+
+    underTest.listen(ruleChangeEvent);
+
+    underTest.registerClient(sonarLintClient);
+    doThrow(new IllegalStateException("Things went wrong")).when(sonarLintClient).writeAndFlush(anyString());
+
+    underTest.listen(ruleChangeEvent);
+
+    verify(sonarLintClient, times(2)).close();
+  }
+
   private SonarLintClient createSampleSLClient() {
     SonarLintClient mock = mock(SonarLintClient.class);
     when(mock.getLanguages()).thenReturn(Set.of("java"));
@@ -185,5 +208,4 @@ public class SonarLintClientsRegistryTest {
     javaRule.setKey("rule-key");
     return javaRule;
   }
-
 }