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() {
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;
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");
}
@Override
public void listen(RuleSetChangeEvent ruleChangeEvent) {
- LOG.info("Generating a RuleSetChangeEvent");
broadcastMessage(ruleChangeEvent, getFilterForEvent(ruleChangeEvent));
}
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);
}
});
}
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;
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
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;
}
@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);
underTest.unregisterClient(sonarLintClient);
assertThat(underTest.countConnectedClients()).isZero();
+ verify(sonarLintClient).close();
}
@Test
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"));
javaRule.setKey("rule-key");
return javaRule;
}
-
}