Package hudson.plugins.sonar

Source Code of hudson.plugins.sonar.SonarRunnerBuilder$DescriptorImpl

/*
* 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 com.google.common.annotations.VisibleForTesting;
import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.model.JDK;
import hudson.plugins.sonar.utils.ExtendedArgumentListBuilder;
import hudson.plugins.sonar.utils.Logger;
import hudson.plugins.sonar.utils.SonarUtils;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.ArgumentListBuilder;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Map.Entry;
import java.util.Properties;

/**
* @since 1.7
*/
public class SonarRunnerBuilder extends Builder {

  /**
   * Identifies {@link SonarInstallation} to be used.
   */
  private final String installationName;
  private final String project;
  private final String properties;
  private final String javaOpts;

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

  /**
   * Identifies {@link SonarRunnerInstallation} to be used.
   * @since 2.0
   */
  private final String sonarRunnerName;

  /**
   * Optional task to run
   * @since 2.1
   */
  private final String task;

  /**
   * @deprecated in 2.0
   */
  @Deprecated
  public SonarRunnerBuilder(String installationName, String project, String properties, String javaOpts) {
    this(installationName, null, project, properties, javaOpts, null);
  }

  /**
   * @deprecated in 2.0
   */
  @Deprecated
  public SonarRunnerBuilder(String installationName, String sonarRunnerName, String project, String properties, String javaOpts) {
    this(installationName, sonarRunnerName, project, properties, javaOpts, null);
  }

  /**
   * @deprecated in 2.1
   */
  @Deprecated
  public SonarRunnerBuilder(String installationName, String sonarRunnerName, String project, String properties, String javaOpts, String jdk) {
    this(installationName, sonarRunnerName, project, properties, javaOpts, jdk, null);
  }

  @DataBoundConstructor
  public SonarRunnerBuilder(String installationName, String sonarRunnerName, String project, String properties, String javaOpts, String jdk, String task) {
    this.installationName = installationName;
    this.sonarRunnerName = sonarRunnerName;
    this.javaOpts = javaOpts;
    this.project = project;
    this.properties = properties;
    this.jdk = jdk;
    this.task = task;
  }

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

  /**
   * @return name of {@link hudson.plugins.sonar.SonarRunnerInstallation}
   */
  public String getSonarRunnerName() {
    return Util.fixNull(sonarRunnerName);
  }

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

  /**
   * @return path to a file with properties for project, never <tt>null</tt>
   */
  public String getProject() {
    return Util.fixNull(project);
  }

  /**
   * @return additional properties, never <tt>null</tt>
   */
  public String getProperties() {
    return Util.fixNull(properties);
  }

  /**
   * @return Java options, never <tt>null</tt>
   */
  public String getJavaOpts() {
    return Util.fixNull(javaOpts);
  }

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

  public String getTask() {
    return task;
  }

  @Override
  public DescriptorImpl getDescriptor() {
    return (DescriptorImpl) super.getDescriptor();
  }

  public SonarRunnerInstallation getSonarRunnerInstallation() {
    for (SonarRunnerInstallation sri : getDescriptor().getSonarRunnerInstallations()) {
      if (sonarRunnerName != null && sonarRunnerName.equals(sri.getName())) {
        return sri;
      }
    }
    // If no installation match then take the first one
    if (getDescriptor().getSonarRunnerInstallations().length > 0) {
      return getDescriptor().getSonarRunnerInstallations()[0];
    }
    return null;
  }

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

    ArgumentListBuilder args = new ArgumentListBuilder();

    EnvVars env = build.getEnvironment(listener);
    env.overrideAll(build.getBuildVariables());

