hudson.plugins.sonar.SonarPublisher.java Source code

Java tutorial

Introduction

Here is the source code for hudson.plugins.sonar.SonarPublisher.java

Source

/*
 * Jenkins Plugin for SonarQube, open source software quality management tool.
 * mailto:contact AT sonarsource DOT com
 *
 * Jenkins Plugin for SonarQube 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.
 *
 * Jenkins Plugin for SonarQube 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.
 */
/*
 * Sonar 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.
 *
 * Sonar 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 Sonar; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package hudson.plugins.sonar;

import hudson.plugins.sonar.action.SonarMarkerAction;
import hudson.CopyOnWrite;
import hudson.Extension;
import hudson.EnvVars;
import hudson.Launcher;
import com.google.common.annotations.VisibleForTesting;
import hudson.Util;
import hudson.maven.MavenModuleSet;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.JDK;
import hudson.plugins.sonar.model.TriggersConfig;
import hudson.plugins.sonar.utils.Logger;
import hudson.plugins.sonar.utils.SonarMaven;
import hudson.plugins.sonar.utils.SonarUtils;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Notifier;
import hudson.tasks.Publisher;
import hudson.tasks.Maven;
import hudson.tasks.Maven.MavenInstallation;
import jenkins.model.Jenkins;
import jenkins.mvn.GlobalSettingsProvider;
import jenkins.mvn.SettingsProvider;
import jenkins.mvn.DefaultGlobalSettingsProvider;
import jenkins.mvn.DefaultSettingsProvider;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.IOException;

/**
 * Old fields should be left so that old config data can be read in, but
 * they should be deprecated and transient so that they won't show up in XML
 * when writing back
 */
public class SonarPublisher extends Notifier {

    /**
     * Identifies {@link JDK} to be used.
     * Null if no explicit configuration is required.
     *
     * <p>
     * Can't store {@link JDK} directly because {@link Jenkins} and {@link Project}
     * are saved independently.
     *
     * @see Jenkins#getJDK(String)
     */
    private String jdk;

    /**
     * Sonar installation name.
     */
    private String installationName;

    /**
     * Optional.
     *
     * @since 1.4
     */
    private String branch;

    /**
     * Optional.
     *
     * @since 1.6
     * @deprecated since 2.2 kept for progressive data migration
     */
    @VisibleForTesting
    @Deprecated
    transient String language;

    /**
     * Optional.
     */
    private final String mavenOpts;

    /**
     * Optional.
     */
    private String jobAdditionalProperties;

    /**
     * Triggers. If null, then we should use triggers from {@link SonarInstallation}.
     *
     * @since 1.2
     */
    private final TriggersConfig triggers;

    // =================================================
    // Next fields available only for free-style projects

    /**
     * Null means to reuse job default
     */
    private final String mavenInstallationName;

    /**
     * @since 1.2
     */
    private final String rootPom;

    /**
     * @since 2.1
     */
    private final SettingsProvider settings;

    /**
     * @since 2.1
     */
    private final GlobalSettingsProvider globalSettings;

    /**
     * If true, the build will use its own local Maven repository
     * via "-Dmaven.repo.local=...".
     *
     * @since 2.1
     */
    private final boolean usePrivateRepository;

    @DataBoundConstructor
    public SonarPublisher(String installationName, String branch, TriggersConfig triggers,
            String jobAdditionalProperties, String mavenOpts, String mavenInstallationName, String rootPom,
            String jdk, SettingsProvider settings, GlobalSettingsProvider globalSettings,
            boolean usePrivateRepository) {
        this.installationName = installationName;
        this.branch = branch;
        this.jdk = StringUtils.trimToNull(jdk);
        // Triggers
        this.triggers = triggers;
        // Maven
        this.mavenOpts = mavenOpts;
        this.jobAdditionalProperties = jobAdditionalProperties;
        // Non Maven Project
        this.mavenInstallationName = StringUtils.trimToNull(mavenInstallationName);
        this.rootPom = rootPom;
        this.settings = settings != null ? settings : new DefaultSettingsProvider();
        this.globalSettings = globalSettings != null ? globalSettings : new DefaultGlobalSettingsProvider();
        this.usePrivateRepository = usePrivateRepository;
    }

    // compatibility with earlier plugins
    public Object readResolve() {
        if (StringUtils.isNotBlank(this.language)) {
            StringBuilder sb = new StringBuilder();
            sb.append("-Dsonar.language=").append(language);
            if (StringUtils.isNotBlank(this.jobAdditionalProperties)) {
                sb.append(" ").append(this.jobAdditionalProperties);
            }
            this.jobAdditionalProperties = sb.toString();
        }
        return this;
    }

