/** * * Copyright (C) norad.fr * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package fr.norad.visuwall.plugin.sonar; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import fr.norad.visuwall.providers.sonar.Sonar; import fr.norad.visuwall.providers.sonar.domain.SonarQualityMetric; import fr.norad.visuwall.providers.sonar.exception.SonarMeasureNotFoundException; import fr.norad.visuwall.providers.sonar.exception.SonarMetricsNotFoundException; import fr.norad.visuwall.providers.sonar.exception.SonarProjectNotFoundException; import fr.norad.visuwall.providers.sonar.exception.SonarProjectsNotFoundException; import fr.norad.visuwall.providers.sonar.exception.SonarResourceNotFoundException; import fr.norad.visuwall.providers.sonar.resource.Project; import fr.norad.visuwall.api.domain.BuildState; import fr.norad.visuwall.api.domain.BuildTime; import fr.norad.visuwall.api.domain.Commiter; import fr.norad.visuwall.api.domain.ProjectKey; import fr.norad.visuwall.api.domain.SoftwareProjectId; import fr.norad.visuwall.api.domain.TestResult; import fr.norad.visuwall.api.domain.quality.QualityMeasure; import fr.norad.visuwall.api.domain.quality.QualityMetric; import fr.norad.visuwall.api.domain.quality.QualityResult; import fr.norad.visuwall.api.exception.BuildIdNotFoundException; import fr.norad.visuwall.api.exception.BuildNotFoundException; import fr.norad.visuwall.api.exception.MavenIdNotFoundException; import fr.norad.visuwall.api.exception.ProjectNotFoundException; import fr.norad.visuwall.api.plugin.capability.MetricCapability; import fr.norad.visuwall.api.plugin.capability.TestCapability; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.wsclient.services.Measure; import org.sonar.wsclient.services.Resource; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Strings; public class SonarConnection implements MetricCapability, TestCapability { private static final Logger LOG = LoggerFactory.getLogger(SonarConnection.class); private final UUID id = UUID.randomUUID(); @VisibleForTesting Sonar sonarClient; private Map<String, QualityMetric> metricsMap; private String[] metricKeys = new String[] {}; private boolean connected; private String url; public SonarConnection() { } @Override public void connect(String url, String login, String password) { this.url = url; if (LOG.isInfoEnabled()) { LOG.info("Initialize sonar with url " + url); } if (sonarClient == null) { sonarClient = new Sonar(url, login, password); } connected = true; } @Override public Map<String, List<QualityMetric>> getMetricsByCategory() { checkConnected(); if (metricsMap == null) { initializeMetrics(); } Map<String, List<QualityMetric>> metricsByDomain = new HashMap<String, List<QualityMetric>>(); for (QualityMetric metricValue : metricsMap.values()) { String domain = metricValue.getDomain(); List<QualityMetric> domainMetrics = metricsByDomain.get(domain); if (domainMetrics == null) { domainMetrics = new ArrayList<QualityMetric>(); metricsByDomain.put(domain, domainMetrics); } domainMetrics.add(metricValue); } return metricsByDomain; } @Override public Map<SoftwareProjectId, String> listSoftwareProjectIds() { checkConnected(); Map<SoftwareProjectId, String> projects = new HashMap<SoftwareProjectId, String>(); try { List<Project> names = sonarClient.findProjects().getProjects(); for (Project project : names) { String key = project.getKey(); projects.put(new SoftwareProjectId(key), project.getName()); } } catch (SonarProjectsNotFoundException e) { LOG.warn(e.getMessage(), e); } return projects; } @Override public void close() { connected = false; } @Override public boolean equals(Object o) { if (o instanceof SonarConnection) { SonarConnection s = (SonarConnection) o; return id.equals(s.id); } return false; } @Override public int hashCode() { return Objects.hashCode(id); } @Override public String getDescription(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { String artifactId = softwareProjectId.getProjectId(); Resource resource = sonarClient.findResource(artifactId); return resource.getName(true); } catch (SonarResourceNotFoundException e) { throw new ProjectNotFoundException("Can't get description of software project id: " + softwareProjectId, e); } } @Override public SoftwareProjectId identify(ProjectKey projectKey) throws ProjectNotFoundException { checkConnected(); Preconditions.checkNotNull(projectKey, "projectKey is mandatory"); try { String mavenId = projectKey.getMavenId(); if (mavenId != null) { Resource resource = sonarClient.findResource(mavenId); SoftwareProjectId softwareProjectId = new SoftwareProjectId(resource.getKey()); return softwareProjectId; } } catch (SonarResourceNotFoundException e) { throw new ProjectNotFoundException("Can't identify project key: " + projectKey, e); } throw new ProjectNotFoundException("Can't identify project key, there is not enough informations: " + projectKey); } @Override public TestResult analyzeUnitTests(SoftwareProjectId projectId) { checkConnected(); checkSoftwareProjectId(projectId); TestResult unitTestResult = new TestResult(); String artifactId = projectId.getProjectId(); if (Strings.isNullOrEmpty(artifactId)) { if (LOG.isDebugEnabled()) { LOG.debug("can't analyze project " + projectId + " without artifactId. Is it a maven project ?"); } } else { unitTestResult = createUnitTestAnalysis(artifactId); } return unitTestResult; } @Override public TestResult analyzeIntegrationTests(SoftwareProjectId projectId) { checkConnected(); checkSoftwareProjectId(projectId); TestResult integrationTestResult = new TestResult(); try { String artifactId = projectId.getProjectId(); if (Strings.isNullOrEmpty(artifactId)) { if (LOG.isDebugEnabled()) { LOG.debug("can't analyze project " + projectId + " without artifactId. Is it a maven project ?"); } } else { Measure itCoverageMeasure = sonarClient.findMeasure(artifactId, "it_coverage"); if (itCoverageMeasure != null) { Double itCoverage = itCoverageMeasure.getValue(); integrationTestResult.setCoverage(itCoverage); } } } catch (SonarMeasureNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug("Integration tests informations are not available for project " + projectId + ", cause " + e.getMessage()); } } return integrationTestResult; } @Override public QualityResult analyzeQuality(SoftwareProjectId projectId, String... metrics) { checkConnected(); checkSoftwareProjectId(projectId); if (metricsMap == null) { initializeMetrics(); } if (metrics.length == 0) { metrics = metricKeys; } QualityResult qualityResult = new QualityResult(); String artifactId = projectId.getProjectId(); if (!Strings.isNullOrEmpty(artifactId)) { for (String key : metrics) { addQualityMeasure(qualityResult, artifactId, key); } } return qualityResult; } private void addQualityMeasure(QualityResult qualityResult, String artifactId, String key) { try { Measure measure = sonarClient.findMeasure(artifactId, key); if (measure != null && measure.getValue() != null) { SonarQualityMeasure qualityMeasure = QualityMeasures.asQualityMeasure(measure, key); qualityMeasure.setName(metricsMap.get(key).getName()); qualityResult.add(key, asQualityMeasure(qualityMeasure)); } } catch (SonarMeasureNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getMessage()); } } } private QualityMeasure asQualityMeasure(SonarQualityMeasure sonarQualityMeasure) { QualityMeasure qualityMeasure = new QualityMeasure(); qualityMeasure.setFormattedValue(sonarQualityMeasure.getFormattedValue()); qualityMeasure.setKey(sonarQualityMeasure.getKey()); qualityMeasure.setName(sonarQualityMeasure.getName()); qualityMeasure.setValue(sonarQualityMeasure.getValue()); return qualityMeasure; } @Override public String getMavenId(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException, MavenIdNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { String artifactId = softwareProjectId.getProjectId(); Resource resource = sonarClient.findResource(artifactId); return resource.getKey(); } catch (SonarResourceNotFoundException e) { throw new MavenIdNotFoundException("Can't get maven id of software project id: " + softwareProjectId, e); } } @Override public String getName(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { String artifactId = softwareProjectId.getProjectId(); Resource resource = sonarClient.findResource(artifactId); return resource.getName(); } catch (SonarResourceNotFoundException e) { throw new ProjectNotFoundException("Can't get name of software project id: " + softwareProjectId, e); } } @Override public boolean isClosed() { return !connected; } @Override public boolean isProjectDisabled(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { String artifactId = softwareProjectId.getProjectId(); sonarClient.findProject(artifactId); } catch (SonarProjectNotFoundException e) { throw new ProjectNotFoundException("Can't find if project is disabled, softwareProjectId:" + softwareProjectId + ", url: " + url); } return false; } private TestResult createUnitTestAnalysis(String artifactId) { TestResult unitTestResult = new TestResult(); try { Double coverage = sonarClient.findMeasure(artifactId, "coverage").getValue(); Double failures = sonarClient.findMeasure(artifactId, "test_failures").getValue(); Double errors = sonarClient.findMeasure(artifactId, "test_errors").getValue(); Double passTests = sonarClient.findMeasure(artifactId, "tests").getValue(); int skipCount = sonarClient.findMeasure(artifactId, "skipped_tests").getIntValue(); int failCount = failures.intValue() + errors.intValue(); int passCount = passTests.intValue() - failCount; unitTestResult.setCoverage(coverage); unitTestResult.setFailCount(failCount); unitTestResult.setSkipCount(skipCount); unitTestResult.setPassCount(passCount); } catch (SonarMeasureNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug("Unit tests informations are not available for project with artifactId : " + artifactId + ", cause " + e.getMessage()); } } return unitTestResult; } private void initializeMetrics() { try { metricsMap = asMetrics(sonarClient.findMetrics()); Set<String> metricKeysSet = metricsMap.keySet(); int countMetricKeys = metricKeysSet.size(); metricKeys = metricKeysSet.toArray(new String[countMetricKeys]); } catch (SonarMetricsNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug("Can't initialize metrics", e); } } } private Map<String, QualityMetric> asMetrics(Map<String, SonarQualityMetric> sonarMetrics) { Map<String, QualityMetric> metrics = new HashMap<String, QualityMetric>(); for (Map.Entry<String, SonarQualityMetric> metric : sonarMetrics.entrySet()) { String key = metric.getKey(); SonarQualityMetric value = metric.getValue(); QualityMetric qualityMetric = asQualityMetric(value); metrics.put(key, qualityMetric); } return metrics; } private QualityMetric asQualityMetric(SonarQualityMetric sonarQualityMetric) { QualityMetric qualityMetric = new QualityMetric(); qualityMetric.setDescription(sonarQualityMetric.getDescription()); qualityMetric.setDirection(sonarQualityMetric.getDirection()); qualityMetric.setDomain(sonarQualityMetric.getDomain()); qualityMetric.setHidden(sonarQualityMetric.getHidden()); qualityMetric.setKey(sonarQualityMetric.getKey()); qualityMetric.setName(sonarQualityMetric.getName()); qualityMetric.setQualitative(sonarQualityMetric.getQualitative()); qualityMetric.setUserManaged(sonarQualityMetric.getUserManaged()); qualityMetric.setValTyp(sonarQualityMetric.getValTyp()); return qualityMetric; } public BuildTime getBuildTime(SoftwareProjectId softwareProjectId, Integer buildId) throws BuildNotFoundException, ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); BuildTime buildTime = new BuildTime(); buildTime.setDuration(1); buildTime.setStartTime(new Date()); return buildTime; } @Deprecated // NOT USED public List<Integer> getBuildIds(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); return Arrays.asList(1); } @Deprecated // NOT USED public BuildState getBuildState(SoftwareProjectId softwareProjectId, Integer buildId) throws ProjectNotFoundException, BuildNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); return BuildState.SUCCESS; } @Deprecated // NOT USED public Date getEstimatedFinishTime(SoftwareProjectId softwareProjectId, Integer buildId) throws ProjectNotFoundException, BuildNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); return new Date(); } @Deprecated // NOT USED public boolean isBuilding(SoftwareProjectId softwareProjectId, Integer buildId) throws ProjectNotFoundException, BuildNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); return false; } @Deprecated // NOT USED public int getLastBuildId(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException, BuildIdNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); return 1; } @Deprecated // NOT USED public List<Commiter> getBuildCommiters(SoftwareProjectId softwareProjectId, Integer buildId) throws BuildNotFoundException, ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); checkBuildId(buildId); return new ArrayList<Commiter>(); } private void checkBuildId(int buildId) { Preconditions.checkNotNull(buildId, "buildId is mandatory"); } private void checkSoftwareProjectId(SoftwareProjectId softwareProjectId) { Preconditions.checkNotNull(softwareProjectId, "softwareProjectId is mandatory"); } private void checkConnected() { Preconditions.checkState(connected, "You must connect your plugin"); } }