    SonarRunnerInstallation sri = getSonarRunnerInstallation();
    if (sri == null) {
      args.add(launcher.isUnix() ? "sonar-runner" : "sonar-runner.bat");
    } else {
      sri = sri.forNode(Computer.currentComputer().getNode(), listener);
      sri = sri.forEnvironment(env);
      String exe = sri.getExecutable(launcher);
      if (exe == null) {
        Logger.printFailureMessage(listener);
        listener.fatalError(Messages.SonarRunner_ExecutableNotFound(sri.getName()));
        return false;
      }
      args.add(exe);
      env.put("SONAR_RUNNER_HOME", sri.getHome());
    }
    addTaskArgument(args);
    ExtendedArgumentListBuilder argsBuilder = new ExtendedArgumentListBuilder(args, launcher.isUnix());
    if (!populateConfiguration(argsBuilder, build, listener, env, getSonarInstallation())) {
      return false;
    }

    // Java
    computeJdkToUse(build, listener, env);

    // Java options
    env.put("SONAR_RUNNER_OPTS", getJavaOpts());

    long startTime = System.currentTimeMillis();
    int r;
    try {
      r = executeSonarRunner(build, launcher, listener, args, env);
    } catch (IOException e) {
      handleErrors(build, listener, sri, startTime, e);
      r = -1;
    }
    return r == 0;
  }

  private void handleErrors(AbstractBuild<?, ?> build, BuildListener listener, SonarRunnerInstallation sri, long startTime, IOException e) {
    Logger.printFailureMessage(listener);
    Util.displayIOException(e, listener);

    String errorMessage = Messages.SonarRunner_ExecFailed();
    if (sri == null && (System.currentTimeMillis() - startTime) < 1000 && getDescriptor().getSonarRunnerInstallations() == null) {
      // looks like the user didn't configure any Sonar Runner installation
      errorMessage += Messages.SonarRunner_GlobalConfigNeeded();
    }
    e.printStackTrace(listener.fatalError(errorMessage));
    // Badge should be added only once - SONARPLUGINS-1521
    if (build.getAction(BuildSonarAction.class) == null) {
      build.addAction(new BuildSonarAction());
    }
  }

  private int executeSonarRunner(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener, ArgumentListBuilder args, EnvVars env) throws IOException,
      InterruptedException {
    int r = launcher.launch().cmds(args).envs(env).stdout(listener).pwd(build.getModuleRoot()).join();
    if (build.getAction(BuildSonarAction.class) == null) {
      if (r != 0) {
        build.addAction(new BuildSonarAction());
      }
      else {
        String url = SonarUtils.extractSonarProjectURLFromLogs(build);
        build.addAction(new BuildSonarAction(url));
      }
    }
    return r;
  }

  private void computeJdkToUse(AbstractBuild<?, ?> build, BuildListener listener, EnvVars env) throws IOException, InterruptedException {
    JDK jdkToUse = getJdkToUse(build.getProject());
    if (jdkToUse != null) {
      Computer computer = Computer.currentComputer();
      // just in case we are not in a build
      if (computer != null) {
        jdkToUse = jdkToUse.forNode(computer.getNode(), listener);
      }
      jdkToUse.buildEnvVars(env);
    }
  }

  private void addTaskArgument(ArgumentListBuilder args) {
    if (StringUtils.isNotBlank(getTask())) {
      args.add(task);
    }
  }

  public static boolean isSonarInstallationValid(String sonarInstallationName, BuildListener listener) {
    String failureMsg;
    SonarInstallation sonarInstallation = SonarInstallation.get(sonarInstallationName);
    if (sonarInstallation == null) {
      if (StringUtils.isBlank(sonarInstallationName)) {
        failureMsg = Messages.SonarPublisher_NoInstallation(SonarInstallation.all().length);
      }
      else {
        failureMsg = Messages.SonarPublisher_NoMatchInstallation(sonarInstallationName, SonarInstallation.all().length);
      }
      failureMsg += "\n" + Messages.SonarPublisher_FixInstalltionTip();
    } else if (sonarInstallation.isDisabled()) {
      failureMsg = Messages.SonarPublisher_InstallDisabled(sonarInstallation.getName());
    } else {
      failureMsg = null;
    }
    if (failureMsg != null) {
      Logger.printFailureMessage(listener);
      listener.fatalError(failureMsg);
      return false;
    }
    return true;
  }

  @VisibleForTesting
  boolean populateConfiguration(ExtendedArgumentListBuilder args, AbstractBuild<?, ?> build,
      BuildListener listener, EnvVars env, SonarInstallation si) throws IOException, InterruptedException {
    if (si != null) {
      args.append("sonar.jdbc.driver", si.getDatabaseDriver());
      args.append("sonar.jdbc.url", si.getDatabaseUrl());
      args.appendMasked("sonar.jdbc.username", si.getDatabaseLogin());
      args.appendMasked("sonar.jdbc.password", si.getDatabasePassword());
      args.append("sonar.host.url", si.getServerUrl());
      if (StringUtils.isNotBlank(si.getSonarLogin())) {
        args.appendMasked("sonar.login", si.getSonarLogin());
        args.appendMasked("sonar.password", si.getSonarPassword());
      }
    }

    FilePath moduleRoot = build.getModuleRoot();
    args.append("sonar.projectBaseDir", moduleRoot.getRemote());

    // Project properties
    if (StringUtils.isNotBlank(getProject())) {
      String projectSettingsFile = env.expand(getProject());
      FilePath projectSettingsFilePath = build.getModuleRoot().child(projectSettingsFile);
      if (!projectSettingsFilePath.exists()) {
        // because of the poor choice of getModuleRoot() with CVS/Subversion, people often get confused
        // with where the build file path is relative to. Now it's too late to change this behavior
        // due to compatibility issue, but at least we can make this less painful by looking for errors
        // and diagnosing it nicely. See HUDSON-1782

        // first check if this appears to be a valid relative path from workspace root
        FilePath workspace = build.getWorkspace();
        if (workspace == null) {
          listener.fatalError("Project workspace is null");
          return false;
        }
        FilePath projectSettingsFilePath2 = workspace.child(projectSettingsFile);
        if (projectSettingsFilePath2.exists()) {
          // This must be what the user meant. Let it continue.
          projectSettingsFilePath = projectSettingsFilePath2;
        } else {
          // neither file exists. So this now really does look like an error.
          listener.fatalError("Unable to find Sonar project settings at " + projectSettingsFilePath);
          return false;
        }
      }
      args.append("project.settings", projectSettingsFilePath.getRemote());
    }

    // Additional properties
    Properties p = new Properties();
    p.load(new ByteArrayInputStream(env.expand(getProperties()).getBytes()));
    loadProperties(args, p);

    return true;
  }

  private void loadProperties(ExtendedArgumentListBuilder args, Properties p) {
    for (Entry<Object, Object> entry : p.entrySet()) {
      args.append(entry.getKey().toString(), entry.getValue().toString());
    }
  }

  /**
   * @return JDK to be used with this project.
   */
  private JDK getJdkToUse(AbstractProject<?, ?> project) {
    JDK jdkToUse = getJDK();
    if (jdkToUse == null) {
      jdkToUse = project.getJDK();
    }
    return jdkToUse;
  }

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

  @Extension
  public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {

    /**
     * This method is used in UI, so signature and location of this method is important.
     *
     * @return all configured {@link hudson.plugins.sonar.SonarInstallation}
     */
    public SonarInstallation[] getSonarInstallations() {
      return SonarInstallation.all();
    }

    @Override
    public boolean isApplicable(Class<? extends AbstractProject> jobType) {
      return true;
    }

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

    public SonarRunnerInstallation[] getSonarRunnerInstallations() {
      return Hudson.getInstance().getDescriptorByType(SonarRunnerInstallation.DescriptorImpl.class).getInstallations();
    }

  }

}
TOP

Related Classes of hudson.plugins.sonar.SonarRunnerBuilder$DescriptorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.