All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sorald.sonar.SonarLintEngine Maven / Gradle / Ivy

Go to download

An automatic repair system for static code analysis warnings from Sonar Java.

There is a newer version: 0.8.6
Show newest version
package sorald.sonar;

import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.sonarsource.sonarlint.core.AbstractSonarLintEngine;
import org.sonarsource.sonarlint.core.analysis.AnalysisEngine;
import org.sonarsource.sonarlint.core.analysis.api.ActiveRule;
import org.sonarsource.sonarlint.core.analysis.api.AnalysisConfiguration;
import org.sonarsource.sonarlint.core.analysis.api.AnalysisEngineConfiguration;
import org.sonarsource.sonarlint.core.analysis.api.AnalysisResults;
import org.sonarsource.sonarlint.core.analysis.command.AnalyzeCommand;
import org.sonarsource.sonarlint.core.client.api.common.PluginDetails;
import org.sonarsource.sonarlint.core.client.api.common.RuleKey;
import org.sonarsource.sonarlint.core.client.api.common.analysis.DefaultClientIssue;
import org.sonarsource.sonarlint.core.client.api.common.analysis.IssueListener;
import org.sonarsource.sonarlint.core.client.api.exceptions.SonarLintWrappedException;
import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneAnalysisConfiguration;
import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneGlobalConfiguration;
import org.sonarsource.sonarlint.core.commons.Language;
import org.sonarsource.sonarlint.core.commons.log.ClientLogOutput;
import org.sonarsource.sonarlint.core.commons.progress.ClientProgressMonitor;
import org.sonarsource.sonarlint.core.commons.progress.ProgressMonitor;
import org.sonarsource.sonarlint.core.plugin.commons.PluginInstancesRepository;
import org.sonarsource.sonarlint.core.plugin.commons.PluginInstancesRepository.Configuration;
import org.sonarsource.sonarlint.core.rule.extractor.SonarLintRuleDefinition;
import sorald.FileUtils;
import sorald.util.ConfigLoader;

public final class SonarLintEngine extends AbstractSonarLintEngine {

    // The order of these initialisations is important as each field is dependent upon the previous
    // one.
    private static final String SONAR_JAVA_PLUGIN_JAR_NAME = "sonar-java-plugin.jar";
    private static final Path sonarJavaPlugin = getOrDownloadSonarJavaPlugin().getPath();
    private static final StandaloneGlobalConfiguration globalConfig = buildGlobalConfig();
    private static final PluginInstancesRepositoryWhichCannotBeClosed pluginInstancesRepository =
            createPluginInstancesRepository();
    private static final Map allRulesDefinitionsByKey =
            computeAllRulesDefinitionsByKey();
    private static final AnalysisEngineConfiguration analysisGlobalConfig =
            buildAnalysisEngineConfiguration();

    // The only instance of this singleton class
    private static SonarLintEngine theOnlyInstance;

    // We need to reinitialise it before starting analysis of any source files on any rules.
    private AnalysisEngine analysisEngine;

    private SonarLintEngine() {
        super(null);
        setLogging(null);

        this.analysisEngine =
                new AnalysisEngine(analysisGlobalConfig, pluginInstancesRepository, null);
    }

    private static SonarJavaJarHolder getOrDownloadSonarJavaPlugin() {
        File cacheDirectory = FileUtils.getCacheDir();
        String sonarJavaPluginFileName =
                cacheDirectory + System.getProperty("file.separator") + SONAR_JAVA_PLUGIN_JAR_NAME;
        File sonarJavaPlugin = new File(sonarJavaPluginFileName);
        if (sonarJavaPlugin.exists()) {
            return new SonarJavaJarHolder(sonarJavaPlugin.toPath(), false);
        }

        try {
            InputStream inputStream = new URL(ConfigLoader.getSonarJavaPluginUrl()).openStream();
            Files.copy(
                    inputStream,
                    Paths.get(sonarJavaPluginFileName),
                    StandardCopyOption.REPLACE_EXISTING);
            return new SonarJavaJarHolder(new File(sonarJavaPluginFileName).toPath(), true);
        } catch (IOException ignore) {
            throw new RuntimeException("Could not download Sonar Java plugin"); // NOSONAR:S112
        }
    }

    /** Store the path to SonarJava plugin and if it is downloaded or fetched from cache. */
    public static class SonarJavaJarHolder {
        private final Path path;
        private final boolean downloaded;

        SonarJavaJarHolder(Path path, boolean downloaded) {
            this.path = path;
            this.downloaded = downloaded;
        }

        public Path getPath() {
            return path;
        }

        public boolean isDownloaded() {
            return downloaded;
        }
    }

    private static StandaloneGlobalConfiguration buildGlobalConfig() {
        return StandaloneGlobalConfiguration.builder()
                .addPlugin(sonarJavaPlugin)
                .addEnabledLanguage(Language.JAVA)
                .build();
    }

