package qubexplorer.runner;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import org.netbeans.api.java.queries.SourceLevelQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.queries.FileEncodingQuery;
import org.sonar.runner.api.ForkedRunner;
import org.sonar.runner.api.PrintStreamConsumer;
import org.sonar.runner.api.ProcessMonitor;
import org.sonar.runner.api.Runner;
import qubexplorer.AuthorizationException;
import qubexplorer.MvnModelInputException;
import qubexplorer.PassEncoder;
import qubexplorer.SonarMvnProject;
import qubexplorer.SonarQubeProjectConfiguration;
import qubexplorer.UserCredentials;
import qubexplorer.server.SonarQube;
import qubexplorer.server.Version;
import qubexplorer.ui.ProjectContext;
/**
*
* @author Victor
*/
public class SonarRunnerProccess {
protected static final String JSON_FILENAME = "sonar-report.json";
public enum AnalysisMode {
INCREMENTAL,
PREVIEW;
}
private final ProjectContext projectContext;
private final String sonarUrl;
private AnalysisMode analysisMode = AnalysisMode.INCREMENTAL;
private PrintStreamConsumer outConsumer;
private PrintStreamConsumer errConsumer;
private WrapperConsumer wrapper;
private List<String> jvmArguments = Collections.emptyList();
private final List<VersionConfig> versionConfigs = Collections.unmodifiableList(Arrays.asList(new VersionConfigLessThan4(), new VersionConfigLessThan5Point2(), new VersionConfigMoreThan5Point2()));
/**
* This state is modified while running.
*/
private String projectHome;
private final Properties properties = new Properties();
public SonarRunnerProccess(String sonarUrl, ProjectContext projectContext) {
this.sonarUrl = sonarUrl;
this.projectContext = projectContext;
}
public PrintStreamConsumer getOutConsumer() {
return outConsumer;
}
public void setOutConsumer(PrintStreamConsumer outConsumer) {
this.outConsumer = outConsumer;
}
public PrintStreamConsumer getErrConsumer() {
return errConsumer;
}
public void setErrConsumer(PrintStreamConsumer errConsumer) {
this.errConsumer = errConsumer;
}
public AnalysisMode getAnalysisMode() {
return analysisMode;
}
public void setAnalysisMode(AnalysisMode analysisMode) {
Objects.requireNonNull(analysisMode, "analysisMode is null");
this.analysisMode = analysisMode;
}
public List<String> getJvmArguments() {
return jvmArguments;
}
public void setJvmArguments(List<String> jvmArguments) {
Objects.requireNonNull(jvmArguments, "argument list is null");
this.jvmArguments = jvmArguments;
}
protected Runner createRunnerForProject(UserCredentials userCredentials, ProcessMonitor processMonitor) throws MvnModelInputException {
ForkedRunner runner = ForkedRunner.create(processMonitor);
projectHome = projectContext.getProject().getProjectDirectory().getPath();
configureProperties(userCredentials);
runner.addProperties(properties);
if (outConsumer != null) {
runner.setStdOut(outConsumer);
}
wrapper = new WrapperConsumer(errConsumer);
runner.setStdErr(wrapper);
runner.addJvmArguments(jvmArguments);
return runner;
}
private void configureProperties(UserCredentials userCredentials) throws MvnModelInputException {
SonarQubeProjectConfiguration projectConfiguration = projectContext.getConfiguration();
Version sonarQubeVersion = new SonarQube(sonarUrl).getVersion(userCredentials);
Module mainModule = Module.createMainModule(projectContext);
//mainModule.loadExternalProperties(properties);
properties.setProperty("sonar.projectBaseDir", projectHome);
properties.setProperty("sonar.host.url", sonarUrl);
properties.setProperty("sonar.projectDir", projectHome);
properties.setProperty("project.home", projectHome);
// properties.setProperty("sonar.projectName", projectConfiguration.getName());
properties.setProperty("sonar.projectVersion", projectConfiguration.getVersion());
properties.setProperty("sonar.sourceEncoding", FileEncodingQuery.getEncoding(projectContext.getProject().getProjectDirectory()).displayName());
properties.setProperty("sonar.working.directory", getWorkingDirectory());
//optional properties
if (userCredentials != null) {
properties.setProperty("sonar.login", userCredentials.getUsername());
properties.setProperty("sonar.password", PassEncoder.decodeAsString(userCredentials.getPassword()));
}
String sourceLevel = SourceLevelQuery.getSourceLevel(projectContext.getProject().getProjectDirectory());
if (sourceLevel != null) {
properties.setProperty("sonar.java.source", sourceLevel);
}
if (SonarMvnProject.isMvnProject(projectContext.getProject())) {
properties.setProperty("sonar.junit.reportsPath", new File(SonarMvnProject.getOutputDirectory(projectContext.getProject()), "/surefire-reports").getAbsolutePath());
}
//end optional
for (VersionConfig versionConfig : getVersionConfigsFor(sonarQubeVersion)) {
versionConfig.apply(this, properties);
}
mainModule.addModuleProperties(sonarQubeVersion, properties);
// mainModule.configureSourcesAndBinariesProperties(sonarQubeVersion, properties);
ModulesConfigurationResult result = configureModulesProperties(mainModule, sonarQubeVersion);
if (!result.hasModulesWithSources() && !mainModule.containsSources()) {
throw new SourcesNotFoundException();
}
properties.setProperty("sonar.projectKey", result.hasSubmodules() ? projectConfiguration.getKey().getPart(0) : projectConfiguration.getKey().toString());
}
private List<VersionConfig> getVersionConfigsFor(Version sonarQubeVersion) {
List<VersionConfig> list = new LinkedList<>();
for (VersionConfig config : versionConfigs) {
if (config.applies(sonarQubeVersion)) {
list.add(config);
}
}
return list;
}
public String getWorkingDirectory() throws MvnModelInputException {
String workingDirectory;
if (SonarMvnProject.isMvnProject(projectContext.getProject())) {
workingDirectory = new File(SonarMvnProject.getOutputDirectory(projectContext.getProject()), "sonar").getAbsolutePath();
} else {
workingDirectory = projectHome + "/./.sonar";
}
return workingDirectory;
}
private ModulesConfigurationResult configureModulesProperties(Module mainModule, Version sonarQubeVersion) throws MvnModelInputException {
int sourcesCounter = 0;
StringBuilder modulesWithSources = new StringBuilder();
Set<Project> subprojects = getSubprojects();
for (Project subproject : subprojects) {
Module module = mainModule.createSubmodule(subproject);
module.addModuleProperties(sonarQubeVersion, properties);
if (module.containsSources()) {
if (modulesWithSources.length() > 0) {
modulesWithSources.append(',');
}
modulesWithSources.append(module.getName());
sourcesCounter++;
}
}
if (modulesWithSources.length() > 0) {
properties.setProperty("sonar.modules", modulesWithSources.toString());
}
return new ModulesConfigurationResult(!subprojects.isEmpty(), sourcesCounter > 0);
}
public Set<Project> getSubprojects() {
Set<Project> subprojects = ProjectUtils.getContainedProjects(projectContext.getProject(), true);
if (subprojects == null) {
subprojects = Collections.emptySet();
}
return subprojects;
}
public SonarRunnerResult executeRunner(UserCredentials credentials, ProcessMonitor processMonitor) throws MvnModelInputException {
Runner runner = createRunnerForProject(credentials, processMonitor);
try {
runner.execute();
} catch (Exception ex) {
if (wrapper.isUnauthorized()) {
throw new AuthorizationException();
} else {
throw new SonarRunnerException(ex);
}
}
if (processMonitor.stop()) {
throw new SonarRunnerCancelledException();
} else {
File jsonFile = new File(properties.getProperty("sonar.working.directory"), JSON_FILENAME);
if (!jsonFile.exists()) {
throw new SonarRunnerException("No result file");
} else {
return new SonarRunnerResult(jsonFile);
}
}
}
private static class WrapperConsumer extends PrintStreamConsumer {
private boolean unauthorized;
private final PrintStreamConsumer wrapee;
public WrapperConsumer(PrintStreamConsumer consumer) {
super(null);
this.wrapee = consumer;
}
public boolean isUnauthorized() {
return unauthorized;
}
@Override
public void consumeLine(String line) {
if (line.toLowerCase().contains("not authorized")) {
unauthorized = true;
}
if (wrapee != null) {
wrapee.consumeLine(line);
}
}
}
private static class ModulesConfigurationResult {
private final boolean hasModules;
private final boolean hasModulesWithSources;
public ModulesConfigurationResult(boolean hasModules, boolean hasModulesWithSources) {
this.hasModules = hasModules;
this.hasModulesWithSources = hasModulesWithSources;
}
public boolean hasSubmodules() {
return hasModules;
}
public boolean hasModulesWithSources() {
return hasModulesWithSources;
}
}
}