    public String getJdkName() {
        return jdk;
    }

    public void setJdkName(final String jdk) {
        this.jdk = jdk;
    }

    /**
     * Gets the JDK that this Sonar publisher is configured with, or null.
     */
    private JDK getJDK() {
        return Jenkins.getInstance().getJDK(jdk);
    }

    /**
     * @return name of {@link hudson.plugins.sonar.SonarInstallation}
     */
    public String getInstallationName() {
        return installationName;
    }

    public void setInstallationName(String installationName) {
        this.installationName = installationName;
    }

    /**
     * @return MAVEN_OPTS
     */
    public String getMavenOpts() {
        return mavenOpts;
    }

    /**
     * @return additional Maven options like "-Pprofile" and "-Dname=value"
     */
    public String getJobAdditionalProperties() {
        return StringUtils.trimToEmpty(jobAdditionalProperties);
    }

    public void setJobAdditionalProperties(final String jobAdditionalProperties) {
        this.jobAdditionalProperties = jobAdditionalProperties;
    }

    /**
     * @return true, if we should use triggers from {@link SonarInstallation}
     */
    public boolean isUseGlobalTriggers() {
        return triggers == null;
    }

    public boolean isUseLocalTriggers() {
        return !isUseGlobalTriggers();
    }

    /**
     * See sonar.branch option.
     *
     * @return branch
     * @since 1.4
     */
    public String getBranch() {
        return branch;
    }

    public void setBranch(final String branch) {
        this.branch = branch;
    }

    /**
     * @deprecated since 2.2
     */
    @Deprecated
    public String getLanguage() {
        return StringUtils.trimToEmpty(language);
    }

    /**
     * @return triggers configuration
     */
    public TriggersConfig getTriggers() {
        return triggers;
    }

    /**
     * @return name of {@link hudson.tasks.Maven.MavenInstallation}
     */
    public String getMavenInstallationName() {
        return mavenInstallationName;
    }

    /**
     * Root POM. Should be applied only for free-style projects.
     *
     * @return Root POM
     */
    public String getRootPom() {
        return StringUtils.trimToEmpty(rootPom);
    }

    public static boolean isMavenBuilder(AbstractProject<?, ?> currentProject) {
        return currentProject instanceof MavenModuleSet;
    }

    public SonarInstallation getInstallation() {
        return SonarInstallation.get(getInstallationName());
    }

    private boolean isSkip(AbstractBuild<?, ?> build, BuildListener listener, SonarInstallation sonarInstallation)
            throws IOException, InterruptedException {
        String skipLaunchMsg;
        if (isUseGlobalTriggers()) {
            skipLaunchMsg = sonarInstallation.getTriggers().isSkipSonar(build, listener);
        } else {
            skipLaunchMsg = getTriggers().isSkipSonar(build, listener);
        }
        if (skipLaunchMsg != null) {
            listener.getLogger().println(skipLaunchMsg);
            return true;
        }
        return false;
    }

    @Override
    public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
            throws IOException, InterruptedException {
        if (!SonarInstallation.isValid(getInstallationName(), listener)) {
            return false;
        }
        SonarInstallation sonarInstallation = getInstallation();

        if (isSkip(build, listener, sonarInstallation)) {
            SonarUtils.addBuildInfoFromLastBuildTo(build, sonarInstallation.getName(), true);
            return true;
        }

        boolean sonarSuccess = executeSonar(build, launcher, listener, sonarInstallation);

        if (!sonarSuccess) {
            // returning false has no effect on the global build status so need to do it manually
            build.setResult(Result.FAILURE);
        }
        SonarUtils.addBuildInfoTo(build, sonarInstallation.getName());
        listener.getLogger().println("SonarQube analysis completed: " + build.getResult());
        return sonarSuccess;
    }

    public MavenModuleSet getMavenProject(AbstractBuild<?, ?> build) {
        return (build.getProject() instanceof MavenModuleSet) ? (MavenModuleSet) build.getProject() : null;
    }

    private String getPomName(AbstractBuild<?, ?> build, BuildListener listener)
            throws IOException, InterruptedException {
        String pomName;
        MavenModuleSet mavenModuleProject = getMavenProject(build);
        if (mavenModuleProject != null) {
            EnvVars envVars = build.getEnvironment(listener);
            pomName = mavenModuleProject.getRootPOM(envVars);
        } else {
            pomName = getRootPom();
        }
        if (StringUtils.isEmpty(pomName)) {
            pomName = "pom.xml";
        }
        return pomName;
    }