    private static PluginInstancesRepositoryWhichCannotBeClosed createPluginInstancesRepository() {
        var config =
                new Configuration(
                        globalConfig.getPluginPaths(),
                        globalConfig.getEnabledLanguages(),
                        Optional.ofNullable(globalConfig.getNodeJsVersion()));
        return new PluginInstancesRepositoryWhichCannotBeClosed(config);
    }

    private static Map computeAllRulesDefinitionsByKey() {
        return loadPluginMetadata(
                pluginInstancesRepository, globalConfig.getEnabledLanguages(), false);
    }

    private static AnalysisEngineConfiguration buildAnalysisEngineConfiguration() {
        return AnalysisEngineConfiguration.builder()
                .addEnabledLanguages(globalConfig.getEnabledLanguages())
                .setClientPid(globalConfig.getClientPid())
                .setExtraProperties(globalConfig.extraProperties())
                .setWorkDir(globalConfig.getWorkDir())
                .setModulesProvider(globalConfig.getModulesProvider())
                .build();
    }

    /** Get or creates the one and only instance of this class. */
    public static SonarLintEngine getInstance() {
        if (theOnlyInstance == null) {
            theOnlyInstance = new SonarLintEngine();
        } else {
            theOnlyInstance.recreateAnalysisEngine();
        }
        return theOnlyInstance;
    }

    /**
     * Recreates the analysis engine as it is stopped after each analysis executed by {@link
     * SonarStaticAnalyzer}.
     */
    public void recreateAnalysisEngine() {
        this.analysisEngine =
                new AnalysisEngine(analysisGlobalConfig, pluginInstancesRepository, logOutput);
    }

    @Override
    public AnalysisEngine getAnalysisEngine() {
        return analysisEngine;
    }

    public AnalysisResults analyze(
            StandaloneAnalysisConfiguration configuration,
            IssueListener issueListener,
            @Nullable ClientLogOutput logOutput,
            @Nullable ClientProgressMonitor monitor) {
        requireNonNull(configuration);
        requireNonNull(issueListener);
        setLogging(logOutput);

        var analysisConfig =
                AnalysisConfiguration.builder()
                        .addInputFiles(configuration.inputFiles())
                        .putAllExtraProperties(configuration.extraProperties())
                        .addActiveRules(identifyActiveRules(configuration))
                        .setBaseDir(configuration.baseDir())
                        .build();
        try {
            var analysisResults =
                    analysisEngine
                            .post(
                                    new AnalyzeCommand(
                                            configuration.moduleKey(),
                                            analysisConfig,
                                            i ->
                                                    issueListener.handle(
                                                            new DefaultClientIssue(
                                                                    i,
                                                                    allRulesDefinitionsByKey.get(
                                                                            i.getRuleKey()))),
                                            logOutput),
                                    new ProgressMonitor(monitor))
                            .get();
            return analysisResults == null ? new AnalysisResults() : analysisResults;
        } catch (ExecutionException e) {
            throw SonarLintWrappedException.wrap(e);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw SonarLintWrappedException.wrap(e);
        }
    }

    /** Returns all rule keys available at the provided version of Sonar Java. */
    public static Map getAllRulesDefinitionsByKey() {
        return allRulesDefinitionsByKey;
    }

    private Collection identifyActiveRules(
            StandaloneAnalysisConfiguration configuration) {
        Set includedRules =
                configuration.includedRules().stream().map(RuleKey::toString).collect(toSet());

        return allRulesDefinitionsByKey.values().stream()
                .filter(isImplementedBySonarJavaPlugin(includedRules))
                .map(
                        rd -> {
                            ActiveRule activeRule =
                                    new ActiveRule(rd.getKey(), rd.getLanguage().getLanguageKey());
                            RuleKey ruleKey = RuleKey.parse(rd.getKey());
                            if (configuration.ruleParameters().containsKey(ruleKey)) {
                                activeRule.setParams(configuration.ruleParameters().get(ruleKey));
                            }
                            return activeRule;
                        })
                .collect(Collectors.toList());
    }

    private static Predicate isImplementedBySonarJavaPlugin(
            Set includedRules) {
        return r -> includedRules.contains(r.getKey());
    }

    public void stop() {
        analysisEngine.stop();
    }

    public List getPluginDetails() {
        throw new RuntimeException("Not implemented"); // NOSONAR:S112
    }

    /**
     * Overriding this class to ensure its instance never closes throughout the lifecycle of JVM.
     */
    public static class PluginInstancesRepositoryWhichCannotBeClosed
            extends PluginInstancesRepository {

        public PluginInstancesRepositoryWhichCannotBeClosed(Configuration configuration) {
            super(configuration);
        }

        @Override
        public void close() throws Exception {
            // Prevent closing of instance of this class
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy