diff options
author | Pierre <pierre.guillot@sonarsource.com> | 2022-02-03 12:10:18 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-02-18 15:48:04 +0000 |
commit | 0c8ff1bac53c2dfa89e7be014b64b411f24434c0 (patch) | |
tree | bcad0a2d4daafe106d82f7765be71fec146eb879 /server/sonar-webserver-pushapi | |
parent | 24cd8d71c3b6df3f33f1ff0232c83bbb0a9fc060 (diff) | |
download | sonarqube-0c8ff1bac53c2dfa89e7be014b64b411f24434c0.tar.gz sonarqube-0c8ff1bac53c2dfa89e7be014b64b411f24434c0.zip |
SONAR-15919 add ruleset changes
Diffstat (limited to 'server/sonar-webserver-pushapi')
6 files changed, 117 insertions, 4 deletions
diff --git a/server/sonar-webserver-pushapi/build.gradle b/server/sonar-webserver-pushapi/build.gradle index a2c94714bd0..f3f66fea494 100644 --- a/server/sonar-webserver-pushapi/build.gradle +++ b/server/sonar-webserver-pushapi/build.gradle @@ -8,6 +8,7 @@ dependencies { compile 'javax.servlet:javax.servlet-api' compile project(':server:sonar-webserver-auth') compile project(':server:sonar-webserver-ws') + compile project(':server:sonar-webserver-webapi') testCompile 'junit:junit' testCompile 'org.assertj:assertj-core' diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java index 4904b9e7cd1..cc0d3d7f0c7 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java @@ -20,6 +20,7 @@ package org.sonar.server.pushapi; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -34,6 +35,17 @@ public abstract class ServerPushClient { private static final Logger LOG = Loggers.get(ServerPushClient.class); private static final int DEFAULT_HEARTBEAT_PERIOD = 60; + + + + private static final byte[] CRLF = new byte[] {'\r', '\n'}; + private static final byte[] DATA_END = new byte[] {'\n', '\n'}; + private static final byte[] DATA_FIELD = "data: ".getBytes(StandardCharsets.UTF_8); + + + + + protected final AsyncContext asyncContext; private final ScheduledExecutorService executorService; @@ -50,6 +62,11 @@ public abstract class ServerPushClient { startedHeartbeat = executorService.schedule(heartbeatTask, DEFAULT_HEARTBEAT_PERIOD, TimeUnit.SECONDS); } + public void writeAndFlush(String payload) throws IOException { + output().write(payload.getBytes(StandardCharsets.UTF_8)); + flush(); + } + public void writeAndFlush(char character) { write(character); flush(); diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java index b022cbcb25d..93512a974ec 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java @@ -39,6 +39,14 @@ public class SonarLintClient extends ServerPushClient { this.languages = languages; } + public Set<String> getLanguages() { + return languages; + } + + public Set<String> getClientProjectKeys() { + return projectKeys; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java index e93bf5f9073..4b72f6de66d 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java @@ -19,25 +19,46 @@ */ package org.sonar.server.pushapi.sonarlint; +import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; +import org.json.JSONArray; +import org.json.JSONObject; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; +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.qualityprofile.RuleActivatorEventsDistributor; + +import static java.util.Arrays.asList; @ServerSide -public class SonarLintClientsRegistry { +public class SonarLintClientsRegistry implements RuleActivationListener { private static final Logger LOG = Loggers.get(SonarLintClientsRegistry.class); + private final RuleActivatorEventsDistributor ruleActivatorEventsDistributor; + + public SonarLintClientsRegistry(RuleActivatorEventsDistributor ruleActivatorEventsDistributor) { + this.ruleActivatorEventsDistributor = ruleActivatorEventsDistributor; + } + + private final List<SonarLintClient> clients = new CopyOnWriteArrayList<>(); public void registerClient(SonarLintClient sonarLintClient) { clients.add(sonarLintClient); sonarLintClient.scheduleHeartbeat(); sonarLintClient.addListener(new SonarLintClientEventsListener(sonarLintClient)); + ruleActivatorEventsDistributor.subscribe(this); + LOG.debug("Registering new SonarLint client"); } @@ -50,6 +71,71 @@ public class SonarLintClientsRegistry { return clients.size(); } + @Override + public void listen(RuleSetChangeEvent ruleChangeEvent) { + LOG.info("Generating a RuleSetChangeEvent"); + // TODO filter on languages here as well + broadcastMessage(ruleChangeEvent, f -> f.getClientProjectKeys().isEmpty() || !Collections.disjoint(f.getClientProjectKeys(), asList(ruleChangeEvent.getProjects()))); + } + + + public void broadcastMessage(RuleSetChangeEvent message, Predicate<SonarLintClient> filter) { + String jsonString = getJSONString(message); + clients.stream().filter(filter).forEach(c -> { + try { + c.writeAndFlush(jsonString); + } catch (IOException e) { + LOG.error("Unable to send message to a client: " + e.getMessage()); + } + }); + } + + + public String getJSONString(RuleSetChangeEvent ruleSetChangeEvent) { + JSONObject result = new JSONObject(); + result.put("event", ruleSetChangeEvent.getEvent()); + + JSONObject data = new JSONObject(); + data.put("projects", ruleSetChangeEvent.getProjects()); + + JSONArray activatedRulesJson = new JSONArray(); + for (RuleChange rule : ruleSetChangeEvent.getActivatedRules()) { + activatedRulesJson.put(toJson(rule)); + } + data.put("activatedRules", activatedRulesJson); + + JSONArray deactivatedRulesJson = new JSONArray(); + for (RuleChange rule : ruleSetChangeEvent.getDeactivatedRules()) { + deactivatedRulesJson.put(toJson(rule)); + } + data.put("deactivatedRules", deactivatedRulesJson); + + result.put("data", data); + return result.toString(); + } + + private JSONObject toJson(RuleChange rule) { + JSONObject ruleJson = new JSONObject(); + ruleJson.put("key", rule.getKey()); + ruleJson.put("language", rule.getLanguage()); + ruleJson.put("severity", rule.getSeverity()); + ruleJson.put("templateKey", rule.getTemplateKey()); + + JSONArray params = new JSONArray(); + for (ParamChange paramChange : rule.getParams()) { + params.put(toJson(paramChange)); + } + ruleJson.put("params", params); + return ruleJson; + } + + private JSONObject toJson(ParamChange paramChange) { + JSONObject param = new JSONObject(); + param.put("key", paramChange.getKey()); + param.put("value", paramChange.getValue()); + return param; + } + class SonarLintClientEventsListener implements AsyncListener { private final SonarLintClient client; diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java index 5bcce895562..2c27c48cda1 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java @@ -84,7 +84,7 @@ public class SonarLintPushAction extends ServerPushAction { if (!isServerSideEventsRequest(servletRequest)) { servletResponse.stream().setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); - return; + return; // TODO fixme this is not closing the connexion properly } setHeadersForResponse(servletResponse); @@ -92,7 +92,7 @@ public class SonarLintPushAction extends ServerPushAction { AsyncContext asyncContext = servletRequest.startAsync(); asyncContext.setTimeout(0); - var sonarLintClient = new SonarLintClient(asyncContext, params.getProjectKeys(), params.getLanguages()); + SonarLintClient sonarLintClient = new SonarLintClient(asyncContext, params.getProjectKeys(), params.getLanguages()); clientsRegistry.registerClient(sonarLintClient); } diff --git a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java index 450245c13c0..49076199685 100644 --- a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java +++ b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java @@ -23,6 +23,7 @@ import java.util.Set; import javax.servlet.AsyncContext; import org.junit.Before; import org.junit.Test; +import org.sonar.server.qualityprofile.StandaloneRuleActivatorEventsDistributor; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -39,7 +40,7 @@ public class SonarLintClientsRegistryTest { @Before public void before() { - underTest = new SonarLintClientsRegistry(); + underTest = new SonarLintClientsRegistry(mock(StandaloneRuleActivatorEventsDistributor.class)); } @Test |