diff options
author | Belen Pruvost <belen.pruvost@sonarsource.com> | 2022-07-28 16:55:41 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-07-28 20:02:56 +0000 |
commit | 7ed0c0a19b7a0da9c6c907cab9a57a4d723dd03e (patch) | |
tree | 243c1417f126cc6f954c9c3352cf4c7f6ad61794 /server/sonar-webserver-pushapi/src/main | |
parent | ca7e8a8d9687f93d1e4ddad5748aee2d66f57252 (diff) | |
download | sonarqube-7ed0c0a19b7a0da9c6c907cab9a57a4d723dd03e.tar.gz sonarqube-7ed0c0a19b7a0da9c6c907cab9a57a4d723dd03e.zip |
SONAR-16647 - Move previous SSE events to DB queue
Diffstat (limited to 'server/sonar-webserver-pushapi/src/main')
13 files changed, 93 insertions, 506 deletions
diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/DistributedIssueChangeEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/DistributedIssueChangeEventsDistributor.java deleted file mode 100644 index ae5bbc7e5dc..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/DistributedIssueChangeEventsDistributor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.issues; - -import org.sonar.api.server.ServerSide; -import org.sonar.core.util.issue.IssueChangeListener; -import org.sonar.core.util.issue.IssueChangedEvent; -import org.sonar.process.cluster.hz.HazelcastMember; - -@ServerSide -public class DistributedIssueChangeEventsDistributor implements IssueChangeEventsDistributor { - - private HazelcastMember hazelcastMember; - - public DistributedIssueChangeEventsDistributor(HazelcastMember hazelcastMember) { - this.hazelcastMember = hazelcastMember; - } - - @Override - public void subscribe(IssueChangeListener listener) { - hazelcastMember.subscribeIssueChangeTopic(listener); - } - - @Override - public void pushEvent(IssueChangedEvent event) { - hazelcastMember.publishEvent(event); - } -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeBroadcastUtils.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeBroadcastUtils.java deleted file mode 100644 index b784f0eead5..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeBroadcastUtils.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.issues; - -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; -import org.json.JSONArray; -import org.json.JSONObject; -import org.sonar.core.util.issue.Issue; -import org.sonar.core.util.issue.IssueChangedEvent; -import org.sonar.server.pushapi.sonarlint.SonarLintClient; - -import static java.util.Arrays.asList; - -public class IssueChangeBroadcastUtils { - private IssueChangeBroadcastUtils() { - - } - - public static Predicate<SonarLintClient> getFilterForEvent(IssueChangedEvent issueChangedEvent) { - List<String> affectedProjects = asList(issueChangedEvent.getProjectKey()); - return client -> { - Set<String> clientProjectKeys = client.getClientProjectKeys(); - return !Collections.disjoint(clientProjectKeys, affectedProjects); - }; - } - - public static String getMessage(IssueChangedEvent issueChangedEvent) { - return "event: " + issueChangedEvent.getEvent() + "\n" - + "data: " + toJson(issueChangedEvent); - } - - private static String toJson(IssueChangedEvent issueChangedEvent) { - JSONObject data = new JSONObject(); - data.put("projectKey", issueChangedEvent.getProjectKey()); - - JSONArray issuesJson = new JSONArray(); - for (Issue issue : issueChangedEvent.getIssues()) { - issuesJson.put(toJson(issue)); - } - data.put("issues", issuesJson); - data.put("userSeverity", issueChangedEvent.getUserSeverity()); - data.put("userType", issueChangedEvent.getUserType()); - data.put("resolved", issueChangedEvent.getResolved()); - - return data.toString(); - } - - private static JSONObject toJson(Issue issue) { - JSONObject ruleJson = new JSONObject(); - ruleJson.put("issueKey", issue.getIssueKey()); - ruleJson.put("branchName", issue.getBranchName()); - return ruleJson; - } - -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeEventServiceImpl.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeEventServiceImpl.java index 54dc1c71ba1..2e0e7f9b223 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeEventServiceImpl.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeEventServiceImpl.java @@ -19,6 +19,8 @@ */ package org.sonar.server.pushapi.issues; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; @@ -31,9 +33,13 @@ import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.FieldDiffs.Diff; import org.sonar.core.util.issue.Issue; import org.sonar.core.util.issue.IssueChangedEvent; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; import org.sonar.db.component.BranchDto; import org.sonar.db.component.ComponentDto; +import org.sonar.db.pushevent.PushEventDto; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.elasticsearch.common.Strings.isNullOrEmpty; import static org.sonar.api.issue.DefaultTransitions.CONFIRM; import static org.sonar.api.issue.DefaultTransitions.FALSE_POSITIVE; @@ -43,6 +49,9 @@ import static org.sonar.db.component.BranchType.BRANCH; @ServerSide public class IssueChangeEventServiceImpl implements IssueChangeEventService { + private static final Gson GSON = new GsonBuilder().create(); + + private static final String EVENT_NAME = "IssueChanged"; private static final String FALSE_POSITIVE_KEY = "FALSE-POSITIVE"; private static final String WONT_FIX_KEY = "WONTFIX"; @@ -50,10 +59,10 @@ public class IssueChangeEventServiceImpl implements IssueChangeEventService { private static final String SEVERITY_KEY = "severity"; private static final String TYPE_KEY = "type"; - private final IssueChangeEventsDistributor eventsDistributor; + private final DbClient dbClient; - public IssueChangeEventServiceImpl(IssueChangeEventsDistributor eventsDistributor) { - this.eventsDistributor = eventsDistributor; + public IssueChangeEventServiceImpl(DbClient dbClient) { + this.dbClient = dbClient; } @Override @@ -69,7 +78,8 @@ public class IssueChangeEventServiceImpl implements IssueChangeEventService { IssueChangedEvent event = new IssueChangedEvent(projectKey, new Issue[]{changedIssue}, resolved, severity, type); - eventsDistributor.pushEvent(event); + + persistEvent(event, issue.projectUuid()); } @Override @@ -96,7 +106,7 @@ public class IssueChangeEventServiceImpl implements IssueChangeEventService { IssueChangedEvent event = getIssueChangedEvent(projectKey, issuesInProject, issueChanges); if (event != null) { - eventsDistributor.pushEvent(event); + persistEvent(event, entry.getValue().projectUuid()); } } } @@ -151,4 +161,19 @@ public class IssueChangeEventServiceImpl implements IssueChangeEventService { return transitionOrStatus.equals(WONT_FIX) || transitionOrStatus.equals(FALSE_POSITIVE) || transitionOrStatus.equals(FALSE_POSITIVE_KEY) || transitionOrStatus.equals(WONT_FIX_KEY); } + + private void persistEvent(IssueChangedEvent event, String entry) { + try (DbSession dbSession = dbClient.openSession(false)) { + PushEventDto eventDto = new PushEventDto() + .setName(EVENT_NAME) + .setProjectUuid(entry) + .setPayload(serializeIssueToPushEvent(event)); + dbClient.pushEventDao().insert(dbSession, eventDto); + dbSession.commit(); + } + } + + private static byte[] serializeIssueToPushEvent(IssueChangedEvent event) { + return GSON.toJson(event).getBytes(UTF_8); + } } diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeEventsDistributor.java deleted file mode 100644 index 486cc87068f..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/IssueChangeEventsDistributor.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.issues; - -import org.sonar.core.util.issue.IssueChangeListener; -import org.sonar.core.util.issue.IssueChangedEvent; - -public interface IssueChangeEventsDistributor { - - void subscribe(IssueChangeListener listener); - - void pushEvent(IssueChangedEvent event); -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/StandaloneIssueChangeEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/StandaloneIssueChangeEventsDistributor.java deleted file mode 100644 index 53aa3f34054..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/issues/StandaloneIssueChangeEventsDistributor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.issues; - -import java.util.ArrayList; -import java.util.List; -import org.sonar.api.server.ServerSide; -import org.sonar.core.util.issue.IssueChangeListener; -import org.sonar.core.util.issue.IssueChangedEvent; - -@ServerSide -public class StandaloneIssueChangeEventsDistributor implements IssueChangeEventsDistributor { - - private List<IssueChangeListener> listeners = new ArrayList<>(); - - @Override - public void subscribe(IssueChangeListener listener) { - listeners.add(listener); - } - - @Override - public void pushEvent(IssueChangedEvent event) { - listeners.forEach(l -> l.listen(event)); - } -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/DistributedRuleActivatorEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/DistributedRuleActivatorEventsDistributor.java deleted file mode 100644 index a45ff9ce87f..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/DistributedRuleActivatorEventsDistributor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.qualityprofile; - -import org.sonar.api.server.ServerSide; -import org.sonar.core.util.rule.RuleActivationListener; -import org.sonar.core.util.rule.RuleSetChangedEvent; -import org.sonar.process.cluster.hz.HazelcastMember; - -@ServerSide -public class DistributedRuleActivatorEventsDistributor implements RuleActivatorEventsDistributor { - - private HazelcastMember hazelcastMember; - - public DistributedRuleActivatorEventsDistributor(HazelcastMember hazelcastMember) { - this.hazelcastMember = hazelcastMember; - } - - @Override - public void subscribe(RuleActivationListener listener) { - hazelcastMember.subscribeRuleActivationTopic(listener); - } - - @Override - public void pushEvent(RuleSetChangedEvent event) { - hazelcastMember.publishEvent(event); - } -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java index 76fc0799986..bb4bd7a0461 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/QualityProfileChangeEventServiceImpl.java @@ -19,8 +19,11 @@ */ package org.sonar.server.pushapi.qualityprofile; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -39,6 +42,7 @@ import org.sonar.core.util.rule.RuleSetChangedEvent; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.project.ProjectDto; +import org.sonar.db.pushevent.PushEventDto; import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleParamDto; import org.sonar.db.qualityprofile.OrgActiveRuleDto; @@ -47,6 +51,7 @@ import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.db.rule.RuleDto; import org.sonar.server.qualityprofile.ActiveRuleChange; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.function.Predicate.not; import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED; import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED; @@ -54,17 +59,18 @@ import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED; @ServerSide public class QualityProfileChangeEventServiceImpl implements QualityProfileChangeEventService { + private static final Gson GSON = new GsonBuilder().create(); + private static final String EVENT_NAME = "RuleSetChanged"; private final DbClient dbClient; - private final RuleActivatorEventsDistributor eventsDistributor; - public QualityProfileChangeEventServiceImpl(DbClient dbClient, RuleActivatorEventsDistributor eventsDistributor) { + public QualityProfileChangeEventServiceImpl(DbClient dbClient) { this.dbClient = dbClient; - this.eventsDistributor = eventsDistributor; } @Override - public void publishRuleActivationToSonarLintClients(ProjectDto project, @Nullable QProfileDto activatedProfile, @Nullable QProfileDto deactivatedProfile) { + public void publishRuleActivationToSonarLintClients(ProjectDto project, @Nullable QProfileDto activatedProfile, + @Nullable QProfileDto deactivatedProfile) { List<RuleChange> activatedRules = new ArrayList<>(); Set<String> deactivatedRules = new HashSet<>(); @@ -81,9 +87,8 @@ public class QualityProfileChangeEventServiceImpl implements QualityProfileChang } String language = activatedProfile != null ? activatedProfile.getLanguage() : deactivatedProfile.getLanguage(); - RuleSetChangedEvent event = new RuleSetChangedEvent(new String[] {project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new String[0]), - language); - eventsDistributor.pushEvent(event); + + persistPushEvent(project.getKey(), activatedRules.toArray(new RuleChange[0]), deactivatedRules, language, project.getUuid()); } private List<RuleChange> createRuleChanges(@NotNull QProfileDto profileDto) { @@ -193,15 +198,29 @@ public class QualityProfileChangeEventServiceImpl implements QualityProfileChang .map(RuleKey::toString) .collect(Collectors.toSet()); - Set<String> projectKeys = getProjectKeys(profiles); + Map<String, String> projectKeyAndUuids = getProjectKeyAndUuids(profiles); if (activatedRules.isEmpty() && deactivatedRules.isEmpty()) { return; } - RuleSetChangedEvent event = new RuleSetChangedEvent(projectKeys.toArray(new String[0]), activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new String[0]), - language); - eventsDistributor.pushEvent(event); + for (Map.Entry<String, String> entry : projectKeyAndUuids.entrySet()) { + persistPushEvent(entry.getKey(), activatedRules.toArray(new RuleChange[0]), deactivatedRules, language, entry.getValue()); + } + } + + private void persistPushEvent(String projectKey, RuleChange[] activatedRules, Set<String> deactivatedRules, String language, String projectUuid) { + RuleSetChangedEvent event = new RuleSetChangedEvent(projectKey, activatedRules, deactivatedRules.toArray(new String[0])); + + try (DbSession dbSession = dbClient.openSession(false)) { + PushEventDto eventDto = new PushEventDto() + .setName(EVENT_NAME) + .setProjectUuid(projectUuid) + .setLanguage(language) + .setPayload(serializeIssueToPushEvent(event)); + dbClient.pushEventDao().insert(dbSession, eventDto); + dbSession.commit(); + } } private Optional<String> templateKey(ActiveRuleChange arc) { @@ -219,17 +238,20 @@ public class QualityProfileChangeEventServiceImpl implements QualityProfileChang return Optional.empty(); } - private Set<String> getProjectKeys(Collection<QProfileDto> profiles) { - Set<String> projectKeys = new HashSet<>(); + private Map<String, String> getProjectKeyAndUuids(Collection<QProfileDto> profiles) { + Map<String, String> projectKeyAndUuids = new HashMap<>(); try (DbSession dbSession = dbClient.openSession(false)) { for (QProfileDto profileDto : profiles) { List<ProjectQprofileAssociationDto> associationDtos = dbClient.qualityProfileDao().selectSelectedProjects(dbSession, profileDto, null); for (ProjectQprofileAssociationDto associationDto : associationDtos) { - projectKeys.add(associationDto.getProjectKey()); + projectKeyAndUuids.put(associationDto.getProjectKey(), associationDto.getProjectUuid()); } } - return projectKeys; + return projectKeyAndUuids; } } + private static byte[] serializeIssueToPushEvent(RuleSetChangedEvent event) { + return GSON.toJson(event).getBytes(UTF_8); + } } diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleActivatorEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleActivatorEventsDistributor.java deleted file mode 100644 index 8914610b28a..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleActivatorEventsDistributor.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.qualityprofile; - -import org.sonar.core.util.rule.RuleActivationListener; -import org.sonar.core.util.rule.RuleSetChangedEvent; - -public interface RuleActivatorEventsDistributor { - - void subscribe(RuleActivationListener listener); - - void pushEvent(RuleSetChangedEvent event); -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleSetChangeBroadcastUtils.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleSetChangeBroadcastUtils.java deleted file mode 100644 index 159d67fa593..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/RuleSetChangeBroadcastUtils.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.qualityprofile; - -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; -import org.json.JSONArray; -import org.json.JSONObject; -import org.sonar.core.util.ParamChange; -import org.sonar.core.util.rule.RuleChange; -import org.sonar.core.util.rule.RuleSetChangedEvent; -import org.sonar.server.pushapi.sonarlint.SonarLintClient; - -import static java.util.Arrays.asList; - -public class RuleSetChangeBroadcastUtils { - private RuleSetChangeBroadcastUtils() { - } - - public static Predicate<SonarLintClient> getFilterForEvent(RuleSetChangedEvent ruleSetChangedEvent) { - List<String> affectedProjects = asList(ruleSetChangedEvent.getProjects()); - return client -> { - Set<String> clientProjectKeys = client.getClientProjectKeys(); - Set<String> languages = client.getLanguages(); - return !Collections.disjoint(clientProjectKeys, affectedProjects) && languages.contains(ruleSetChangedEvent.getLanguage()); - }; - } - - public static String getMessage(RuleSetChangedEvent ruleSetChangedEvent) { - return "event: " + ruleSetChangedEvent.getEvent() + "\n" - + "data: " + toJson(ruleSetChangedEvent); - } - - private static String toJson(RuleSetChangedEvent ruleSetChangedEvent) { - JSONObject data = new JSONObject(); - data.put("projects", ruleSetChangedEvent.getProjects()); - - JSONArray activatedRulesJson = new JSONArray(); - for (RuleChange rule : ruleSetChangedEvent.getActivatedRules()) { - activatedRulesJson.put(toJson(rule)); - } - data.put("activatedRules", activatedRulesJson); - - JSONArray deactivatedRulesJson = new JSONArray(); - for (String ruleKey : ruleSetChangedEvent.getDeactivatedRules()) { - deactivatedRulesJson.put(ruleKey); - } - data.put("deactivatedRules", deactivatedRulesJson); - - return data.toString(); - } - - private static 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 static JSONObject toJson(ParamChange paramChange) { - JSONObject param = new JSONObject(); - param.put("key", paramChange.getKey()); - param.put("value", paramChange.getValue()); - return param; - } - -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/StandaloneRuleActivatorEventsDistributor.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/StandaloneRuleActivatorEventsDistributor.java deleted file mode 100644 index 64a5d15e10a..00000000000 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/qualityprofile/StandaloneRuleActivatorEventsDistributor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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.qualityprofile; - -import java.util.ArrayList; -import java.util.List; -import org.sonar.api.server.ServerSide; -import org.sonar.core.util.rule.RuleActivationListener; -import org.sonar.core.util.rule.RuleSetChangedEvent; - -@ServerSide -public class StandaloneRuleActivatorEventsDistributor implements RuleActivatorEventsDistributor { - - private List<RuleActivationListener> listeners = new ArrayList<>(); - - @Override - public void subscribe(RuleActivationListener listener) { - listeners.add(listener); - } - - @Override - public void pushEvent(RuleSetChangedEvent event) { - listeners.forEach(l -> l.listen(event)); - } -} diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/scheduler/polling/PushEventPollScheduler.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/scheduler/polling/PushEventPollScheduler.java index 3f94d26d4f2..d7b0f9d01ab 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/scheduler/polling/PushEventPollScheduler.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/scheduler/polling/PushEventPollScheduler.java @@ -49,6 +49,7 @@ public class PushEventPollScheduler implements Startable { private static final Logger LOG = Loggers.get(PushEventPollScheduler.class); private static final String INITIAL_DELAY_IN_SECONDS = "sonar.pushevents.polling.initial.delay"; + private static final String LAST_TIMESTAMP_IN_SECONDS = "sonar.pushevents.polling.last.timestamp"; private static final String PERIOD_IN_SECONDS = "sonar.pushevents.polling.period"; private static final String PAGE_SIZE = "sonar.pushevents.polling.page.size"; @@ -91,7 +92,7 @@ public class PushEventPollScheduler implements Startable { } if (lastPullTimestamp == null) { - lastPullTimestamp = system2.now(); + lastPullTimestamp = getLastPullTimestamp(); } var projectKeys = getClientsProjectKeys(clients); @@ -120,7 +121,8 @@ public class PushEventPollScheduler implements Startable { LOG.debug("Could not find key for project with uuid [{}]", pushEventDto.getProjectUuid()); return Optional.empty(); } - return Optional.of(new SonarLintPushEvent(pushEventDto.getName(), pushEventDto.getPayload(), resolvedProjectKey)); + return Optional.of(new SonarLintPushEvent(pushEventDto.getName(), pushEventDto.getPayload(), resolvedProjectKey, + pushEventDto.getLanguage())); } private static Set<String> getClientsProjectKeys(List<SonarLintClient> clients) { @@ -154,8 +156,12 @@ public class PushEventPollScheduler implements Startable { return config.getLong(PERIOD_IN_SECONDS).orElse(40L); } + public long getLastPullTimestamp() { + // execute every 40 seconds + return config.getLong(LAST_TIMESTAMP_IN_SECONDS).orElse(system2.now()); + } + public long getPageSize() { - // 20 events per 40 seconds return config.getLong(PAGE_SIZE).orElse(20L); } 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 5905f91bb4a..ede8477eea3 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 @@ -24,62 +24,32 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Predicate; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; 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.issue.IssueChangeListener; -import org.sonar.core.util.issue.IssueChangedEvent; -import org.sonar.core.util.rule.RuleActivationListener; -import org.sonar.core.util.rule.RuleSetChangedEvent; import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.pushapi.issues.IssueChangeBroadcastUtils; -import org.sonar.server.pushapi.issues.IssueChangeEventsDistributor; -import org.sonar.server.pushapi.qualityprofile.RuleActivatorEventsDistributor; -import org.sonar.server.pushapi.qualityprofile.RuleSetChangeBroadcastUtils; @ServerSide -public class SonarLintClientsRegistry implements RuleActivationListener, IssueChangeListener { +public class SonarLintClientsRegistry { private static final Logger LOG = Loggers.get(SonarLintClientsRegistry.class); private final SonarLintClientPermissionsValidator sonarLintClientPermissionsValidator; private final List<SonarLintClient> clients = new CopyOnWriteArrayList<>(); - private final RuleActivatorEventsDistributor ruleEventsDistributor; - private final IssueChangeEventsDistributor issueChangeEventsDistributor; - private boolean registeredToEvents = false; - - public SonarLintClientsRegistry(IssueChangeEventsDistributor issueChangeEventsDistributor, - RuleActivatorEventsDistributor ruleActivatorEventsDistributor, SonarLintClientPermissionsValidator permissionsValidator) { - this.issueChangeEventsDistributor = issueChangeEventsDistributor; + public SonarLintClientsRegistry(SonarLintClientPermissionsValidator permissionsValidator) { this.sonarLintClientPermissionsValidator = permissionsValidator; - this.ruleEventsDistributor = ruleActivatorEventsDistributor; } public void registerClient(SonarLintClient sonarLintClient) { - ensureListeningToEvents(); clients.add(sonarLintClient); sonarLintClient.scheduleHeartbeat(); sonarLintClient.addListener(new SonarLintClientEventsListener(sonarLintClient)); LOG.debug("Registering new SonarLint client"); } - private synchronized void ensureListeningToEvents() { - if (registeredToEvents) { - return; - } - try { - ruleEventsDistributor.subscribe(this); - issueChangeEventsDistributor.subscribe(this); - registeredToEvents = true; - } catch (RuntimeException e) { - LOG.warn("Can not listen to rule activation or issue events for server push. Web Server might not have started fully yet.", e); - } - } - public void unregisterClient(SonarLintClient client) { client.close(); clients.remove(client); @@ -94,18 +64,8 @@ public class SonarLintClientsRegistry implements RuleActivationListener, IssueCh return clients.size(); } - @Override - public void listen(RuleSetChangedEvent ruleSetChangedEvent) { - broadcastMessage(ruleSetChangedEvent, RuleSetChangeBroadcastUtils.getFilterForEvent(ruleSetChangedEvent)); - } - - @Override - public void listen(IssueChangedEvent issueChangedEvent) { - broadcastMessage(issueChangedEvent, IssueChangeBroadcastUtils.getFilterForEvent(issueChangedEvent)); - } - public void broadcastMessage(SonarLintPushEvent event) { - clients.stream().filter(client -> client.getClientProjectKeys().contains(event.getProjectKey())) + clients.stream().filter(client -> isRelevantEvent(event, client)) .forEach(c -> { Set<String> clientProjectKeys = new HashSet<>(c.getClientProjectKeys()); clientProjectKeys.retainAll(Set.of(event.getProjectKey())); @@ -122,42 +82,9 @@ public class SonarLintClientsRegistry implements RuleActivationListener, IssueCh }); } - public void broadcastMessage(RuleSetChangedEvent event, Predicate<SonarLintClient> filter) { - clients.stream().filter(filter).forEach(c -> { - Set<String> projectKeysInterestingForClient = new HashSet<>(c.getClientProjectKeys()); - projectKeysInterestingForClient.retainAll(Set.of(event.getProjects())); - try { - sonarLintClientPermissionsValidator.validateUserCanReceivePushEventForProjects(c.getUserUuid(), projectKeysInterestingForClient); - RuleSetChangedEvent personalizedEvent = new RuleSetChangedEvent(projectKeysInterestingForClient.toArray(String[]::new), event.getActivatedRules(), - event.getDeactivatedRules(), event.getLanguage()); - String message = RuleSetChangeBroadcastUtils.getMessage(personalizedEvent); - c.writeAndFlush(message); - } catch (ForbiddenException forbiddenException) { - logClientUnauthenticated(forbiddenException); - unregisterClient(c); - } catch (IllegalStateException | IOException e) { - logUnexpectedError(e); - unregisterClient(c); - } - }); - } - - public void broadcastMessage(IssueChangedEvent event, Predicate<SonarLintClient> filter) { - clients.stream().filter(filter).forEach(c -> { - Set<String> projectKeysInterestingForClient = new HashSet<>(c.getClientProjectKeys()); - projectKeysInterestingForClient.retainAll(Set.of(event.getProjectKey())); - try { - sonarLintClientPermissionsValidator.validateUserCanReceivePushEventForProjects(c.getUserUuid(), projectKeysInterestingForClient); - String message = IssueChangeBroadcastUtils.getMessage(event); - c.writeAndFlush(message); - } catch (ForbiddenException forbiddenException) { - logClientUnauthenticated(forbiddenException); - unregisterClient(c); - } catch (IllegalStateException | IOException e) { - logUnexpectedError(e); - unregisterClient(c); - } - }); + private static boolean isRelevantEvent(SonarLintPushEvent event, SonarLintClient client) { + return client.getClientProjectKeys().contains(event.getProjectKey()) + && (!event.getName().equals("RuleSetChanged") || client.getLanguages().contains(event.getLanguage())); } private static void logUnexpectedError(Exception e) { diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushEvent.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushEvent.java index 5484fb17602..9e7ee98f044 100644 --- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushEvent.java +++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushEvent.java @@ -19,6 +19,9 @@ */ package org.sonar.server.pushapi.sonarlint; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + import static java.nio.charset.StandardCharsets.UTF_8; public class SonarLintPushEvent { @@ -26,17 +29,24 @@ public class SonarLintPushEvent { private final String name; private final byte[] data; private final String projectKey; + private final String language; - public SonarLintPushEvent(String name, byte[] data, String projectKey) { + public SonarLintPushEvent(String name, byte[] data, String projectKey, @Nullable String language) { this.name = name; this.data = data; this.projectKey = projectKey; + this.language = language; } public String getProjectKey() { return projectKey; } + @CheckForNull + public String getLanguage() { + return language; + } + public String getName() { return name; } |