Skip to content

Commit 638e063

Browse files
SLCORE-2062 Fix support for sonarlint-omnisharp (#1982)
* SLCORE-2062 Fix support for sonarlint-omnisharp * Do not load Omnisharp if dependencies are missing * Use a single way to sync server plugins * Factorize the plugins synchronization * Avoid using the fork-join pool * PR review * Fix text developer sync * Fix consecutive reset commands
1 parent e99bcf3 commit 638e063

58 files changed

Lines changed: 1307 additions & 1612 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/AnalysisQueue.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ public synchronized Command takeNextCommand() throws InterruptedException {
7676
}
7777
}
7878

79-
public synchronized void clearAllButAnalyses() {
80-
removeAll(queuedCommand -> !(queuedCommand.command instanceof AnalyzeCommand));
79+
public synchronized void clearAllButAnalysesAndResets() {
80+
removeAll(queuedCommand -> !(queuedCommand.command instanceof AnalyzeCommand) && !(queuedCommand.command instanceof ResetPluginsCommand));
8181
}
8282

8383
private Optional<QueuedCommand> pollNextReadyCommand() {

backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/AnalysisScheduler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public AnalysisScheduler(AnalysisSchedulerConfiguration analysisGlobalConfig, Lo
5151
analysisThread.start();
5252
}
5353

54-
public void reset(AnalysisSchedulerConfiguration analysisGlobalConfig, Supplier<LoadedPlugins> pluginsSupplier) {
55-
post(new ResetPluginsCommand(analysisGlobalConfig, globalAnalysisContainer, analysisQueue, pluginsSupplier));
54+
public void reset(Supplier<SchedulerResetConfiguration> pluginsWithConfigSupplier) {
55+
post(new ResetPluginsCommand(globalAnalysisContainer, analysisQueue, pluginsWithConfigSupplier));
5656
}
5757

5858
public void wakeUp() {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* SonarLint Core - Analysis Engine
3+
* Copyright (C) SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonarsource.sonarlint.core.analysis;
21+
22+
import org.sonarsource.sonarlint.core.analysis.api.AnalysisSchedulerConfiguration;
23+
import org.sonarsource.sonarlint.core.plugin.commons.LoadedPlugins;
24+
25+
/**
26+
* Pairs an {@link AnalysisSchedulerConfiguration} with the {@link LoadedPlugins} it was built
27+
* alongside, so both are always resolved atomically inside a
28+
* {@link org.sonarsource.sonarlint.core.analysis.command.ResetPluginsCommand}.
29+
*/
30+
public record SchedulerResetConfiguration(AnalysisSchedulerConfiguration config, LoadedPlugins plugins) {
31+
}

backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/command/ResetPluginsCommand.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,29 @@
2222
import java.util.concurrent.atomic.AtomicReference;
2323
import java.util.function.Supplier;
2424
import org.sonarsource.sonarlint.core.analysis.AnalysisQueue;
25-
import org.sonarsource.sonarlint.core.analysis.api.AnalysisSchedulerConfiguration;
25+
import org.sonarsource.sonarlint.core.analysis.SchedulerResetConfiguration;
2626
import org.sonarsource.sonarlint.core.analysis.container.global.GlobalAnalysisContainer;
2727
import org.sonarsource.sonarlint.core.analysis.container.global.ModuleRegistry;
28-
import org.sonarsource.sonarlint.core.plugin.commons.LoadedPlugins;
2928

3029
public class ResetPluginsCommand extends Command {
3130

32-
private final AnalysisSchedulerConfiguration analysisGlobalConfig;
33-
private final Supplier<LoadedPlugins> pluginsSupplier;
31+
private final Supplier<SchedulerResetConfiguration> schedulerResetConfigurationSupplier;
3432
private final AtomicReference<GlobalAnalysisContainer> globalAnalysisContainer;
3533
private final AnalysisQueue analysisQueue;
3634

37-
public ResetPluginsCommand(AnalysisSchedulerConfiguration analysisGlobalConfig, AtomicReference<GlobalAnalysisContainer> globalAnalysisContainer, AnalysisQueue analysisQueue,
38-
Supplier<LoadedPlugins> pluginsSupplier) {
39-
this.analysisGlobalConfig = analysisGlobalConfig;
40-
this.pluginsSupplier = pluginsSupplier;
35+
public ResetPluginsCommand(AtomicReference<GlobalAnalysisContainer> globalAnalysisContainer, AnalysisQueue analysisQueue,
36+
Supplier<SchedulerResetConfiguration> schedulerResetConfigurationSupplier) {
37+
this.schedulerResetConfigurationSupplier = schedulerResetConfigurationSupplier;
4138
this.globalAnalysisContainer = globalAnalysisContainer;
4239
this.analysisQueue = analysisQueue;
4340
}
4441

4542
@Override
4643
public void execute(ModuleRegistry moduleRegistry) {
4744
globalAnalysisContainer.get().stopComponents();
48-
var newPlugins = pluginsSupplier.get();
49-
globalAnalysisContainer.set(new GlobalAnalysisContainer(analysisGlobalConfig, newPlugins));
45+
var pluginsWithConfig = schedulerResetConfigurationSupplier.get();
46+
globalAnalysisContainer.set(new GlobalAnalysisContainer(pluginsWithConfig.config(), pluginsWithConfig.plugins()));
5047
globalAnalysisContainer.get().startComponents();
51-
analysisQueue.clearAllButAnalyses();
48+
analysisQueue.clearAllButAnalysesAndResets();
5249
}
5350
}

backend/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginSynchronizationSummary.java renamed to backend/commons/src/main/java/org/sonarsource/sonarlint/core/commons/plugins/Dependency.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SonarLint Core - Server Connection
2+
* SonarLint Core - Commons
33
* Copyright (C) SonarSource Sàrl
44
* mailto:info AT sonarsource DOT com
55
*
@@ -17,16 +17,14 @@
1717
* along with this program; if not, write to the Free Software Foundation,
1818
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
*/
20-
package org.sonarsource.sonarlint.core.serverconnection;
20+
package org.sonarsource.sonarlint.core.commons.plugins;
2121

22-
public class PluginSynchronizationSummary {
23-
private final boolean anyPluginSynchronized;
24-
25-
public PluginSynchronizationSummary(boolean anyPluginSynchronized) {
26-
this.anyPluginSynchronized = anyPluginSynchronized;
22+
public record Dependency(SonarArtifact artifact, boolean optional) {
23+
public static Dependency optional(SonarArtifact artifact) {
24+
return new Dependency(artifact, true);
2725
}
2826

29-
public boolean anyPluginSynchronized() {
30-
return anyPluginSynchronized;
27+
public static Dependency required(SonarArtifact key) {
28+
return new Dependency(key, false);
3129
}
3230
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* SonarLint Core - Commons
3+
* Copyright (C) SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonarsource.sonarlint.core.commons.plugins;
21+
22+
import java.util.Set;
23+
import org.sonarsource.sonarlint.core.commons.api.SonarLanguage;
24+
25+
public interface SonarArtifact {
26+
/* A key that uniquely identifies the artifact */
27+
String getKey();
28+
29+
/* The list of languages that the artifact supports */
30+
Set<SonarLanguage> getLanguages();
31+
}

backend/commons/src/main/java/org/sonarsource/sonarlint/core/commons/plugins/SonarPlugin.java

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,17 @@
2121

2222
import java.util.Arrays;
2323
import java.util.EnumSet;
24-
import java.util.Objects;
2524
import java.util.Optional;
2625
import java.util.Set;
2726
import java.util.stream.Collectors;
2827
import javax.annotation.Nullable;
2928
import org.sonarsource.sonarlint.core.commons.Version;
3029
import org.sonarsource.sonarlint.core.commons.api.SonarLanguage;
3130

32-
public enum SonarPlugin {
31+
public enum SonarPlugin implements SonarArtifact {
3332
ABAP("abap"),
3433
APEX("sonarapex"),
3534
C_FAMILY("cpp"),
36-
// CSHARP_ENTERPRISE must be declared before CS_OSS so it can be referenced in the constructor
3735
CSHARP_ENTERPRISE("csharpenterprise"),
3836
CS_OSS("csharp", CSHARP_ENTERPRISE),
3937
COBOL("cobol"),
@@ -50,11 +48,17 @@ public enum SonarPlugin {
5048
RPG("rpg"),
5149
RUBY("ruby"),
5250
SCALA("sonarscala"),
51+
SONARLINT_OMNISHARP("omnisharp", Set.of(
52+
Dependency.required(CS_OSS),
53+
Dependency.optional(CSHARP_ENTERPRISE),
54+
Dependency.required(SonarPluginDependency.OMNISHARP_MONO),
55+
Dependency.required(SonarPluginDependency.OMNISHARP_NET472),
56+
Dependency.required(SonarPluginDependency.OMNISHARP_NET6))),
5357
SWIFT("swift"),
58+
TEXT_DEVELOPER("textdeveloper"),
5459
TEXT_ENTERPRISE("textenterprise"),
55-
TEXT("text", TEXT_ENTERPRISE),
60+
TEXT("text", TEXT_DEVELOPER, TEXT_ENTERPRISE),
5661
TSQL("tsql"),
57-
// VBNET_ENTERPRISE must be declared before VBNET_OSS
5862
VBNET_ENTERPRISE("vbnetenterprise"),
5963
VBNET_OSS("vbnet", VBNET_ENTERPRISE),
6064
WEB("web"),
@@ -75,8 +79,7 @@ public static Optional<SonarPlugin> findByKey(String key) {
7579
*/
7680
public static boolean isEnterpriseVariant(String key) {
7781
return Arrays.stream(values())
78-
.map(p -> p.enterpriseVariant)
79-
.filter(Objects::nonNull)
82+
.flatMap(p -> p.enterpriseVariants.stream())
8083
.anyMatch(ev -> ev.getKey().equals(key));
8184
}
8285

@@ -87,7 +90,7 @@ public static boolean isEnterpriseVariant(String key) {
8790
*/
8891
public static Optional<SonarPlugin> basePluginFor(String enterpriseKey) {
8992
return Arrays.stream(values())
90-
.filter(p -> p.enterpriseVariant != null && p.enterpriseVariant.getKey().equals(enterpriseKey))
93+
.filter(p -> p.enterpriseVariants.stream().map(SonarPlugin::getKey).anyMatch(key -> key.equals(enterpriseKey)))
9194
.findFirst();
9295
}
9396

@@ -103,47 +106,59 @@ public static Optional<String> baseKeyFor(String enterpriseKey) {
103106

104107
private final String key;
105108
/**
106-
* Non-null for plugins that have an enterprise variant plugin that uses a
107-
* different server key (e.g. {@code CSHARP_ENTERPRISE}).
109+
* Non-empty for plugins that have at least one enterprise variant plugin that uses a
110+
* different server key (e.g. {@code CSHARP_ENTERPRISE}). There can be more than one variant.
108111
*/
109-
@Nullable
110-
private final SonarPlugin enterpriseVariant;
112+
private final Set<SonarPlugin> enterpriseVariants;
111113
/**
112114
* Non-null for plugins whose enterprise edition is a drop-in replacement served under the
113115
* <em>same</em> plugin key (GO, IAC, TEXT).
114116
*/
115117
@Nullable
116118
private final EnterpriseReplacement enterpriseReplacement;
119+
private final Set<Dependency> dependencies;
117120

118121
SonarPlugin(String key) {
119122
this.key = key;
120-
this.enterpriseVariant = null;
123+
this.enterpriseVariants = Set.of();
121124
this.enterpriseReplacement = null;
125+
this.dependencies = Set.of();
122126
}
123127

124128
/** Constructor for plugins with a different-key enterprise variant (CS, VBNET). */
125-
SonarPlugin(String key, SonarPlugin enterpriseVariant) {
129+
SonarPlugin(String key, SonarPlugin... enterpriseVariants) {
126130
this.key = key;
127-
this.enterpriseVariant = enterpriseVariant;
131+
this.enterpriseVariants = Set.of(enterpriseVariants);
128132
this.enterpriseReplacement = null;
133+
this.dependencies = Set.of();
129134
}
130135

131136
/** Constructor for same-key enterprise plugins (GO, IAC, TEXT). */
132137
SonarPlugin(String key, EnterpriseReplacement enterpriseReplacement) {
133138
this.key = key;
134-
this.enterpriseVariant = null;
139+
this.enterpriseVariants = Set.of();
135140
this.enterpriseReplacement = enterpriseReplacement;
141+
this.dependencies = Set.of();
136142
}
137143

144+
/** Constructor for plugins with dependencies (e.g. SONARLINT_OMNISHARP). */
145+
SonarPlugin(String key, Set<Dependency> dependencies) {
146+
this.key = key;
147+
this.enterpriseVariants = Set.of();
148+
this.enterpriseReplacement = null;
149+
this.dependencies = dependencies;
150+
}
151+
152+
@Override
138153
public String getKey() {
139154
return key;
140155
}
141156

142157
/**
143-
* Returns the enterprise variant plugin (with a different key) for this plugin, if any.
158+
* Returns the enterprise variant plugins (with different keys) for this plugin, if any. Never null.
144159
*/
145-
public Optional<SonarPlugin> getEnterpriseVariant() {
146-
return Optional.ofNullable(enterpriseVariant);
160+
public Set<SonarPlugin> getEnterpriseVariants() {
161+
return enterpriseVariants;
147162
}
148163

149164
/**
@@ -154,6 +169,11 @@ public Optional<EnterpriseReplacement> getEnterpriseReplacement() {
154169
return Optional.ofNullable(enterpriseReplacement);
155170
}
156171

172+
public Set<Dependency> getDependencies() {
173+
return dependencies;
174+
}
175+
176+
@Override
157177
public Set<SonarLanguage> getLanguages() {
158178
var sonarLanguages = EnumSet.noneOf(SonarLanguage.class);
159179
sonarLanguages.addAll(Arrays.stream(SonarLanguage.values()).filter(l -> l.getPlugin().getKey().equals(key)).collect(Collectors.toSet()));
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* SonarLint Core - Commons
3+
* Copyright (C) SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonarsource.sonarlint.core.commons.plugins;
21+
22+
import java.util.Arrays;
23+
import java.util.Optional;
24+
import java.util.Set;
25+
import java.util.stream.Collectors;
26+
import org.sonarsource.sonarlint.core.commons.api.SonarLanguage;
27+
28+
public enum SonarPluginDependency implements SonarArtifact {
29+
OMNISHARP_MONO("omnisharp-mono"),
30+
OMNISHARP_NET472("omnisharp-net472"),
31+
OMNISHARP_NET6("omnisharp-net6");
32+
33+
public static Optional<SonarPluginDependency> findByKey(String key) {
34+
return Arrays.stream(values()).filter(p -> p.key.equals(key)).findFirst();
35+
}
36+
37+
private final String key;
38+
39+
SonarPluginDependency(String key) {
40+
this.key = key;
41+
}
42+
43+
@Override
44+
public String getKey() {
45+
return key;
46+
}
47+
48+
/**
49+
* All current dependency artifacts are Omnisharp-related and support C# only.
50+
* Computed lazily to avoid circular static initialization between SonarLanguage,
51+
* SonarPlugin, and SonarPluginDependency.
52+
*/
53+
@Override
54+
public Set<SonarLanguage> getLanguages() {
55+
return Set.of(SonarLanguage.CS);
56+
}
57+
58+
public Set<SonarPlugin> getDependents() {
59+
return Arrays.stream(SonarPlugin.values())
60+
.filter(plugin -> plugin.getDependencies().stream().anyMatch(dep -> dep.artifact().equals(this)))
61+
.collect(Collectors.toSet());
62+
}
63+
}

0 commit comments

Comments
 (0)