    private boolean executeSonar(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener,
            SonarInstallation sonarInstallation) {
        try {
            String pomName = getPomName(build, listener);
            String mavenInstallName = getMavenInstallationName();
            if (isMavenBuilder(build.getProject())) {
                MavenModuleSet mavenModuleSet = getMavenProject(build);
                if (null != mavenModuleSet.getMaven().getName()) {
                    mavenInstallName = mavenModuleSet.getMaven().getName();
                }
            }

            // Execute maven
            return SonarMaven.executeMaven(build, launcher, listener, mavenInstallName, pomName, sonarInstallation,
                    this, getJDK(), getSettings(), getGlobalSettings(), usesPrivateRepository());
        } catch (IOException e) {
            Logger.printFailureMessage(listener);
            Util.displayIOException(e, listener);
            Logger.LOG.throwing(this.getClass().getName(), "setValues", e);
            e.printStackTrace(listener.fatalError("command execution failed"));
            return false;
        } catch (InterruptedException e) {
            Logger.LOG.throwing(this.getClass().getName(), "executeSonar", e);
            return false;
        } catch (Exception e) {
            Logger.printFailureMessage(listener);
            e.printStackTrace(listener.fatalError("command execution failed"));
            Logger.LOG.throwing(this.getClass().getName(), "executeSonar", e);
            return false;
        }
    }

    @Override
    public Action getProjectAction(AbstractProject<?, ?> project) {
        return new SonarMarkerAction();
    }

    @Override
    public BuildStepMonitor getRequiredMonitorService() {
        return BuildStepMonitor.BUILD;
    }

    /**
     * @since 2.1
     */
    public SettingsProvider getSettings() {
        return settings != null ? settings : new DefaultSettingsProvider();
    }

    /**
     * @since 2.1
     */
    public GlobalSettingsProvider getGlobalSettings() {
        return globalSettings != null ? globalSettings : new DefaultGlobalSettingsProvider();
    }

    public boolean usesPrivateRepository() {
        return usePrivateRepository;
    }

    /**
     * Optional because Maven plugin might not be available.
     */
    @Extension(ordinal = 1000, optional = true)
    public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {

        @CopyOnWrite
        private volatile SonarInstallation[] installations = new SonarInstallation[0];
        private volatile boolean buildWrapperEnabled = false;

        public DescriptorImpl() {
            load();
        }

        @Override
        public String getDisplayName() {
            return Messages.SonarPublisher_DisplayName();
        }

        /**
         * @return all configured {@link hudson.plugins.sonar.SonarInstallation}
         * @deprecated Global configuration has migrated to {@link SonarGlobalConfiguration}
         */
        @Deprecated
        public SonarInstallation[] getDeprecatedInstallations() {
            return installations;
        }

        /**
         * @deprecated Global configuration has migrated to {@link SonarGlobalConfiguration} 
         */
        @Deprecated
        public boolean isDeprecatedBuildWrapperEnabled() {
            return buildWrapperEnabled;
        }

        /**
         * @deprecated Global configuration has migrated to {@link SonarGlobalConfiguration} 
         */
        @Deprecated
        @VisibleForTesting
        void setDeprecatedInstallations(SonarInstallation[] installations) {
            this.installations = installations;
        }

        /**
         * @deprecated Global configuration has migrated to {@link SonarGlobalConfiguration} 
         */
        @Deprecated
        @VisibleForTesting
        void setDeprecatedBuildWrapperEnabled(boolean enabled) {
            this.buildWrapperEnabled = enabled;
        }

        public SonarInstallation[] getInstallations() {
            return SonarInstallation.all();
        }

        @Override
        public String getHelpFile() {
            return "/plugin/sonar/help-sonar-publisher.html";
        }

        @Override
        public String getHelpFile(String fieldName) {
            if ("globalSettings".equals(fieldName) || "settings".equals(fieldName)) {
                // Reuse help from Maven plugin
                return Jenkins.getInstance().getDescriptorByType(Maven.DescriptorImpl.class)
                        .getHelpFile("settings");
            }
            return super.getHelpFile(fieldName);
        }

        /**
         * Deletes all global configuration done through this component.
         * To be done after migration to {@link SonarGlobalConfiguration}.
         */
        public void deleteGlobalConfiguration() {
            installations = null;
            buildWrapperEnabled = false;
            save();
        }

        /**
         * This method is used in UI, so signature and location of this method is important (see SONARPLUGINS-1337).
         *
         * @return all configured {@link hudson.tasks.Maven.MavenInstallation}
         */
        public MavenInstallation[] getMavenInstallations() {
            return Jenkins.getInstance().getDescriptorByType(Maven.DescriptorImpl.class).getInstallations();
        }

        @Override
        public boolean isApplicable(Class<? extends AbstractProject> jobType) {
            // eventually check if job type of FreeStyleProject.class || MavenModuleSet.class
            return true;
        }
    }
}