/*
* 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.scanner.scan;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Languages;
import org.sonar.api.resources.ResourceTypes;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.metric.ScannerMetrics;
import org.sonar.core.platform.ComponentContainer;
import org.sonar.scanner.ProjectAnalysisInfo;
import org.sonar.scanner.analysis.AnalysisProperties;
import org.sonar.scanner.analysis.AnalysisTempFolderProvider;
import org.sonar.scanner.analysis.DefaultAnalysisMode;
import org.sonar.scanner.bootstrap.ExtensionInstaller;
import org.sonar.scanner.bootstrap.ExtensionMatcher;
import org.sonar.scanner.bootstrap.ExtensionUtils;
import org.sonar.scanner.bootstrap.MetricProvider;
import org.sonar.scanner.cpd.CpdExecutor;
import org.sonar.scanner.cpd.index.SonarCpdBlockIndex;
import org.sonar.scanner.deprecated.test.TestPlanBuilder;
import org.sonar.scanner.deprecated.test.TestableBuilder;
import org.sonar.scanner.events.EventBus;
import org.sonar.scanner.index.DefaultIndex;
import org.sonar.scanner.issue.DefaultIssueCallback;
import org.sonar.scanner.issue.DefaultProjectIssues;
import org.sonar.scanner.issue.IssueCache;
import org.sonar.scanner.issue.tracking.DefaultServerLineHashesLoader;
import org.sonar.scanner.issue.tracking.IssueTransition;
import org.sonar.scanner.issue.tracking.LocalIssueTracking;
import org.sonar.scanner.issue.tracking.ServerIssueRepository;
import org.sonar.scanner.issue.tracking.ServerLineHashesLoader;
import org.sonar.scanner.mediumtest.ScanTaskObservers;
import org.sonar.scanner.phases.PhasesTimeProfiler;
import org.sonar.scanner.profiling.PhasesSumUpTimeProfiler;
import org.sonar.scanner.report.ActiveRulesPublisher;
import org.sonar.scanner.report.AnalysisContextReportPublisher;
import org.sonar.scanner.report.ComponentsPublisher;
import org.sonar.scanner.report.ContextPropertiesPublisher;
import org.sonar.scanner.report.CoveragePublisher;
import org.sonar.scanner.report.MeasuresPublisher;
import org.sonar.scanner.report.MetadataPublisher;
import org.sonar.scanner.report.ReportPublisher;
import org.sonar.scanner.report.SourcePublisher;
import org.sonar.scanner.report.TestExecutionAndCoveragePublisher;
import org.sonar.scanner.repository.ContextPropertiesCache;
import org.sonar.scanner.repository.DefaultProjectRepositoriesLoader;
import org.sonar.scanner.repository.DefaultQualityProfileLoader;
import org.sonar.scanner.repository.DefaultServerIssuesLoader;
import org.sonar.scanner.repository.ProjectRepositories;
import org.sonar.scanner.repository.ProjectRepositoriesLoader;
import org.sonar.scanner.repository.ProjectRepositoriesProvider;
import org.sonar.scanner.repository.QualityProfileLoader;
import org.sonar.scanner.repository.QualityProfileProvider;
import org.sonar.scanner.repository.ServerIssuesLoader;
import org.sonar.scanner.repository.language.DefaultLanguagesRepository;
import org.sonar.scanner.rule.ActiveRulesLoader;
import org.sonar.scanner.rule.ActiveRulesProvider;
import org.sonar.scanner.rule.DefaultActiveRulesLoader;
import org.sonar.scanner.rule.DefaultRulesLoader;
import org.sonar.scanner.rule.RulesLoader;
import org.sonar.scanner.rule.RulesProvider;
import org.sonar.scanner.scan.filesystem.BatchIdGenerator;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
import org.sonar.scanner.scan.measure.DefaultMetricFinder;
import org.sonar.scanner.scan.measure.DeprecatedMetricFinder;
import org.sonar.scanner.scan.measure.MeasureCache;
import org.sonar.scanner.storage.Storages;
public class ProjectScanContainer extends ComponentContainer {
private static final Logger LOG = Loggers.get(ProjectScanContainer.class);
private final AnalysisProperties props;
public ProjectScanContainer(ComponentContainer globalContainer, AnalysisProperties props) {
super(globalContainer);
this.props = props;
}
@Override
protected void doBeforeStart() {
addBatchComponents();
ProjectLock lock = getComponentByType(ProjectLock.class);
lock.tryLock();
getComponentByType(WorkDirectoryCleaner.class).execute();
addBatchExtensions();
Settings settings = getComponentByType(Settings.class);
if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) {
add(PhasesSumUpTimeProfiler.class);
}
if (isTherePreviousAnalysis()) {
addIssueTrackingComponents();
}
}
private void addBatchComponents() {
add(
props,
DefaultAnalysisMode.class,
ProjectReactorBuilder.class,
WorkDirectoryCleaner.class,
new MutableProjectReactorProvider(),
new ImmutableProjectReactorProvider(),
ProjectBuildersExecutor.class,
ProjectLock.class,
EventBus.class,
PhasesTimeProfiler.class,
ResourceTypes.class,
ProjectReactorValidator.class,
MetricProvider.class,
ProjectAnalysisInfo.class,
DefaultIndex.class,
Storages.class,
new RulesProvider(),
new ProjectRepositoriesProvider(),
// temp
new AnalysisTempFolderProvider(),
// file system
ModuleIndexer.class,
InputComponentStore.class,
PathResolver.class,
DefaultInputModuleHierarchy.class,
DefaultComponentTree.class,
BatchIdGenerator.class,
// rules
new ActiveRulesProvider(),
new QualityProfileProvider(),
// issues
DefaultIssueCallback.class,
IssueCache.class,
DefaultProjectIssues.class,
IssueTransition.class,
// metrics
DefaultMetricFinder.class,
DeprecatedMetricFinder.class,
// tests
TestPlanBuilder.class,
TestableBuilder.class,
// lang
Languages.class,
DefaultLanguagesRepository.class,
// Measures
MeasureCache.class,
// context
ContextPropertiesCache.class,
ContextPropertiesPublisher.class,
ProjectSettings.class,
// Report
ScannerMetrics.class,
ReportPublisher.class,
AnalysisContextReportPublisher.class,
MetadataPublisher.class,
ActiveRulesPublisher.class,
ComponentsPublisher.class,
MeasuresPublisher.class,
CoveragePublisher.class,
SourcePublisher.class,
TestExecutionAndCoveragePublisher.class,
// Cpd
CpdExecutor.class,
SonarCpdBlockIndex.class,
ScanTaskObservers.class);
addIfMissing(DefaultRulesLoader.class, RulesLoader.class);
addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class);
addIfMissing(DefaultQualityProfileLoader.class, QualityProfileLoader.class);
addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class);
}
private void addIssueTrackingComponents() {
add(
LocalIssueTracking.class,
ServerIssueRepository.class);
addIfMissing(DefaultServerIssuesLoader.class, ServerIssuesLoader.class);
addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class);
}
private boolean isTherePreviousAnalysis() {
return getComponentByType(ProjectRepositories.class).lastAnalysisDate() != null;
}
private void addBatchExtensions() {
getComponentByType(ExtensionInstaller.class).install(this, new BatchExtensionFilter());
}
@Override
protected void doAfterStart() {
DefaultAnalysisMode analysisMode = getComponentByType(DefaultAnalysisMode.class);
InputModuleHierarchy tree = getComponentByType(InputModuleHierarchy.class);
analysisMode.printMode();
LOG.info("Project key: {}", tree.root().key());
String organization = props.property("sonar.organization");
if (StringUtils.isNotEmpty(organization)) {
LOG.info("Organization key: {}", organization);
}
String branch = tree.root().definition().getBranch();
if (branch != null) {
LOG.info("Branch key: {}", branch);
}
LOG.debug("Start recursive analysis of project modules");
scanRecursively(tree, tree.root());
if (analysisMode.isMediumTest()) {
getComponentByType(ScanTaskObservers.class).notifyEndOfScanTask();
}
}
private void scanRecursively(InputModuleHierarchy tree, DefaultInputModule module) {
for (DefaultInputModule child : tree.children(module)) {
scanRecursively(tree, child);
}
scan(module);
}
@VisibleForTesting
void scan(DefaultInputModule module) {
new ModuleScanContainer(this, module).execute();
}
static class BatchExtensionFilter implements ExtensionMatcher {
@Override
public boolean accept(Object extension) {
return ExtensionUtils.isScannerSide(extension)
&& ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH);
}
}
}