/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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.
*/
package org.sonar.server.computation.task.projectanalysis.step;
import com.google.common.base.Optional;
import java.util.Objects;
import java.util.function.Function;
import javax.annotation.CheckForNull;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.component.SnapshotQuery;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolder;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.ComponentRootBuilder;
import org.sonar.server.computation.task.projectanalysis.component.MutableTreeRootHolder;
import org.sonar.server.computation.task.projectanalysis.component.UuidFactory;
import org.sonar.server.computation.task.step.ComputationStep;
import static com.google.common.base.Preconditions.checkState;
import static org.sonar.core.component.ComponentKeys.createKey;
/**
* Populates the {@link MutableTreeRootHolder} and {@link MutableAnalysisMetadataHolder} from the {@link BatchReportReader}
*/
public class BuildComponentTreeStep implements ComputationStep {
private final DbClient dbClient;
private final BatchReportReader reportReader;
private final MutableTreeRootHolder treeRootHolder;
private final MutableAnalysisMetadataHolder analysisMetadataHolder;
public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, MutableTreeRootHolder treeRootHolder, MutableAnalysisMetadataHolder analysisMetadataHolder) {
this.dbClient = dbClient;
this.reportReader = reportReader;
this.treeRootHolder = treeRootHolder;
this.analysisMetadataHolder = analysisMetadataHolder;
}
@Override
public String getDescription() {
return "Build tree of components";
}
@Override
public void execute() {
String branch = analysisMetadataHolder.getBranch();
ScannerReport.Component reportProject = reportReader.readComponent(analysisMetadataHolder.getRootComponentRef());
String projectKey = createKey(reportProject.getKey(), branch);
UuidFactory uuidFactory = new UuidFactory(dbClient, projectKey);
try (DbSession dbSession = dbClient.openSession(false)) {
BaseAnalysisSupplier baseAnalysisSupplier = new BaseAnalysisSupplier(dbClient, dbSession);
ComponentRootBuilder rootBuilder = new ComponentRootBuilder(branch,
uuidFactory::getOrCreateForKey,
reportReader::readComponent,
() -> dbClient.componentDao().selectByKey(dbSession, projectKey),
baseAnalysisSupplier);
Component project = rootBuilder.build(reportProject, projectKey);
treeRootHolder.setRoot(project);
analysisMetadataHolder.setBaseAnalysis(toAnalysis(baseAnalysisSupplier.apply(project.getUuid())));
}
}
/**
* A supplier of the base analysis of the project (if it exists) that will cache the retrieved SnapshotDto and
* implement a sanity check to ensure it is always call with the same UUID value (since it's the project's UUID, it
* is unique for a whole task).
*/
private static final class BaseAnalysisSupplier implements Function<String, Optional<SnapshotDto>> {
private final DbClient dbClient;
private final DbSession dbSession;
private String projectUuid = null;
private Optional<SnapshotDto> cache = null;
private BaseAnalysisSupplier(DbClient dbClient, DbSession dbSession) {
this.dbClient = dbClient;
this.dbSession = dbSession;
}
@Override
public Optional<SnapshotDto> apply(String projectUuid) {
if (this.cache == null) {
this.cache = Optional.fromNullable(
dbClient.snapshotDao().selectAnalysisByQuery(
dbSession,
new SnapshotQuery()
.setComponentUuid(projectUuid)
.setIsLast(true)));
this.projectUuid = projectUuid;
} else {
checkState(
Objects.equals(this.projectUuid, projectUuid),
"BaseAnalysisSupplier called with different project uuid values. First one was %s but current one is %s",
this.projectUuid, projectUuid);
}
return this.cache;
}
}
@CheckForNull
private static Analysis toAnalysis(Optional<SnapshotDto> snapshotDto) {
if (snapshotDto.isPresent()) {
SnapshotDto dto = snapshotDto.get();
return new Analysis.Builder()
.setId(dto.getId())
.setUuid(dto.getUuid())
.setCreatedAt(dto.getCreatedAt())
.build();
}
return null;
}
}