diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2017-01-17 17:45:09 +0100 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2017-01-27 16:26:30 +0100 |
commit | eea589c564a924993e8edba9d8fa9691e756bce4 (patch) | |
tree | c112f6d41d7f4dfeb7ad8d14f895e1b879a68411 /sonar-scanner-engine/src/main/java | |
parent | 211a993bd85b5d12ace1686b133677381da8c597 (diff) | |
download | sonarqube-eea589c564a924993e8edba9d8fa9691e756bce4.tar.gz sonarqube-eea589c564a924993e8edba9d8fa9691e756bce4.zip |
Refactor resources API
Diffstat (limited to 'sonar-scanner-engine/src/main/java')
58 files changed, 922 insertions, 1164 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultProjectTree.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultProjectTree.java deleted file mode 100644 index c412cd15ead..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultProjectTree.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import org.apache.commons.lang.ObjectUtils; -import org.picocontainer.Startable; -import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.api.resources.Project; -import org.sonar.scanner.scan.ImmutableProjectReactor; - -public class DefaultProjectTree implements Startable { - - private final ProjectConfigurator configurator; - private final ImmutableProjectReactor projectReactor; - - private List<Project> projects; - private Map<ProjectDefinition, Project> projectsByDef; - - public DefaultProjectTree(ImmutableProjectReactor projectReactor, ProjectConfigurator projectConfigurator) { - this.projectReactor = projectReactor; - this.configurator = projectConfigurator; - } - - @Override - public void start() { - doStart(projectReactor.getProjects()); - } - - @Override - public void stop() { - // Nothing to do - } - - void doStart(Collection<ProjectDefinition> definitions) { - projects = Lists.newArrayList(); - projectsByDef = Maps.newHashMap(); - - for (ProjectDefinition def : definitions) { - Project project = configurator.create(def); - projectsByDef.put(def, project); - projects.add(project); - } - - for (Map.Entry<ProjectDefinition, Project> entry : projectsByDef.entrySet()) { - ProjectDefinition def = entry.getKey(); - Project project = entry.getValue(); - for (ProjectDefinition module : def.getSubProjects()) { - projectsByDef.get(module).setParent(project); - } - } - - // Configure - for (Project project : projects) { - configurator.configure(project); - } - } - - public List<Project> getProjects() { - return projects; - } - - public Project getRootProject() { - for (Project project : projects) { - if (project.getParent() == null) { - return project; - } - } - throw new IllegalStateException("Can not find the root project from the list of Maven modules"); - } - - public ProjectDefinition getProjectDefinition(Project project) { - for (Map.Entry<ProjectDefinition, Project> entry : projectsByDef.entrySet()) { - if (ObjectUtils.equals(entry.getValue(), project)) { - return entry.getKey(); - } - } - throw new IllegalStateException("Can not find ProjectDefinition for " + project); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectConfigurator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java index 16ef6131a23..3441db14cef 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectConfigurator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java @@ -20,12 +20,10 @@ package org.sonar.scanner; import java.util.Date; -import org.apache.commons.lang.StringUtils; +import org.picocontainer.Startable; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; import org.sonar.api.utils.SonarException; import org.sonar.api.utils.System2; @@ -34,29 +32,24 @@ import org.sonar.api.utils.System2; * */ @ScannerSide -public class ProjectConfigurator { - +public class ProjectAnalysisInfo implements Startable { private final System2 system2; private Settings settings; - public ProjectConfigurator(Settings settings, System2 system2) { + private Date analysisDate; + private String analysisVersion; + + public ProjectAnalysisInfo(Settings settings, System2 system2) { this.settings = settings; this.system2 = system2; } - public Project create(ProjectDefinition definition) { - Project project = new Project(definition.getKey(), definition.getBranch(), definition.getName()); - project.setDescription(StringUtils.defaultString(definition.getDescription())); - project.setOriginalName(definition.getOriginalName()); - return project; + public Date analysisDate() { + return analysisDate; } - public ProjectConfigurator configure(Project project) { - Date analysisDate = loadAnalysisDate(); - project - .setAnalysisDate(analysisDate) - .setAnalysisVersion(loadAnalysisVersion()); - return this; + public String analysisVersion() { + return analysisVersion; } private Date loadAnalysisDate() { @@ -78,4 +71,15 @@ public class ProjectConfigurator { private String loadAnalysisVersion() { return settings.getString(CoreProperties.PROJECT_VERSION_PROPERTY); } + + @Override + public void start() { + this.analysisDate = loadAnalysisDate(); + this.analysisVersion = loadAnalysisVersion(); + } + + @Override + public void stop() { + // nothing to do + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java index 1ffe0400337..1fbeed00db8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java @@ -36,6 +36,7 @@ import org.sonar.api.batch.CheckProject; import org.sonar.api.batch.DependedUpon; import org.sonar.api.batch.DependsUpon; import org.sonar.api.batch.Phase; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.postjob.PostJob; import org.sonar.api.batch.postjob.PostJobContext; import org.sonar.api.batch.sensor.Sensor; @@ -61,7 +62,8 @@ public class ScannerExtensionDictionnary { private final PostJobContext postJobContext; private final PostJobOptimizer postJobOptimizer; - public ScannerExtensionDictionnary(ComponentContainer componentContainer, DefaultSensorContext sensorContext, SensorOptimizer sensorOptimizer, PostJobContext postJobContext, + public ScannerExtensionDictionnary(ComponentContainer componentContainer, DefaultSensorContext sensorContext, + SensorOptimizer sensorOptimizer, PostJobContext postJobContext, PostJobOptimizer postJobOptimizer) { this.componentContainer = componentContainer; this.sensorContext = sensorContext; @@ -70,8 +72,8 @@ public class ScannerExtensionDictionnary { this.postJobOptimizer = postJobOptimizer; } - public <T> Collection<T> select(Class<T> type, @Nullable Project project, boolean sort, @Nullable ExtensionMatcher matcher) { - List<T> result = getFilteredExtensions(type, project, matcher); + public <T> Collection<T> select(Class<T> type, @Nullable DefaultInputModule module, boolean sort, @Nullable ExtensionMatcher matcher) { + List<T> result = getFilteredExtensions(type, module, matcher); if (sort) { return sort(result); } @@ -92,13 +94,13 @@ public class ScannerExtensionDictionnary { return Phase.Name.DEFAULT; } - private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable Project project, @Nullable ExtensionMatcher matcher) { + private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable DefaultInputModule module, @Nullable ExtensionMatcher matcher) { List<T> result = Lists.newArrayList(); for (Object extension : getExtensions(type)) { if (org.sonar.api.batch.Sensor.class.equals(type) && extension instanceof Sensor) { extension = new SensorWrapper((Sensor) extension, sensorContext, sensorOptimizer); } - if (shouldKeep(type, extension, project, matcher)) { + if (shouldKeep(type, extension, module, matcher)) { result.add((T) extension); } } @@ -106,7 +108,7 @@ public class ScannerExtensionDictionnary { // Retrieve new Sensors and wrap then in SensorWrapper for (Object extension : getExtensions(Sensor.class)) { extension = new SensorWrapper((Sensor) extension, sensorContext, sensorOptimizer); - if (shouldKeep(type, extension, project, matcher)) { + if (shouldKeep(type, extension, module, matcher)) { result.add((T) extension); } } @@ -115,7 +117,7 @@ public class ScannerExtensionDictionnary { // Retrieve new PostJob and wrap then in PostJobWrapper for (Object extension : getExtensions(PostJob.class)) { extension = new PostJobWrapper((PostJob) extension, postJobContext, postJobOptimizer); - if (shouldKeep(type, extension, project, matcher)) { + if (shouldKeep(type, extension, module, matcher)) { result.add((T) extension); } } @@ -253,12 +255,12 @@ public class ScannerExtensionDictionnary { } } - private static boolean shouldKeep(Class type, Object extension, @Nullable Project project, @Nullable ExtensionMatcher matcher) { + private boolean shouldKeep(Class type, Object extension, @Nullable DefaultInputModule module, @Nullable ExtensionMatcher matcher) { boolean keep = (ClassUtils.isAssignable(extension.getClass(), type) || (org.sonar.api.batch.Sensor.class.equals(type) && ClassUtils.isAssignable(extension.getClass(), Sensor.class))) && (matcher == null || matcher.accept(extension)); - if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) { - keep = ((CheckProject) extension).shouldExecuteOnProject(project); + if (keep && module != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) { + keep = ((CheckProject) extension).shouldExecuteOnProject(new Project(module.definition())); } return keep; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java index db8ba5417cb..451d0a92642 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java @@ -30,7 +30,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; + +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.config.Settings; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -40,12 +43,11 @@ import org.sonar.duplications.index.CloneGroup; import org.sonar.duplications.index.ClonePart; import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Duplicate; import org.sonar.scanner.protocol.output.ScannerReport.Duplication; import org.sonar.scanner.report.ReportPublisher; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.util.ProgressReport; import static com.google.common.collect.FluentIterable.from; @@ -64,17 +66,17 @@ public class CpdExecutor { private final SonarCpdBlockIndex index; private final ReportPublisher publisher; - private final BatchComponentCache batchComponentCache; + private final InputComponentStore componentStore; private final Settings settings; private final ProgressReport progressReport; private int count; private int total; - public CpdExecutor(Settings settings, SonarCpdBlockIndex index, ReportPublisher publisher, BatchComponentCache batchComponentCache) { + public CpdExecutor(Settings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) { this.settings = settings; this.index = index; this.publisher = publisher; - this.batchComponentCache = batchComponentCache; + this.componentStore = inputComponentCache; this.progressReport = new ProgressReport("CPD computation", TimeUnit.SECONDS.toMillis(10)); } @@ -106,13 +108,13 @@ public class CpdExecutor { @VisibleForTesting void runCpdAnalysis(ExecutorService executorService, String componentKey, final Collection<Block> fileBlocks, long timeout) { - BatchComponent component = batchComponentCache.get(componentKey); + DefaultInputComponent component = (DefaultInputComponent) componentStore.getByKey(componentKey); if (component == null) { LOG.error("Resource not found in component cache: {}. Skipping CPD computation for it", componentKey); return; } - InputFile inputFile = (InputFile) component.inputComponent(); + InputFile inputFile = (InputFile) component; LOG.debug("Detection of duplications for {}", inputFile.absolutePath()); progressReport.message(String.format("%d/%d - current file: %s", count, total, inputFile.absolutePath())); @@ -156,9 +158,9 @@ public class CpdExecutor { } @VisibleForTesting - final void saveDuplications(final BatchComponent component, List<CloneGroup> duplications) { + final void saveDuplications(final DefaultInputComponent component, List<CloneGroup> duplications) { if (duplications.size() > MAX_CLONE_GROUP_PER_FILE) { - LOG.warn("Too many duplication groups on file " + component.inputComponent() + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + + LOG.warn("Too many duplication groups on file " + component + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + " groups."); } Iterable<ScannerReport.Duplication> reportDuplications = from(duplications) @@ -177,7 +179,7 @@ public class CpdExecutor { publisher.getWriter().writeComponentDuplications(component.batchId(), reportDuplications); } - private Duplication toReportDuplication(BatchComponent component, Duplication.Builder dupBuilder, Duplicate.Builder blockBuilder, CloneGroup input) { + private Duplication toReportDuplication(InputComponent component, Duplication.Builder dupBuilder, Duplicate.Builder blockBuilder, CloneGroup input) { dupBuilder.clear(); ClonePart originBlock = input.getOriginPart(); blockBuilder.clear(); @@ -190,7 +192,7 @@ public class CpdExecutor { if (!duplicate.equals(originBlock)) { clonePartCount++; if (clonePartCount > MAX_CLONE_PART_PER_GROUP) { - LOG.warn("Too many duplication references on file " + component.inputComponent() + " for block at line " + + LOG.warn("Too many duplication references on file " + component + " for block at line " + originBlock.getStartLine() + ". Keep only the first " + MAX_CLONE_PART_PER_GROUP + " references."); break; @@ -198,7 +200,7 @@ public class CpdExecutor { blockBuilder.clear(); String componentKey = duplicate.getResourceId(); if (!component.key().equals(componentKey)) { - BatchComponent sameProjectComponent = batchComponentCache.get(componentKey); + DefaultInputComponent sameProjectComponent = (DefaultInputComponent) componentStore.getByKey(componentKey); blockBuilder.setOtherFileRef(sameProjectComponent.batchId()); } dupBuilder.addDuplicate(blockBuilder diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java index 619a06e4c20..3e701877e4f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; @@ -34,7 +35,6 @@ import org.sonar.duplications.index.AbstractCloneIndex; import org.sonar.duplications.index.CloneIndex; import org.sonar.duplications.index.PackedMemoryCloneIndex; import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.FileStructure; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.report.ReportPublisher; @@ -43,20 +43,18 @@ public class SonarCpdBlockIndex extends AbstractCloneIndex { private final CloneIndex mem = new PackedMemoryCloneIndex(); private final ReportPublisher publisher; - private final BatchComponentCache batchComponentCache; private final Settings settings; // Files already tokenized private final Set<InputFile> indexedFiles = new HashSet<>(); - public SonarCpdBlockIndex(ReportPublisher publisher, BatchComponentCache batchComponentCache, Settings settings) { + public SonarCpdBlockIndex(ReportPublisher publisher, Settings settings) { this.publisher = publisher; - this.batchComponentCache = batchComponentCache; this.settings = settings; } public void insert(InputFile inputFile, Collection<Block> blocks) { if (isCrossProjectDuplicationEnabled(settings)) { - int id = batchComponentCache.get(inputFile).batchId(); + int id = ((DefaultInputFile) inputFile).batchId(); if (publisher.getWriter().hasComponentData(FileStructure.Domain.CPD_TEXT_BLOCKS, id)) { throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + inputFile.absolutePath()); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java index 4b19f7d70d2..1908cf638b2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java @@ -26,7 +26,6 @@ import org.sonar.api.SonarRuntime; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.InputPath; @@ -37,86 +36,83 @@ import org.sonar.api.design.Dependency; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.Metric; -import org.sonar.api.resources.Directory; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.core.component.ComponentKeys; import org.sonar.scanner.index.DefaultIndex; import org.sonar.scanner.sensor.DefaultSensorContext; public class DeprecatedSensorContext extends DefaultSensorContext implements SensorContext { - private final DefaultIndex index; - private final Project project; + private final InputModule module; - public DeprecatedSensorContext(InputModule module, DefaultIndex index, Project project, Settings settings, FileSystem fs, ActiveRules activeRules, + public DeprecatedSensorContext(InputModule module, DefaultIndex index, Settings settings, FileSystem fs, ActiveRules activeRules, AnalysisMode analysisMode, SensorStorage sensorStorage, SonarRuntime sonarRuntime) { super(module, settings, fs, activeRules, analysisMode, sensorStorage, sonarRuntime); this.index = index; - this.project = project; - - } - - public Project getProject() { - return project; + this.module = module; } @Override public Resource getParent(Resource reference) { - return index.getParent(reference); + return index.getParent(reference.getEffectiveKey()); } @Override public Collection<Resource> getChildren(Resource reference) { - return index.getChildren(reference); + return index.getChildren(reference.getEffectiveKey()); } @Override public <G extends Serializable> Measure<G> getMeasure(Metric<G> metric) { - return index.getMeasure(project, metric); + return index.getMeasure(module.key(), metric); + } + + private String getEffectiveKey(Resource r) { + if (r.getEffectiveKey() != null) { + return r.getEffectiveKey(); + } + + if (ResourceUtils.isProject(r) || /* For technical projects */ResourceUtils.isRootProject(r)) { + return r.getKey(); + } else { + return ComponentKeys.createEffectiveKey(module.key(), r); + } } @Override public <M> M getMeasures(MeasuresFilter<M> filter) { - return index.getMeasures(project, filter); + return index.getMeasures(module.key(), filter); } @Override public Measure saveMeasure(Measure measure) { - return index.addMeasure(project, measure); + return index.addMeasure(module.key(), measure); } @Override public Measure saveMeasure(Metric metric, Double value) { - return index.addMeasure(project, new Measure(metric, value)); + return index.addMeasure(module.key(), new Measure(metric, value)); } @Override public <G extends Serializable> Measure<G> getMeasure(Resource resource, Metric<G> metric) { - return index.getMeasure(resource, metric); + return index.getMeasure(resource.getEffectiveKey(), metric); } @Override public String saveResource(Resource resource) { - Resource persistedResource = index.addResource(resource); - if (persistedResource != null) { - return persistedResource.getEffectiveKey(); - } - return null; - } - - public boolean saveResource(Resource resource, Resource parentReference) { - return index.index(resource, parentReference); + throw new UnsupportedOperationException("No longer possible to save resources"); } @Override public Resource getResource(Resource resource) { - return index.getResource(resource); + return index.getResource(getEffectiveKey(resource)); } @Override public <M> M getMeasures(Resource resource, MeasuresFilter<M> filter) { - return index.getMeasures(resource, filter); + return index.getMeasures(getEffectiveKey(resource), filter); } @Override @@ -126,9 +122,9 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen } @Override - public Measure saveMeasure(Resource resource, Measure measure) { + public Measure saveMeasure(@Nullable Resource resource, Measure measure) { Resource resourceOrProject = resourceOrProject(resource); - return index.addMeasure(resourceOrProject, measure); + return index.addMeasure(getEffectiveKey(resourceOrProject), measure); } @Override @@ -138,7 +134,7 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen private Resource resourceOrProject(@Nullable Resource resource) { if (resource == null) { - return project; + return index.getResource(module.key()); } Resource indexedResource = getResource(resource); return indexedResource != null ? indexedResource : resource; @@ -147,24 +143,17 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen @Override public Measure saveMeasure(InputFile inputFile, Metric metric, Double value) { Measure<?> measure = new Measure(metric, value); - return saveMeasure(getResource(inputFile), measure); + return saveMeasure(inputFile, measure); } @Override public Measure saveMeasure(InputFile inputFile, Measure measure) { - return saveMeasure(getResource(inputFile), measure); + return index.addMeasure(inputFile.key(), measure); } @Override public Resource getResource(InputPath inputPath) { - Resource r; - if (inputPath instanceof InputDir) { - r = Directory.create(((InputDir) inputPath).relativePath()); - } else if (inputPath instanceof InputFile) { - r = File.create(((InputFile) inputPath).relativePath()); - } else { - throw new IllegalArgumentException("Unknow input path type: " + inputPath); - } + Resource r = index.toResource(inputPath); return getResource(r); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/ScannerPerspectives.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/ScannerPerspectives.java index 1903ef38b12..f15ba18ea66 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/ScannerPerspectives.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/ScannerPerspectives.java @@ -23,21 +23,27 @@ import com.google.common.collect.Maps; import java.util.Map; import javax.annotation.CheckForNull; import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.component.Perspective; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.resources.Resource; -import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.core.component.ComponentKeys; import org.sonar.scanner.index.DefaultIndex; +import org.sonar.scanner.scan.filesystem.InputComponentStore; public class ScannerPerspectives implements ResourcePerspectives { private final Map<Class<?>, PerspectiveBuilder<?>> builders = Maps.newHashMap(); private final DefaultIndex resourceIndex; - private final BatchComponentCache componentCache; + private final InputComponentStore componentStore; + private final DefaultInputModule module; - public ScannerPerspectives(PerspectiveBuilder[] builders, DefaultIndex resourceIndex, BatchComponentCache componentCache) { + public ScannerPerspectives(PerspectiveBuilder[] builders, DefaultInputModule module, DefaultIndex resourceIndex, InputComponentStore componentStore) { this.resourceIndex = resourceIndex; - this.componentCache = componentCache; + this.componentStore = componentStore; + this.module = module; + for (PerspectiveBuilder builder : builders) { this.builders.put(builder.getPerspectiveClass(), builder); } @@ -48,15 +54,27 @@ public class ScannerPerspectives implements ResourcePerspectives { public <P extends Perspective> P as(Class<P> perspectiveClass, Resource resource) { Resource indexedResource = resource; if (resource.getEffectiveKey() == null) { - indexedResource = resourceIndex.getResource(resource); + indexedResource = resourceIndex.getResource(getEffectiveKey(resource)); } if (indexedResource != null) { PerspectiveBuilder<P> builder = builderFor(perspectiveClass); - return builder.loadPerspective(perspectiveClass, componentCache.get(indexedResource).inputComponent()); + return builder.loadPerspective(perspectiveClass, componentStore.getByKey(indexedResource.getEffectiveKey())); } return null; } + private String getEffectiveKey(Resource r) { + if (r.getEffectiveKey() != null) { + return r.getEffectiveKey(); + } + + if (ResourceUtils.isProject(r) || /* For technical projects */ResourceUtils.isRootProject(r)) { + return r.getKey(); + } else { + return ComponentKeys.createEffectiveKey(module.key(), r); + } + } + @Override public <P extends Perspective> P as(Class<P> perspectiveClass, InputPath inputPath) { PerspectiveBuilder<P> builder = builderFor(perspectiveClass); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/BatchComponent.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/BatchComponent.java deleted file mode 100644 index bf522d96bdc..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/BatchComponent.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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.index; - -import java.util.ArrayList; -import java.util.Collection; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; - -public class BatchComponent { - - private final int batchId; - private final Resource r; - private final BatchComponent parent; - private final Collection<BatchComponent> children = new ArrayList<>(); - private InputComponent inputComponent; - - public BatchComponent(int batchId, Resource r, @Nullable BatchComponent parent) { - this.batchId = batchId; - this.r = r; - this.parent = parent; - if (parent != null) { - parent.children.add(this); - } - } - - public String key() { - return r.getEffectiveKey(); - } - - public int batchId() { - return batchId; - } - - public Resource resource() { - return r; - } - - @CheckForNull - public BatchComponent parent() { - return parent; - } - - public Collection<BatchComponent> children() { - return children; - } - - public boolean isFile() { - return this.inputComponent.isFile(); - } - - public BatchComponent setInputComponent(InputComponent inputComponent) { - this.inputComponent = inputComponent; - return this; - } - - public InputComponent inputComponent() { - return inputComponent; - } - - public boolean isProjectOrModule() { - return ResourceUtils.isProject(r); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/BatchComponentCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/BatchComponentCache.java deleted file mode 100644 index 0a2248c9fb5..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/BatchComponentCache.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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.index; - -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import com.google.common.collect.Maps; -import java.util.Collection; -import java.util.Map; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.resources.Resource; - -@ScannerSide -public class BatchComponentCache { - // components by key - private final Map<String, BatchComponent> components = Maps.newLinkedHashMap(); - - private BatchComponent root; - - @CheckForNull - public BatchComponent get(String componentKey) { - return components.get(componentKey); - } - - public BatchComponent get(Resource resource) { - return components.get(resource.getEffectiveKey()); - } - - public BatchComponent get(InputComponent inputComponent) { - return components.get(inputComponent.key()); - } - - public BatchComponent add(Resource resource, @Nullable Resource parentResource) { - String componentKey = resource.getEffectiveKey(); - Preconditions.checkState(!Strings.isNullOrEmpty(componentKey), "Missing resource effective key"); - BatchComponent parent = parentResource != null ? get(parentResource.getEffectiveKey()) : null; - BatchComponent batchComponent = new BatchComponent(components.size() + 1, resource, parent); - components.put(componentKey, batchComponent); - if (parent == null) { - root = batchComponent; - } - return batchComponent; - } - - public Collection<BatchComponent> all() { - return components.values(); - } - - public BatchComponent getRoot() { - return root; - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/Bucket.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/Bucket.java deleted file mode 100644 index 72f41198053..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/Bucket.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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.index; - -import com.google.common.collect.Lists; -import org.sonar.api.resources.Resource; - -import javax.annotation.Nullable; - -import java.util.Collections; -import java.util.List; - -public final class Bucket { - - private Resource resource; - - private Bucket parent; - private List<Bucket> children; - - public Bucket(Resource resource) { - this.resource = resource; - } - - public Resource getResource() { - return resource; - } - - public Bucket setParent(@Nullable Bucket parent) { - this.parent = parent; - if (parent != null) { - parent.addChild(this); - } - return this; - } - - private Bucket addChild(Bucket child) { - if (children == null) { - children = Lists.newArrayList(); - } - children.add(child); - return this; - } - - private void removeChild(Bucket child) { - if (children != null) { - children.remove(child); - } - } - - public List<Bucket> getChildren() { - return children == null ? Collections.<Bucket>emptyList() : children; - } - - public Bucket getParent() { - return parent; - } - - public void clear() { - children = null; - if (parent != null) { - parent.removeChild(this); - parent = null; - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Bucket that = (Bucket) o; - return resource.equals(that.resource); - } - - @Override - public int hashCode() { - return resource.hashCode(); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java index a1db20b949e..48ba1cbf2ab 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java @@ -19,146 +19,70 @@ */ package org.sonar.scanner.index; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; + import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.bootstrap.ProjectDefinition; + +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputDir; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputComponentTree; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.design.Dependency; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.MeasuresFilters; import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric.ValueType; +import org.sonar.api.resources.Directory; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; -import org.sonar.api.scan.filesystem.PathResolver; -import org.sonar.core.component.ComponentKeys; import org.sonar.core.util.stream.Collectors; -import org.sonar.scanner.DefaultProjectTree; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.sensor.DefaultSensorStorage; public class DefaultIndex { - - private static final Logger LOG = LoggerFactory.getLogger(DefaultIndex.class); - - private final BatchComponentCache componentCache; + private final InputComponentStore componentStore; private final MeasureCache measureCache; - private final DefaultProjectTree projectTree; private final MetricFinder metricFinder; // caches private DefaultSensorStorage sensorStorage; - private Project currentProject; - private Map<Resource, Bucket> buckets = Maps.newLinkedHashMap(); - public DefaultIndex(BatchComponentCache componentCache, DefaultProjectTree projectTree, MeasureCache measureCache, MetricFinder metricFinder) { - this.componentCache = componentCache; - this.projectTree = projectTree; + private InputComponentTree tree; + + public DefaultIndex(InputComponentStore componentStore, InputComponentTree tree, MeasureCache measureCache, MetricFinder metricFinder) { + this.componentStore = componentStore; + this.tree = tree; this.measureCache = measureCache; this.metricFinder = metricFinder; } - public void start() { - Project rootProject = projectTree.getRootProject(); - if (StringUtils.isNotBlank(rootProject.getKey())) { - doStart(rootProject); - } - } - - void doStart(Project rootProject) { - Bucket bucket = new Bucket(rootProject); - addBucket(rootProject, bucket); - BatchComponent component = componentCache.add(rootProject, null); - component.setInputComponent(new DefaultInputModule(rootProject.getEffectiveKey())); - currentProject = rootProject; - - for (Project module : rootProject.getModules()) { - addModule(rootProject, module); - } - } - - private void addBucket(Resource resource, Bucket bucket) { - buckets.put(resource, bucket); - } - - private void addModule(Project parent, Project module) { - ProjectDefinition parentDefinition = projectTree.getProjectDefinition(parent); - java.io.File parentBaseDir = parentDefinition.getBaseDir(); - ProjectDefinition moduleDefinition = projectTree.getProjectDefinition(module); - java.io.File moduleBaseDir = moduleDefinition.getBaseDir(); - module.setPath(new PathResolver().relativePath(parentBaseDir, moduleBaseDir)); - addResource(module); - for (Project submodule : module.getModules()) { - addModule(module, submodule); - } - } - - public Project getProject() { - return currentProject; - } - - public void setCurrentProject(Project project, DefaultSensorStorage sensorStorage) { - this.currentProject = project; - + public void setCurrentProject(DefaultSensorStorage sensorStorage) { // the following components depend on the current module, so they need to be reloaded. this.sensorStorage = sensorStorage; } - /** - * Keep only project stuff - */ - public void clear() { - Iterator<Map.Entry<Resource, Bucket>> it = buckets.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<Resource, Bucket> entry = it.next(); - Resource resource = entry.getKey(); - if (!ResourceUtils.isSet(resource)) { - entry.getValue().clear(); - it.remove(); - } - - } - } - @CheckForNull - public Measure getMeasure(Resource resource, org.sonar.api.batch.measure.Metric<?> metric) { - return getMeasures(resource, MeasuresFilters.metric(metric)); + public Measure getMeasure(String key, org.sonar.api.batch.measure.Metric<?> metric) { + return getMeasures(key, MeasuresFilters.metric(metric)); } @CheckForNull - public <M> M getMeasures(Resource resource, MeasuresFilter<M> filter) { - // Reload resource so that effective key is populated - Resource indexedResource = getResource(resource); - if (indexedResource == null) { - return null; - } + public <M> M getMeasures(String key, MeasuresFilter<M> filter) { Collection<DefaultMeasure<?>> unfiltered = new ArrayList<>(); if (filter instanceof MeasuresFilters.MetricFilter) { // optimization - DefaultMeasure<?> byMetric = measureCache.byMetric(indexedResource.getEffectiveKey(), ((MeasuresFilters.MetricFilter<M>) filter).filterOnMetricKey()); + DefaultMeasure<?> byMetric = measureCache.byMetric(key, ((MeasuresFilters.MetricFilter<M>) filter).filterOnMetricKey()); if (byMetric != null) { unfiltered.add(byMetric); } } else { - for (DefaultMeasure<?> measure : measureCache.byComponentKey(indexedResource.getEffectiveKey())) { + for (DefaultMeasure<?> measure : measureCache.byComponentKey(key)) { unfiltered.add(measure); } } @@ -196,10 +120,10 @@ public class DefaultIndex { } } - public Measure addMeasure(Resource resource, Measure measure) { - Bucket bucket = getBucket(resource); - if (bucket == null) { - return measure; + public Measure addMeasure(String key, Measure measure) { + InputComponent component = componentStore.getByKey(key); + if (component == null) { + throw new IllegalStateException("Invalid component key: " + key); } if (sensorStorage.isDeprecatedMetric(measure.getMetricKey())) { // Ignore deprecated metrics @@ -228,130 +152,52 @@ public class DefaultIndex { } else { throw new UnsupportedOperationException("Unsupported type :" + metric.valueType()); } - sensorStorage.saveMeasure(componentCache.get(resource).inputComponent(), newMeasure); + sensorStorage.saveMeasure(component, newMeasure); return measure; } - public Dependency addDependency(Dependency dependency) { - return dependency; - } - - public Set<Resource> getResources() { - return buckets.keySet(); - } - - public String getSource(Resource reference) { - Resource resource = getResource(reference); - if (resource instanceof File) { - File file = (File) resource; - Project module = currentProject; - ProjectDefinition def = projectTree.getProjectDefinition(module); - try { - return FileUtils.readFileToString(new java.io.File(def.getBaseDir(), file.getPath())); - } catch (IOException e) { - throw new IllegalStateException("Unable to read file content " + reference, e); - } - } - return null; - } - - /** - * Does nothing if the resource is already registered. - */ - public Resource addResource(Resource resource) { - Bucket bucket = doIndex(resource); - return bucket != null ? bucket.getResource() : null; - } - @CheckForNull - public <R extends Resource> R getResource(@Nullable R reference) { - Bucket bucket = getBucket(reference); - if (bucket != null) { - return (R) bucket.getResource(); - } - return null; - } - - public List<Resource> getChildren(Resource resource) { - List<Resource> children = Lists.newLinkedList(); - Bucket bucket = getBucket(resource); - if (bucket != null) { - for (Bucket childBucket : bucket.getChildren()) { - children.add(childBucket.getResource()); - } + public Resource getParent(String key) { + InputComponent component = componentStore.getByKey(key); + if (component == null) { + return null; } - return children; - } - - public Resource getParent(Resource resource) { - Bucket bucket = getBucket(resource); - if (bucket != null && bucket.getParent() != null) { - return bucket.getParent().getResource(); + InputComponent parent = tree.getParent(component); + if (parent == null) { + return null; } - return null; - } - - public boolean index(Resource resource) { - Bucket bucket = doIndex(resource); - return bucket != null; - } - private Bucket doIndex(Resource resource) { - if (resource.getParent() != null) { - doIndex(resource.getParent()); - } - return doIndex(resource, resource.getParent()); + return toResource(parent); } - public boolean index(Resource resource, Resource parentReference) { - Bucket bucket = doIndex(resource, parentReference); - return bucket != null; + public Collection<Resource> getChildren(String key) { + InputComponent component = componentStore.getByKey(key); + Collection<InputComponent> children = tree.getChildren(component); + return children.stream().map(this::toResource).collect(Collectors.toList()); } - private Bucket doIndex(Resource resource, @Nullable Resource parentReference) { - Bucket bucket = getBucket(resource); - if (bucket != null) { - return bucket; - } - - if (StringUtils.isBlank(resource.getKey())) { - LOG.warn("Unable to index a resource without key: {}", resource); - return null; - } - - Resource parent = (Resource) ObjectUtils.defaultIfNull(parentReference, currentProject); - - Bucket parentBucket = getBucket(parent); - if (parentBucket == null && parent != null) { - LOG.warn("Resource ignored, parent is not indexed: {}", resource); - return null; - } - - if (ResourceUtils.isProject(resource) || /* For technical projects */ResourceUtils.isRootProject(resource)) { - resource.setEffectiveKey(resource.getKey()); + public Resource toResource(InputComponent inputComponent) { + Resource r; + if (inputComponent instanceof InputDir) { + r = Directory.create(((InputDir) inputComponent).relativePath()); + } else if (inputComponent instanceof InputFile) { + r = File.create(((InputFile) inputComponent).relativePath()); + } else if (inputComponent instanceof InputModule) { + r = new Project(((DefaultInputModule) inputComponent).definition()); } else { - resource.setEffectiveKey(ComponentKeys.createEffectiveKey(currentProject, resource)); - } - bucket = new Bucket(resource).setParent(parentBucket); - addBucket(resource, bucket); - - Resource parentResource = parentBucket != null ? parentBucket.getResource() : null; - BatchComponent component = componentCache.add(resource, parentResource); - if (ResourceUtils.isProject(resource)) { - component.setInputComponent(new DefaultInputModule(resource.getEffectiveKey())); + throw new IllegalArgumentException("Unknow input path type: " + inputComponent); } - return bucket; + r.setEffectiveKey(inputComponent.key()); + return r; } - private Bucket getBucket(@Nullable Resource reference) { - if (reference == null) { + @CheckForNull + public Resource getResource(String key) { + InputComponent component = componentStore.getByKey(key); + if (component == null) { return null; } - if (StringUtils.isNotBlank(reference.getKey())) { - return buckets.get(reference); - } - return null; + return toResource(component); } - } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java index b0cc7c3bb0d..06782401e14 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java @@ -22,21 +22,24 @@ package org.sonar.scanner.issue; import java.util.Date; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; -import org.sonar.api.resources.Project; +import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.rule.RuleKey; import org.sonar.api.scan.issue.filter.FilterableIssue; +import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.protocol.output.ScannerReport.Issue; public class DefaultFilterableIssue implements FilterableIssue { private final Issue rawIssue; - private final Project project; + private final ProjectAnalysisInfo projectAnalysisInfo; private final String componentKey; + private DefaultInputModule module; - public DefaultFilterableIssue(Project project, Issue rawIssue, String componentKey) { - this.project = project; + public DefaultFilterableIssue(InputModule module, ProjectAnalysisInfo projectAnalysisInfo, Issue rawIssue, String componentKey) { + this.module = (DefaultInputModule) module; + this.projectAnalysisInfo = projectAnalysisInfo; this.rawIssue = rawIssue; this.componentKey = componentKey; - } @Override @@ -76,12 +79,12 @@ public class DefaultFilterableIssue implements FilterableIssue { @Override public Date creationDate() { - return project.getAnalysisDate(); + return projectAnalysisInfo.analysisDate(); } @Override public String projectKey() { - return project.getEffectiveKey(); + return module.key(); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueAdapterForFilter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueAdapterForFilter.java index d52ed3ad19e..00cf22d19b2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueAdapterForFilter.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DeprecatedIssueAdapterForFilter.java @@ -24,23 +24,29 @@ import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; + +import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueComment; -import org.sonar.api.resources.Project; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.Duration; +import org.sonar.scanner.ProjectAnalysisInfo; /** * @deprecated since 5.3 */ @Deprecated class DeprecatedIssueAdapterForFilter implements Issue { - private final Project project; private final org.sonar.scanner.protocol.output.ScannerReport.Issue rawIssue; private final String componentKey; + private DefaultInputModule module; + private ProjectAnalysisInfo projectAnalysisInfo; - DeprecatedIssueAdapterForFilter(Project project, org.sonar.scanner.protocol.output.ScannerReport.Issue rawIssue, String componentKey) { - this.project = project; + DeprecatedIssueAdapterForFilter(InputModule module, ProjectAnalysisInfo projectAnalysisInfo, org.sonar.scanner.protocol.output.ScannerReport.Issue rawIssue, + String componentKey) { + this.module = (DefaultInputModule) module; + this.projectAnalysisInfo = projectAnalysisInfo; this.rawIssue = rawIssue; this.componentKey = componentKey; } @@ -113,7 +119,7 @@ class DeprecatedIssueAdapterForFilter implements Issue { @Override public Date creationDate() { - return project.getAnalysisDate(); + return projectAnalysisInfo.analysisDate(); } @Override @@ -169,7 +175,7 @@ class DeprecatedIssueAdapterForFilter implements Issue { @Override public String projectKey() { - return project.getEffectiveKey(); + return module.definition().getKeyWithBranch(); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java index 346f3803464..f485dec405c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java @@ -22,39 +22,42 @@ package org.sonar.scanner.issue; import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilterChain; +import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.InputModule; import org.sonar.api.issue.Issue; import org.sonar.api.scan.issue.filter.IssueFilter; -import org.sonar.api.resources.Project; @ScannerSide public class IssueFilters { private final IssueFilter[] filters; private final org.sonar.api.issue.batch.IssueFilter[] deprecatedFilters; - private final Project project; + private final InputModule module; + private final ProjectAnalysisInfo projectAnalysisInfo; - public IssueFilters(Project project, IssueFilter[] exclusionFilters, org.sonar.api.issue.batch.IssueFilter[] filters) { - this.project = project; + public IssueFilters(InputModule module, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] exclusionFilters, org.sonar.api.issue.batch.IssueFilter[] filters) { + this.module = module; this.filters = exclusionFilters; this.deprecatedFilters = filters; + this.projectAnalysisInfo = projectAnalysisInfo; } - public IssueFilters(Project project, IssueFilter[] filters) { - this(project, filters, new org.sonar.api.issue.batch.IssueFilter[0]); + public IssueFilters(InputModule module, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] filters) { + this(module, projectAnalysisInfo, filters, new org.sonar.api.issue.batch.IssueFilter[0]); } - public IssueFilters(Project project, org.sonar.api.issue.batch.IssueFilter[] deprecatedFilters) { - this(project, new IssueFilter[0], deprecatedFilters); + public IssueFilters(InputModule module, ProjectAnalysisInfo projectAnalysisInfo, org.sonar.api.issue.batch.IssueFilter[] deprecatedFilters) { + this(module, projectAnalysisInfo, new IssueFilter[0], deprecatedFilters); } - public IssueFilters(Project project) { - this(project, new IssueFilter[0], new org.sonar.api.issue.batch.IssueFilter[0]); + public IssueFilters(InputModule module, ProjectAnalysisInfo projectAnalysisInfo) { + this(module, projectAnalysisInfo, new IssueFilter[0], new org.sonar.api.issue.batch.IssueFilter[0]); } public boolean accept(String componentKey, ScannerReport.Issue rawIssue) { IssueFilterChain filterChain = new DefaultIssueFilterChain(filters); - FilterableIssue fIssue = new DefaultFilterableIssue(project, rawIssue, componentKey); + FilterableIssue fIssue = new DefaultFilterableIssue(module, projectAnalysisInfo, rawIssue, componentKey); if (filterChain.accept(fIssue)) { return acceptDeprecated(componentKey, rawIssue); } @@ -63,7 +66,7 @@ public class IssueFilters { } public boolean acceptDeprecated(String componentKey, ScannerReport.Issue rawIssue) { - Issue issue = new DeprecatedIssueAdapterForFilter(project, rawIssue, componentKey); + Issue issue = new DeprecatedIssueAdapterForFilter(module, projectAnalysisInfo, rawIssue, componentKey); return new DeprecatedIssueFilterChain(deprecatedFilters).accept(issue); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueTransformer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueTransformer.java index 2c2a31535c7..cf6e5ec30c6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueTransformer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueTransformer.java @@ -26,11 +26,11 @@ import java.util.Date; import java.util.List; import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.issue.Issue; import org.sonar.api.rule.RuleKey; import org.sonar.core.component.ComponentKeys; import org.sonar.core.util.Uuids; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.issue.tracking.SourceHashHolder; import org.sonar.scanner.issue.tracking.TrackedIssue; import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; @@ -73,7 +73,7 @@ public class IssueTransformer { issue.setResolution(Issue.RESOLUTION_REMOVED); } - public static Collection<TrackedIssue> toTrackedIssue(BatchComponent component, Collection<ScannerReport.Issue> rawIssues, @Nullable SourceHashHolder hashes) { + public static Collection<TrackedIssue> toTrackedIssue(InputComponent component, Collection<ScannerReport.Issue> rawIssues, @Nullable SourceHashHolder hashes) { List<TrackedIssue> issues = new ArrayList<>(rawIssues.size()); for (ScannerReport.Issue issue : rawIssues) { @@ -83,7 +83,7 @@ public class IssueTransformer { return issues; } - public static TrackedIssue toTrackedIssue(BatchComponent component, ScannerReport.Issue rawIssue, @Nullable SourceHashHolder hashes) { + public static TrackedIssue toTrackedIssue(InputComponent component, ScannerReport.Issue rawIssue, @Nullable SourceHashHolder hashes) { RuleKey ruleKey = RuleKey.of(rawIssue.getRuleRepository(), rawIssue.getRuleKey()); Preconditions.checkNotNull(component.key(), "Component key must be set"); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java index 3fe3d2ef369..e618b1ddb96 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java @@ -20,8 +20,8 @@ package org.sonar.scanner.issue; import com.google.common.base.Strings; -import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.Rule; @@ -30,8 +30,6 @@ import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.issue.Issue.Flow; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.MessageException; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.Constants.Severity; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation; @@ -46,19 +44,16 @@ public class ModuleIssues { private final Rules rules; private final IssueFilters filters; private final ReportPublisher reportPublisher; - private final BatchComponentCache componentCache; - public ModuleIssues(ActiveRules activeRules, Rules rules, IssueFilters filters, ReportPublisher reportPublisher, BatchComponentCache componentCache) { + public ModuleIssues(ActiveRules activeRules, Rules rules, IssueFilters filters, ReportPublisher reportPublisher) { this.activeRules = activeRules; this.rules = rules; this.filters = filters; this.reportPublisher = reportPublisher; - this.componentCache = componentCache; } public boolean initAndAddIssue(Issue issue) { - InputComponent inputComponent = issue.primaryLocation().inputComponent(); - BatchComponent component = componentCache.get(inputComponent); + DefaultInputComponent inputComponent = (DefaultInputComponent) issue.primaryLocation().inputComponent(); Rule rule = validateRule(issue); ActiveRule activeRule = activeRules.find(issue.ruleKey()); @@ -81,7 +76,7 @@ public class ModuleIssues { builder.setMsg(primaryMessage); locationBuilder.setMsg(primaryMessage); - locationBuilder.setComponentRef(component.batchId()); + locationBuilder.setComponentRef(inputComponent.batchId()); TextRange primaryTextRange = issue.primaryLocation().textRange(); if (primaryTextRange != null) { builder.setTextRange(toProtobufTextRange(textRangeBuilder, primaryTextRange)); @@ -94,7 +89,7 @@ public class ModuleIssues { ScannerReport.Issue rawIssue = builder.build(); if (filters.accept(inputComponent.key(), rawIssue)) { - write(component, rawIssue); + write(inputComponent.batchId(), rawIssue); return true; } return false; @@ -107,7 +102,7 @@ public class ModuleIssues { flowBuilder.clear(); for (org.sonar.api.batch.sensor.issue.IssueLocation location : flow.locations()) { locationBuilder.clear(); - locationBuilder.setComponentRef(componentCache.get(location.inputComponent()).batchId()); + locationBuilder.setComponentRef(((DefaultInputComponent) location.inputComponent()).batchId()); String message = location.message(); if (message != null) { locationBuilder.setMsg(message); @@ -144,8 +139,8 @@ public class ModuleIssues { return rule; } - public void write(BatchComponent component, ScannerReport.Issue rawIssue) { - reportPublisher.getWriter().appendComponentIssue(component.batchId(), rawIssue); + public void write(int batchId, ScannerReport.Issue rawIssue) { + reportPublisher.getWriter().appendComponentIssue(batchId, rawIssue); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java index 9c3da91e702..99c47fb064b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java @@ -20,15 +20,16 @@ package org.sonar.scanner.issue.tracking; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.resources.Project; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.core.util.CloseableIterator; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.issue.IssueCache; import org.sonar.scanner.issue.IssueTransformer; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.report.ReportPublisher; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.util.ProgressReport; import javax.annotation.Nullable; @@ -41,23 +42,23 @@ import java.util.concurrent.TimeUnit; @ScannerSide public class IssueTransition { private final IssueCache issueCache; - private final BatchComponentCache componentCache; + private final InputComponentStore inputComponentStore; private final ReportPublisher reportPublisher; private final Date analysisDate; @Nullable private final LocalIssueTracking localIssueTracking; - public IssueTransition(BatchComponentCache componentCache, IssueCache issueCache, ReportPublisher reportPublisher, + public IssueTransition(InputComponentStore inputComponentCache, ProjectAnalysisInfo projectAnalysisInfo, IssueCache issueCache, ReportPublisher reportPublisher, @Nullable LocalIssueTracking localIssueTracking) { - this.componentCache = componentCache; + this.inputComponentStore = inputComponentCache; this.issueCache = issueCache; this.reportPublisher = reportPublisher; this.localIssueTracking = localIssueTracking; - this.analysisDate = ((Project) componentCache.getRoot().resource()).getAnalysisDate(); + this.analysisDate = projectAnalysisInfo.analysisDate(); } - public IssueTransition(BatchComponentCache componentCache, IssueCache issueCache, ReportPublisher reportPublisher) { - this(componentCache, issueCache, reportPublisher, null); + public IssueTransition(InputComponentStore inputComponentCache, ProjectAnalysisInfo projectAnalysisInfo, IssueCache issueCache, ReportPublisher reportPublisher) { + this(inputComponentCache, projectAnalysisInfo, issueCache, reportPublisher, null); } public void execute() { @@ -66,7 +67,7 @@ public class IssueTransition { } ScannerReportReader reader = new ScannerReportReader(reportPublisher.getReportDir()); - int nbComponents = componentCache.all().size(); + int nbComponents = inputComponentStore.all().size(); if (nbComponents == 0) { return; @@ -77,8 +78,8 @@ public class IssueTransition { int count = 0; try { - for (BatchComponent component : componentCache.all()) { - trackIssues(reader, component); + for (InputComponent component : inputComponentStore.all()) { + trackIssues(reader, (DefaultInputComponent) component); count++; progressReport.message(count + "/" + nbComponents + " components tracked"); } @@ -87,7 +88,7 @@ public class IssueTransition { } } - public void trackIssues(ScannerReportReader reader, BatchComponent component) { + public void trackIssues(ScannerReportReader reader, DefaultInputComponent component) { // raw issues = all the issues created by rule engines during this module scan and not excluded by filters List<ScannerReport.Issue> rawIssues = new LinkedList<>(); try (CloseableIterator<ScannerReport.Issue> it = reader.readComponentIssues(component.batchId())) { @@ -110,7 +111,7 @@ public class IssueTransition { } } - private static List<TrackedIssue> doTransition(List<ScannerReport.Issue> rawIssues, BatchComponent component) { + private static List<TrackedIssue> doTransition(List<ScannerReport.Issue> rawIssues, InputComponent component) { List<TrackedIssue> issues = new ArrayList<>(rawIssues.size()); for (ScannerReport.Issue issue : rawIssues) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java index e83785cf44b..465d4bbce7c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/LocalIssueTracking.java @@ -20,6 +20,8 @@ package org.sonar.scanner.issue.tracking; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -30,17 +32,18 @@ import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Status; +import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.InputComponentTree; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.rule.ActiveRules; -import org.sonar.api.resources.ResourceUtils; import org.sonar.core.issue.tracking.Input; import org.sonar.core.issue.tracking.Tracker; import org.sonar.core.issue.tracking.Tracking; import org.sonar.scanner.analysis.DefaultAnalysisMode; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.issue.IssueTransformer; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.repository.ProjectRepositories; @@ -52,13 +55,15 @@ public class LocalIssueTracking { private final ActiveRules activeRules; private final ServerIssueRepository serverIssueRepository; private final DefaultAnalysisMode mode; + private final InputComponentTree componentTree; private boolean hasServerAnalysis; - public LocalIssueTracking(Tracker<TrackedIssue, ServerIssueFromWs> tracker, ServerLineHashesLoader lastLineHashes, + public LocalIssueTracking(Tracker<TrackedIssue, ServerIssueFromWs> tracker, ServerLineHashesLoader lastLineHashes, InputComponentTree componentTree, ActiveRules activeRules, ServerIssueRepository serverIssueRepository, ProjectRepositories projectRepositories, DefaultAnalysisMode mode) { this.tracker = tracker; this.lastLineHashes = lastLineHashes; + this.componentTree = componentTree; this.serverIssueRepository = serverIssueRepository; this.mode = mode; this.activeRules = activeRules; @@ -71,7 +76,7 @@ public class LocalIssueTracking { } } - public List<TrackedIssue> trackIssues(BatchComponent component, Collection<ScannerReport.Issue> reportIssues, Date analysisDate) { + public List<TrackedIssue> trackIssues(InputComponent component, Collection<ScannerReport.Issue> reportIssues, Date analysisDate) { List<TrackedIssue> trackedIssues = new LinkedList<>(); if (hasServerAnalysis) { // all the issues that are not closed in db before starting this module scan, including manual issues @@ -96,7 +101,8 @@ public class LocalIssueTracking { } } - if (hasServerAnalysis && ResourceUtils.isRootProject(component.resource())) { + if (hasServerAnalysis && componentTree.getParent(component) == null) { + Preconditions.checkState(component instanceof InputModule, "Object without parent is of type: " + component.getClass()); // issues that relate to deleted components addIssuesOnDeletedComponents(trackedIssues); } @@ -127,9 +133,9 @@ public class LocalIssueTracking { return new IssueTrackingInput<>(rIssues, baseHashes); } - private boolean shouldCopyServerIssues(BatchComponent component) { + private boolean shouldCopyServerIssues(InputComponent component) { if (!mode.scanAllFiles() && component.isFile()) { - InputFile inputFile = (InputFile) component.inputComponent(); + InputFile inputFile = (InputFile) component; if (inputFile.status() == Status.SAME) { return true; } @@ -155,19 +161,16 @@ public class LocalIssueTracking { } @CheckForNull - private SourceHashHolder loadSourceHashes(BatchComponent component) { + private SourceHashHolder loadSourceHashes(InputComponent component) { SourceHashHolder sourceHashHolder = null; if (component.isFile()) { - DefaultInputFile file = (DefaultInputFile) component.inputComponent(); - if (file == null) { - throw new IllegalStateException("Resource " + component.resource() + " was not found in InputPath cache"); - } + DefaultInputFile file = (DefaultInputFile) component; sourceHashHolder = new SourceHashHolder(file, lastLineHashes); } return sourceHashHolder; } - private Collection<ServerIssueFromWs> loadServerIssues(BatchComponent component) { + private Collection<ServerIssueFromWs> loadServerIssues(InputComponent component) { Collection<ServerIssueFromWs> serverIssues = new ArrayList<>(); for (org.sonar.scanner.protocol.input.ScannerInput.ServerIssue previousIssue : serverIssueRepository.byComponent(component)) { serverIssues.add(new ServerIssueFromWs(previousIssue)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java index 116955cc6ea..f706da4e545 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java @@ -22,16 +22,17 @@ package org.sonar.scanner.issue.tracking; import com.google.common.base.Function; import javax.annotation.Nullable; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.core.component.ComponentKeys; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; import org.sonar.scanner.repository.ServerIssuesLoader; import org.sonar.scanner.scan.ImmutableProjectReactor; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.storage.Storage; import org.sonar.scanner.storage.Storages; @@ -46,9 +47,9 @@ public class ServerIssueRepository { private Storage<ServerIssue> issuesCache; private final ServerIssuesLoader previousIssuesLoader; private final ImmutableProjectReactor reactor; - private final BatchComponentCache resourceCache; + private final InputComponentStore resourceCache; - public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, ImmutableProjectReactor reactor, BatchComponentCache resourceCache) { + public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, ImmutableProjectReactor reactor, InputComponentStore resourceCache) { this.caches = caches; this.previousIssuesLoader = previousIssuesLoader; this.reactor = reactor; @@ -63,8 +64,8 @@ public class ServerIssueRepository { profiler.stopInfo(); } - public Iterable<ServerIssue> byComponent(BatchComponent component) { - return issuesCache.values(component.batchId()); + public Iterable<ServerIssue> byComponent(InputComponent component) { + return issuesCache.values(((DefaultInputComponent) component).batchId()); } private class SaveIssueConsumer implements Function<ServerIssue, Void> { @@ -75,7 +76,7 @@ public class ServerIssueRepository { return null; } String componentKey = ComponentKeys.createEffectiveKey(issue.getModuleKey(), issue.hasPath() ? issue.getPath() : null); - BatchComponent r = resourceCache.get(componentKey); + DefaultInputComponent r = (DefaultInputComponent) resourceCache.getByKey(componentKey); if (r == null) { // Deleted resource issuesCache.put(0, issue.getKey(), issue); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java index 879e4a016fd..0366c780caa 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java @@ -50,7 +50,7 @@ import org.sonar.scanner.protocol.output.ScannerReport.Symbol; import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.report.ScannerReportUtils; import org.sonar.scanner.scan.ProjectScanContainer; -import org.sonar.scanner.scan.filesystem.InputPathCache; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.protocol.output.ScannerReportReader; import static org.apache.commons.lang.StringUtils.isNotEmpty; @@ -87,9 +87,9 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver private void storeReportComponents(int componentRef, String parentModuleKey, String branch) { Component component = getReportReader().readComponent(componentRef); if (isNotEmpty(component.getKey())) { - reportComponents.put(component.getKey() + (isNotEmpty(branch) ? (":" + branch) : ""), component); + reportComponents.put(component.getKey(), component); } else { - reportComponents.put(parentModuleKey + (isNotEmpty(branch) ? (":" + branch) : "") + ":" + component.getPath(), component); + reportComponents.put(parentModuleKey + ":" + component.getPath(), component); } for (int childId : component.getChildRefList()) { storeReportComponents(childId, isNotEmpty(component.getKey()) ? component.getKey() : parentModuleKey, branch); @@ -102,7 +102,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver } private void storeFs(ProjectScanContainer container) { - InputPathCache inputFileCache = container.getComponentByType(InputPathCache.class); + InputComponentStore inputFileCache = container.getComponentByType(InputComponentStore.class); for (InputFile inputPath : inputFileCache.allFiles()) { inputFiles.put(inputPath.relativePath(), inputPath); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java index ec6ca476708..ede6ae406fc 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java @@ -20,10 +20,9 @@ package org.sonar.scanner.phases; import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; -import org.sonar.scanner.index.DefaultIndex; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; import org.sonar.scanner.rule.QProfileVerifier; import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem; @@ -36,21 +35,18 @@ public abstract class AbstractPhaseExecutor { private final InitializersExecutor initializersExecutor; private final SensorsExecutor sensorsExecutor; private final SensorContext sensorContext; - private final DefaultIndex index; private final FileSystemLogger fsLogger; private final DefaultModuleFileSystem fs; private final QProfileVerifier profileVerifier; private final IssueExclusionsLoader issueExclusionsLoader; public AbstractPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, - SensorContext sensorContext, DefaultIndex index, - EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, + SensorContext sensorContext, EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader) { this.postJobsExecutor = postJobsExecutor; this.initializersExecutor = initializersExecutor; this.sensorsExecutor = sensorsExecutor; this.sensorContext = sensorContext; - this.index = index; this.eventBus = eventBus; this.fsLogger = fsLogger; this.fs = fs; @@ -61,7 +57,7 @@ public abstract class AbstractPhaseExecutor { /** * Executed on each module */ - public final void execute(Project module) { + public final void execute(DefaultInputModule module) { eventBus.fireEvent(new ProjectAnalysisEvent(module, true)); executeInitializersPhase(); @@ -77,7 +73,7 @@ public abstract class AbstractPhaseExecutor { sensorsExecutor.execute(sensorContext); - if (module.isRoot()) { + if (module.definition().getParent() == null) { executeOnRoot(); postJobsExecutor.execute(sensorContext); } @@ -111,7 +107,7 @@ public abstract class AbstractPhaseExecutor { private void cleanMemory() { String cleanMemory = "Clean memory"; eventBus.fireEvent(new BatchStepEvent(cleanMemory, true)); - index.clear(); + //index.clear(); eventBus.fireEvent(new BatchStepEvent(cleanMemory, false)); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java index 91cf3e899a0..cc0f7bf14a9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java @@ -22,6 +22,7 @@ package org.sonar.scanner.phases; import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.Initializer; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.resources.Project; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -34,23 +35,24 @@ public class InitializersExecutor { private static final Logger LOG = Loggers.get(SensorsExecutor.class); - private Project project; - private ScannerExtensionDictionnary selector; - private EventBus eventBus; + private final DefaultInputModule module; + private final ScannerExtensionDictionnary selector; + private final EventBus eventBus; - public InitializersExecutor(ScannerExtensionDictionnary selector, Project project, EventBus eventBus) { + public InitializersExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus) { this.selector = selector; - this.project = project; + this.module = module; this.eventBus = eventBus; } public void execute() { - Collection<Initializer> initializers = selector.select(Initializer.class, project, true, null); + Collection<Initializer> initializers = selector.select(Initializer.class, module, true, null); eventBus.fireEvent(new InitializersPhaseEvent(Lists.newArrayList(initializers), true)); if (LOG.isDebugEnabled()) { LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> ")); } + Project project = new Project(module.definition()); for (Initializer initializer : initializers) { eventBus.fireEvent(new InitializerExecutionEvent(initializer, true)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java index ff4943453cb..c68bf066b49 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java @@ -24,7 +24,6 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; -import org.sonar.scanner.index.DefaultIndex; import org.sonar.scanner.issue.IssueCallback; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; import org.sonar.scanner.issue.tracking.IssueTransition; @@ -43,9 +42,9 @@ public final class IssuesPhaseExecutor extends AbstractPhaseExecutor { private final IssueCallback issueCallback; public IssuesPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, - DefaultIndex index, EventBus eventBus, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, + EventBus eventBus, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, index, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); this.eventBus = eventBus; this.issuesReport = jsonReport; this.localIssueTracking = localIssueTracking; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java index 23fe945516f..881a81df796 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java @@ -25,6 +25,7 @@ import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.resources.Project; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -37,17 +38,17 @@ public class PostJobsExecutor { private static final Logger LOG = Loggers.get(PostJobsExecutor.class); private final ScannerExtensionDictionnary selector; - private final Project project; + private final DefaultInputModule module; private final EventBus eventBus; - public PostJobsExecutor(ScannerExtensionDictionnary selector, Project project, EventBus eventBus) { + public PostJobsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus) { this.selector = selector; - this.project = project; + this.module = module; this.eventBus = eventBus; } public void execute(SensorContext context) { - Collection<PostJob> postJobs = selector.select(PostJob.class, project, true, null); + Collection<PostJob> postJobs = selector.select(PostJob.class, module, true, null); eventBus.fireEvent(new PostJobPhaseEvent(Lists.newArrayList(postJobs), true)); execute(context, postJobs); @@ -57,6 +58,7 @@ public class PostJobsExecutor { private void execute(SensorContext context, Collection<PostJob> postJobs) { logPostJobs(postJobs); + Project project = new Project(module.definition()); for (PostJob postJob : postJobs) { LOG.info("Executing post-job {}", ScannerUtils.describe(postJob)); eventBus.fireEvent(new PostJobExecutionEvent(postJob, true)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java index aca9dc99bd7..841e82c1e5b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java @@ -20,21 +20,21 @@ package org.sonar.scanner.phases; import org.sonar.api.batch.events.ProjectAnalysisHandler; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.resources.Project; class ProjectAnalysisEvent extends AbstractPhaseEvent<ProjectAnalysisHandler> implements ProjectAnalysisHandler.ProjectAnalysisEvent { + private DefaultInputModule module; - private final Project project; - - ProjectAnalysisEvent(Project project, boolean start) { + ProjectAnalysisEvent(DefaultInputModule module, boolean start) { super(start); - this.project = project; + this.module = module; } @Override public Project getProject() { - return project; + return new Project(module.definition()); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java index 7c3434af247..c0d769ac318 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java @@ -23,7 +23,6 @@ import org.sonar.api.batch.SensorContext; import org.sonar.scanner.cpd.CpdExecutor; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; -import org.sonar.scanner.index.DefaultIndex; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.rule.QProfileVerifier; @@ -37,9 +36,9 @@ public final class PublishPhaseExecutor extends AbstractPhaseExecutor { private final CpdExecutor cpdExecutor; public PublishPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, - DefaultIndex index, EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, + EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, index, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); this.eventBus = eventBus; this.reportPublisher = reportPublisher; this.cpdExecutor = cpdExecutor; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java index acaa8727cf9..dc8a96cc643 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java @@ -23,6 +23,7 @@ import com.google.common.collect.Lists; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.resources.Project; import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; import org.sonar.scanner.events.EventBus; @@ -30,15 +31,14 @@ import java.util.Collection; @ScannerSide public class SensorsExecutor { + private final EventBus eventBus; + private final DefaultInputModule module; + private final ScannerExtensionDictionnary selector; - private EventBus eventBus; - private Project module; - private ScannerExtensionDictionnary selector; - - public SensorsExecutor(ScannerExtensionDictionnary selector, Project project, EventBus eventBus) { + public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus) { this.selector = selector; this.eventBus = eventBus; - this.module = project; + this.module = module; } public void execute(SensorContext context) { @@ -54,7 +54,7 @@ public class SensorsExecutor { private void executeSensor(SensorContext context, Sensor sensor) { eventBus.fireEvent(new SensorExecutionEvent(sensor, true)); - sensor.analyse(module, context); + sensor.analyse(new Project(module.definition()), context); eventBus.fireEvent(new SensorExecutionEvent(sensor, false)); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/DefaultPostJobContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/DefaultPostJobContext.java index ee2c3d64238..7f43c5ac313 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/DefaultPostJobContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/postjob/DefaultPostJobContext.java @@ -30,22 +30,21 @@ import org.sonar.api.batch.postjob.issue.PostJobIssue; import org.sonar.api.batch.rule.Severity; import org.sonar.api.config.Settings; import org.sonar.api.rule.RuleKey; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.issue.IssueCache; import org.sonar.scanner.issue.tracking.TrackedIssue; +import org.sonar.scanner.scan.filesystem.InputComponentStore; public class DefaultPostJobContext implements PostJobContext { private final Settings settings; private final IssueCache cache; - private final BatchComponentCache resourceCache; private final AnalysisMode analysisMode; + private InputComponentStore inputComponentCache; - public DefaultPostJobContext(Settings settings, IssueCache cache, BatchComponentCache resourceCache, AnalysisMode analysisMode) { + public DefaultPostJobContext(Settings settings, IssueCache cache, InputComponentStore inputComponentCache, AnalysisMode analysisMode) { this.settings = settings; this.cache = cache; - this.resourceCache = resourceCache; + this.inputComponentCache = inputComponentCache; this.analysisMode = analysisMode; } @@ -100,8 +99,7 @@ public class DefaultPostJobContext implements PostJobContext { @Override public InputComponent inputComponent() { - BatchComponent component = resourceCache.get(wrapped.componentKey()); - return component != null ? component.inputComponent() : null; + return inputComponentCache.getByKey(wrapped.componentKey()); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfiler.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfiler.java index 8906535df07..5a923f01fde 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfiler.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/profiling/PhasesSumUpTimeProfiler.java @@ -71,7 +71,7 @@ public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorEx private final System2 system; private final File out; - + public PhasesSumUpTimeProfiler(System2 system, GlobalProperties bootstrapProps) { String workingDirPath = StringUtils.defaultIfBlank(bootstrapProps.property(CoreProperties.WORKING_DIRECTORY), CoreProperties.WORKING_DIRECTORY_DEFAULT_VALUE); File workingDir = new File(workingDirPath).getAbsoluteFile(); @@ -115,7 +115,7 @@ public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorEx String fileName = module.getKey() + "-profiler.properties"; dumpToFile(props, ScannerUtils.cleanKeyForFilename(fileName)); totalProfiling.merge(currentModuleProfiling); - if (module.isRoot() && !module.getModules().isEmpty()) { + if (module.getParent() == null && !module.getModules().isEmpty()) { dumpTotalExecutionSummary(); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java index 1f5238b8a8d..1449657f23f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java @@ -23,18 +23,19 @@ import javax.annotation.CheckForNull; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputComponentTree; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType; import org.sonar.scanner.protocol.output.ScannerReport.ComponentLink; import org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType; -import org.sonar.scanner.scan.ImmutableProjectReactor; import org.sonar.scanner.protocol.output.ScannerReportWriter; /** @@ -42,91 +43,109 @@ import org.sonar.scanner.protocol.output.ScannerReportWriter; */ public class ComponentsPublisher implements ReportPublisherStep { - private final BatchComponentCache resourceCache; - private final ImmutableProjectReactor reactor; + private InputComponentTree componentTree; + private InputModuleHierarchy moduleHierarchy; - public ComponentsPublisher(ImmutableProjectReactor reactor, BatchComponentCache resourceCache) { - this.reactor = reactor; - this.resourceCache = resourceCache; + public ComponentsPublisher(InputModuleHierarchy moduleHierarchy, InputComponentTree inputComponentTree) { + this.moduleHierarchy = moduleHierarchy; + this.componentTree = inputComponentTree; } @Override public void publish(ScannerReportWriter writer) { - BatchComponent rootProject = resourceCache.get(reactor.getRoot().getKeyWithBranch()); - recursiveWriteComponent(rootProject, writer); + recursiveWriteComponent((DefaultInputComponent) moduleHierarchy.root(), writer); } - private void recursiveWriteComponent(BatchComponent batchComponent, ScannerReportWriter writer) { - Resource r = batchComponent.resource(); + private void recursiveWriteComponent(DefaultInputComponent component, ScannerReportWriter writer) { ScannerReport.Component.Builder builder = ScannerReport.Component.newBuilder(); // non-null fields - builder.setRef(batchComponent.batchId()); - builder.setType(getType(r)); + builder.setRef(component.batchId()); + builder.setType(getType(component)); // Don't set key on directories and files to save space since it can be deduced from path - if (batchComponent.isProjectOrModule()) { + if (component instanceof InputModule) { + DefaultInputModule inputModule = (DefaultInputModule) component; // Here we want key without branch - ProjectDefinition def = reactor.getProjectDefinition(batchComponent.key()); - builder.setKey(def.getKey()); - } + builder.setKey(inputModule.key()); - // protocol buffers does not accept null values + // protocol buffers does not accept null values + String name = getName(inputModule); + if (name != null) { + builder.setName(name); + } + String description = getDescription(inputModule); + if (description != null) { + builder.setDescription(description); + } - if (batchComponent.isFile()) { - builder.setIsTest(ResourceUtils.isUnitTestFile(r)); - builder.setLines(((InputFile) batchComponent.inputComponent()).lines()); + writeVersion(inputModule, builder); } - String name = getName(r); - if (name != null) { - builder.setName(name); - } - String description = getDescription(r); - if (description != null) { - builder.setDescription(description); + + if (component.isFile()) { + InputFile file = (InputFile) component; + builder.setIsTest(file.type() == InputFile.Type.TEST); + builder.setLines(file.lines()); + + String lang = getLanguageKey(file); + if (lang != null) { + builder.setLanguage(lang); + } } - String path = r.getPath(); + + String path = getPath(component); if (path != null) { builder.setPath(path); } - String lang = getLanguageKey(r); - if (lang != null) { - builder.setLanguage(lang); - } - for (BatchComponent child : batchComponent.children()) { - builder.addChildRef(child.batchId()); + + for (InputComponent child : componentTree.getChildren(component)) { + builder.addChildRef(((DefaultInputComponent) child).batchId()); } - writeLinks(batchComponent, builder); - writeVersion(batchComponent, builder); + writeLinks(component, builder); writer.writeComponent(builder.build()); - for (BatchComponent child : batchComponent.children()) { - recursiveWriteComponent(child, writer); + for (InputComponent child : componentTree.getChildren(component)) { + recursiveWriteComponent((DefaultInputComponent) child, writer); } } - private void writeVersion(BatchComponent c, ScannerReport.Component.Builder builder) { - if (c.isProjectOrModule()) { - ProjectDefinition def = reactor.getProjectDefinition(c.key()); - String version = getVersion(def); - if(version != null) { - builder.setVersion(version); + private void writeVersion(DefaultInputModule module, ScannerReport.Component.Builder builder) { + ProjectDefinition def = module.definition(); + String version = getVersion(def); + if (version != null) { + builder.setVersion(version); + } + } + + @CheckForNull + private String getPath(InputComponent component) { + if (component instanceof InputPath) { + InputPath inputPath = (InputPath) component; + if (StringUtils.isEmpty(inputPath.relativePath())) { + return "/"; + } else { + return inputPath.relativePath(); } + } else if (component instanceof InputModule) { + InputModule module = (InputModule) component; + return moduleHierarchy.relativePath(module); } + throw new IllegalStateException("Unkown component: " + component.getClass()); } private static String getVersion(ProjectDefinition def) { String version = def.getOriginalVersion(); - if(StringUtils.isNotBlank(version)) { + if (StringUtils.isNotBlank(version)) { return version; } - + return def.getParent() != null ? getVersion(def.getParent()) : null; } - private void writeLinks(BatchComponent c, ScannerReport.Component.Builder builder) { - if (c.isProjectOrModule()) { - ProjectDefinition def = reactor.getProjectDefinition(c.key()); + private void writeLinks(InputComponent c, ScannerReport.Component.Builder builder) { + if (c instanceof InputModule) { + DefaultInputModule inputModule = (DefaultInputModule) c; + ProjectDefinition def = inputModule.definition(); ComponentLink.Builder linkBuilder = ComponentLink.newBuilder(); writeProjectLink(builder, def, linkBuilder, CoreProperties.LINKS_HOME_PAGE, ComponentLinkType.HOME); @@ -149,37 +168,35 @@ public class ComponentsPublisher implements ReportPublisherStep { } @CheckForNull - private static String getLanguageKey(Resource r) { - Language language = r.getLanguage(); - return ResourceUtils.isFile(r) && language != null ? language.getKey() : null; + private static String getLanguageKey(InputFile file) { + return file.language(); } @CheckForNull - private static String getName(Resource r) { - if (ResourceUtils.isProject(r)) { - Project project = (Project) r; - return project.getOriginalName(); + private static String getName(DefaultInputModule module) { + if (StringUtils.isNotEmpty(module.definition().getBranch())) { + return module.definition().getOriginalName() + " " + module.definition().getBranch(); + } else { + return module.definition().getOriginalName(); } - // Don't return name for directories and files since it can be guessed from the path - return (ResourceUtils.isFile(r) || ResourceUtils.isDirectory(r)) ? null : r.getName(); } @CheckForNull - private static String getDescription(Resource r) { - // Only for projets and modules - return ResourceUtils.isProject(r) ? r.getDescription() : null; + private static String getDescription(DefaultInputModule module) { + return module.definition().getDescription(); } - private static ComponentType getType(Resource r) { - if (ResourceUtils.isFile(r)) { + private ComponentType getType(InputComponent r) { + if (r instanceof InputFile) { return ComponentType.FILE; - } else if (ResourceUtils.isDirectory(r)) { + } else if (r instanceof InputDir) { return ComponentType.DIRECTORY; - } else if (ResourceUtils.isModuleProject(r)) { - return ComponentType.MODULE; - } else if (ResourceUtils.isRootProject(r)) { + } else if ((r instanceof InputModule) && moduleHierarchy.isRoot((InputModule) r)) { return ComponentType.PROJECT; + } else if (r instanceof InputModule) { + return ComponentType.MODULE; } + throw new IllegalArgumentException("Unknown resource type: " + r); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java index ba776070fa5..2060505fca8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java @@ -26,42 +26,40 @@ import java.util.Map; import javax.annotation.Nonnull; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage; import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.Builder; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.scan.measure.MeasureCache; public class CoveragePublisher implements ReportPublisherStep { - private final BatchComponentCache resourceCache; + private final InputComponentStore componentCache; private final MeasureCache measureCache; - public CoveragePublisher(BatchComponentCache resourceCache, MeasureCache measureCache) { - this.resourceCache = resourceCache; + public CoveragePublisher(InputComponentStore componentCache, MeasureCache measureCache) { + this.componentCache = componentCache; this.measureCache = measureCache; } @Override public void publish(ScannerReportWriter writer) { - for (final BatchComponent resource : resourceCache.all()) { - if (!resource.isFile()) { - continue; - } + for (final InputFile file : componentCache.allFiles()) { + DefaultInputFile inputFile = (DefaultInputFile) file; Map<Integer, LineCoverage.Builder> coveragePerLine = new LinkedHashMap<>(); - int lineCount = ((InputFile) resource.inputComponent()).lines(); - applyLineMeasure(resource.key(), lineCount, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, + int lineCount = inputFile.lines(); + applyLineMeasure(inputFile.key(), lineCount, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, (value, builder) -> builder.setHits(Integer.parseInt(value) > 0)); - applyLineMeasure(resource.key(), lineCount, CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine, + applyLineMeasure(inputFile.key(), lineCount, CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine, (value, builder) -> builder.setConditions(Integer.parseInt(value))); - applyLineMeasure(resource.key(), lineCount, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, + applyLineMeasure(inputFile.key(), lineCount, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, (value, builder) -> builder.setCoveredConditions(Integer.parseInt(value))); - writer.writeComponentCoverage(resource.batchId(), Iterables.transform(coveragePerLine.values(), BuildCoverage.INSTANCE)); + writer.writeComponentCoverage(inputFile.batchId(), Iterables.transform(coveragePerLine.values(), BuildCoverage.INSTANCE)); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java index 33f58a8db3f..30e649716d3 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java @@ -24,8 +24,11 @@ import java.io.Serializable; import java.util.Collections; import java.util.Map; import java.util.stream.StreamSupport; + +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; @@ -34,8 +37,6 @@ import org.sonar.api.test.TestCase.Status; import org.sonar.api.utils.KeyValueFormat; import org.sonar.core.util.stream.Collectors; import org.sonar.scanner.deprecated.test.TestPlanBuilder; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Measure.BoolValue; import org.sonar.scanner.protocol.output.ScannerReport.Measure.DoubleValue; @@ -43,6 +44,7 @@ import org.sonar.scanner.protocol.output.ScannerReport.Measure.IntValue; import org.sonar.scanner.protocol.output.ScannerReport.Measure.LongValue; import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.scan.measure.MeasureCache; import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER; @@ -66,12 +68,12 @@ import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES_KEY; public class MeasuresPublisher implements ReportPublisherStep { - private final BatchComponentCache componentCache; + private final InputComponentStore componentCache; private final MeasureCache measureCache; private final TestPlanBuilder testPlanBuilder; - public MeasuresPublisher(BatchComponentCache resourceCache, MeasureCache measureCache, TestPlanBuilder testPlanBuilder) { - this.componentCache = resourceCache; + public MeasuresPublisher(InputComponentStore componentCache, MeasureCache measureCache, TestPlanBuilder testPlanBuilder) { + this.componentCache = componentCache; this.measureCache = measureCache; this.testPlanBuilder = testPlanBuilder; } @@ -80,7 +82,8 @@ public class MeasuresPublisher implements ReportPublisherStep { public void publish(ScannerReportWriter writer) { final ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); - for (final BatchComponent component : componentCache.all()) { + for (final InputComponent c : componentCache.all()) { + DefaultInputComponent component = (DefaultInputComponent) c; // Recompute all coverage measures from line data to take into account the possible merge of several reports updateCoverageFromLineData(component); // Recompute test execution measures from MutableTestPlan to take into account the possible merge of several reports @@ -119,8 +122,8 @@ public class MeasuresPublisher implements ReportPublisherStep { } } - private void updateTestExecutionFromTestPlan(final BatchComponent component) { - final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component.inputComponent()); + private void updateTestExecutionFromTestPlan(final InputComponent component) { + final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component); if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { return; } @@ -136,8 +139,8 @@ public class MeasuresPublisher implements ReportPublisherStep { measureCache.put(component.key(), TEST_FAILURES_KEY, new DefaultMeasure<Integer>().forMetric(TEST_FAILURES).withValue((int) failedTests)); } - private void updateCoverageFromLineData(final BatchComponent component) { - if (!component.isFile() || ((InputFile) component.inputComponent()).type() != Type.MAIN) { + private void updateCoverageFromLineData(final InputComponent component) { + if (!component.isFile() || ((InputFile) component).type() != Type.MAIN) { return; } DefaultMeasure<String> lineHitsMeasure = (DefaultMeasure<String>) measureCache.byMetric(component.key(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java index 72fd40673fd..da0f2183943 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java @@ -21,39 +21,38 @@ package org.sonar.scanner.report; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; +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.Project; +import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportWriter; import org.sonar.scanner.rule.ModuleQProfiles; import org.sonar.scanner.rule.QProfile; -import org.sonar.scanner.scan.ImmutableProjectReactor; public class MetadataPublisher implements ReportPublisherStep { - private final BatchComponentCache componentCache; - private final ImmutableProjectReactor reactor; private final Settings settings; private final ModuleQProfiles qProfiles; + private final ProjectAnalysisInfo projectAnalysisInfo; + private final InputModuleHierarchy moduleHierarchy; - public MetadataPublisher(BatchComponentCache componentCache, ImmutableProjectReactor reactor, Settings settings, ModuleQProfiles qProfiles) { - this.componentCache = componentCache; - this.reactor = reactor; + public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Settings settings, ModuleQProfiles qProfiles) { + this.projectAnalysisInfo = projectAnalysisInfo; + this.moduleHierarchy = moduleHierarchy; this.settings = settings; this.qProfiles = qProfiles; } @Override public void publish(ScannerReportWriter writer) { - ProjectDefinition root = reactor.getRoot(); - BatchComponent rootProject = componentCache.getRoot(); + DefaultInputModule rootProject = moduleHierarchy.root(); + ProjectDefinition rootDef = rootProject.definition(); ScannerReport.Metadata.Builder builder = ScannerReport.Metadata.newBuilder() - .setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate().getTime()) + .setAnalysisDate(projectAnalysisInfo.analysisDate().getTime()) // Here we want key without branch - .setProjectKey(root.getKey()) + .setProjectKey(rootDef.getKey()) .setCrossProjectDuplicationActivated(SonarCpdBlockIndex.isCrossProjectDuplicationEnabled(settings)) .setRootComponentRef(rootProject.batchId()); @@ -62,7 +61,7 @@ public class MetadataPublisher implements ReportPublisherStep { builder.setOrganizationKey(organization); } - String branch = root.getBranch(); + String branch = rootDef.getBranch(); if (branch != null) { builder.setBranch(branch); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/SourcePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/SourcePublisher.java index 06c4893d6ea..b270567f86f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/SourcePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/SourcePublisher.java @@ -23,9 +23,10 @@ import org.apache.commons.io.ByteOrderMark; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.BOMInputStream; import org.sonar.api.batch.fs.InputFile; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.filesystem.InputComponentStore; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -36,24 +37,21 @@ import java.nio.charset.StandardCharsets; public class SourcePublisher implements ReportPublisherStep { - private final BatchComponentCache resourceCache; + private final InputComponentStore componentCache; - public SourcePublisher(BatchComponentCache resourceCache) { - this.resourceCache = resourceCache; + public SourcePublisher(InputComponentStore componentCache) { + this.componentCache = componentCache; } @Override public void publish(ScannerReportWriter writer) { - for (final BatchComponent resource : resourceCache.all()) { - if (!resource.isFile()) { - continue; - } - - InputFile inputFile = (InputFile) resource.inputComponent(); - File iofile = writer.getSourceFile(resource.batchId()); + for (final InputFile file : componentCache.allFiles()) { + DefaultInputFile inputFile = (DefaultInputFile) file; + File iofile = writer.getSourceFile(inputFile.batchId()); int line = 0; - try (FileOutputStream output = new FileOutputStream(iofile); BOMInputStream bomIn = new BOMInputStream(new FileInputStream(inputFile.file()), - ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE); + try (FileOutputStream output = new FileOutputStream(iofile); + BOMInputStream bomIn = new BOMInputStream(new FileInputStream(inputFile.file()), + ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE); BufferedReader reader = new BufferedReader(new InputStreamReader(bomIn, inputFile.charset()))) { String lineStr = reader.readLine(); while (lineStr != null) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java index 04ae3e824eb..70080c6a9a3 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java @@ -23,29 +23,31 @@ import com.google.common.collect.Iterables; import java.util.HashSet; import java.util.Set; import java.util.stream.StreamSupport; + +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.test.CoverageBlock; import org.sonar.api.test.MutableTestCase; import org.sonar.api.test.MutableTestPlan; import org.sonar.api.test.TestCase; import org.sonar.scanner.deprecated.test.DefaultTestable; import org.sonar.scanner.deprecated.test.TestPlanBuilder; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.CoverageDetail; import org.sonar.scanner.protocol.output.ScannerReport.Test; import org.sonar.scanner.protocol.output.ScannerReport.Test.TestStatus; +import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.protocol.output.ScannerReportWriter; import static java.util.stream.Collectors.toList; public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { - private final BatchComponentCache componentCache; + private final InputComponentStore componentStore; private final TestPlanBuilder testPlanBuilder; - public TestExecutionAndCoveragePublisher(BatchComponentCache resourceCache, TestPlanBuilder testPlanBuilder) { - this.componentCache = resourceCache; + public TestExecutionAndCoveragePublisher(InputComponentStore componentStore, TestPlanBuilder testPlanBuilder) { + this.componentStore = componentStore; this.testPlanBuilder = testPlanBuilder; } @@ -54,8 +56,9 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { final ScannerReport.Test.Builder testBuilder = ScannerReport.Test.newBuilder(); final ScannerReport.CoverageDetail.Builder builder = ScannerReport.CoverageDetail.newBuilder(); final ScannerReport.CoverageDetail.CoveredFile.Builder coveredBuilder = ScannerReport.CoverageDetail.CoveredFile.newBuilder(); - for (final BatchComponent component : componentCache.all()) { - final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component.inputComponent()); + for (final InputComponent c : componentStore.all()) { + DefaultInputComponent component = (DefaultInputComponent) c; + final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component); if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { continue; } @@ -81,7 +84,8 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { builder.setTestName(testName); for (CoverageBlock block : testCase.coverageBlocks()) { coveredBuilder.clear(); - coveredBuilder.setFileRef(componentCache.get(((DefaultTestable) block.testable()).inputFile().key()).batchId()); + DefaultInputComponent c = (DefaultInputComponent) componentStore.getByKey(((DefaultTestable) block.testable()).inputFile().key()); + coveredBuilder.setFileRef(c.batchId()); for (int line : block.lines()) { coveredBuilder.addCoveredLine(line); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultComponentTree.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultComponentTree.java new file mode 100644 index 00000000000..bf5218f3714 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultComponentTree.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.CheckForNull; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.internal.InputComponentTree; + +public class DefaultComponentTree implements InputComponentTree { + private Map<InputComponent, InputComponent> parents = new HashMap<>(); + private Map<InputComponent, Set<InputComponent>> children = new HashMap<>(); + + public void index(InputComponent component, InputComponent parent) { + parents.put(component, parent); + Set<InputComponent> list = children.get(parent); + if (list == null) { + list = new LinkedHashSet<>(); + children.put(parent, list); + } + + list.add(component); + } + + @Override + public Collection<InputComponent> getChildren(InputComponent component) { + return children.getOrDefault(component, Collections.emptySet()); + } + + @CheckForNull + @Override + public InputComponent getParent(InputComponent component) { + return parents.get(component); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java new file mode 100644 index 00000000000..f1f35acf1f9 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java @@ -0,0 +1,119 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.CheckForNull; + +import org.sonar.api.Startable; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.scanner.scan.filesystem.BatchIdGenerator; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +public class DefaultInputModuleHierarchy implements InputModuleHierarchy, Startable { + private final PathResolver pathResolver = new PathResolver(); + private final ImmutableProjectReactor projectReactor; + private final DefaultComponentTree componentTree; + private final BatchIdGenerator batchIdGenerator; + + private DefaultInputModule root; + private Map<DefaultInputModule, DefaultInputModule> parents; + private Multimap<DefaultInputModule, DefaultInputModule> children; + + public DefaultInputModuleHierarchy(ImmutableProjectReactor projectReactor, DefaultComponentTree componentTree, BatchIdGenerator batchIdGenerator) { + this.projectReactor = projectReactor; + this.componentTree = componentTree; + this.batchIdGenerator = batchIdGenerator; + } + + @Override + public void start() { + doStart(projectReactor.getRoot()); + } + + void doStart(ProjectDefinition rootProjectDefinition) { + parents = new HashMap<>(); + children = HashMultimap.create(); + root = new DefaultInputModule(rootProjectDefinition, batchIdGenerator.get()); + createChildren(root); + } + + private void createChildren(DefaultInputModule parent) { + for (ProjectDefinition def : parent.definition().getSubProjects()) { + DefaultInputModule child = new DefaultInputModule(def, batchIdGenerator.get()); + parents.put(child, parent); + children.put(parent, child); + componentTree.index(child, parent); + createChildren(child); + } + } + + @Override + public DefaultInputModule root() { + return root; + } + + @Override + public Collection<DefaultInputModule> children(InputModule component) { + return children.get((DefaultInputModule) component); + } + + @Override + public DefaultInputModule parent(InputModule component) { + return parents.get(component); + } + + @Override + public boolean isRoot(InputModule module) { + return root.equals(module); + } + + @Override + @CheckForNull + public String relativePath(InputModule module) { + DefaultInputModule parent = parent(module); + if (parent == null) { + return null; + } + DefaultInputModule inputModule = (DefaultInputModule) module; + + ProjectDefinition parentDefinition = parent.definition(); + Path parentBaseDir = parentDefinition.getBaseDir().toPath(); + ProjectDefinition moduleDefinition = inputModule.definition(); + Path moduleBaseDir = moduleDefinition.getBaseDir().toPath(); + + return pathResolver.relativePath(parentBaseDir, moduleBaseDir); + } + + @Override + public void stop() { + // nothing to do + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java index fa31b9c7419..35dabd15894 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java @@ -23,21 +23,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.InstantiationStrategy; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.rule.CheckFactory; -import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.FileExclusions; import org.sonar.core.platform.ComponentContainer; import org.sonar.scanner.DefaultFileLinesContextFactory; -import org.sonar.scanner.DefaultProjectTree; import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; import org.sonar.scanner.bootstrap.ExtensionInstaller; import org.sonar.scanner.bootstrap.ExtensionUtils; import org.sonar.scanner.deprecated.DeprecatedSensorContext; import org.sonar.scanner.deprecated.perspectives.ScannerPerspectives; import org.sonar.scanner.events.EventBus; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.index.DefaultIndex; import org.sonar.scanner.issue.IssuableFactory; import org.sonar.scanner.issue.IssueFilters; @@ -59,7 +56,6 @@ import org.sonar.scanner.postjob.PostJobOptimizer; import org.sonar.scanner.rule.QProfileVerifier; import org.sonar.scanner.rule.RuleFinderCompatibility; import org.sonar.scanner.rule.RulesProfileProvider; -import org.sonar.scanner.scan.filesystem.ComponentIndexer; import org.sonar.scanner.scan.filesystem.DefaultModuleFileSystem; import org.sonar.scanner.scan.filesystem.ExclusionFilters; import org.sonar.scanner.scan.filesystem.FileIndexer; @@ -68,7 +64,7 @@ import org.sonar.scanner.scan.filesystem.IndexedFileBuilderProvider; import org.sonar.scanner.scan.filesystem.MetadataGeneratorProvider; import org.sonar.scanner.scan.filesystem.LanguageDetectionFactory; import org.sonar.scanner.scan.filesystem.ModuleFileSystemInitializer; -import org.sonar.scanner.scan.filesystem.ModuleInputFileCache; +import org.sonar.scanner.scan.filesystem.ModuleInputComponentStore; import org.sonar.scanner.scan.filesystem.StatusDetectionFactory; import org.sonar.scanner.scan.report.IssuesReports; import org.sonar.scanner.sensor.DefaultSensorStorage; @@ -79,31 +75,29 @@ import org.sonar.scanner.source.SymbolizableBuilder; public class ModuleScanContainer extends ComponentContainer { private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class); - private final Project module; + private final DefaultInputModule module; - public ModuleScanContainer(ProjectScanContainer parent, Project module) { + public ModuleScanContainer(ProjectScanContainer parent, DefaultInputModule module) { super(parent); this.module = module; } @Override protected void doBeforeStart() { - LOG.info("------------- Scan {}", module.getName()); + LOG.info("------------- Scan {}", module.definition().getName()); addCoreComponents(); addExtensions(); } private void addCoreComponents() { - ProjectDefinition moduleDefinition = getComponentByType(DefaultProjectTree.class).getProjectDefinition(module); add( - moduleDefinition, + module.definition(), module, - getComponentByType(BatchComponentCache.class).get(module).inputComponent(), ModuleSettings.class); // hack to initialize settings before ExtensionProviders ModuleSettings moduleSettings = getComponentByType(ModuleSettings.class); - module.setSettings(moduleSettings); + //module.setSettings(moduleSettings); if (getComponentByType(AnalysisMode.class).isIssues()) { add(IssuesPhaseExecutor.class, @@ -120,7 +114,7 @@ public class ModuleScanContainer extends ComponentContainer { InitializersExecutor.class, // file system - ModuleInputFileCache.class, + ModuleInputComponentStore.class, FileExclusions.class, ExclusionFilters.class, new MetadataGeneratorProvider(), @@ -129,7 +123,6 @@ public class ModuleScanContainer extends ComponentContainer { LanguageDetectionFactory.class, FileIndexer.class, new IndexedFileBuilderProvider(), - ComponentIndexer.class, LanguageVerifier.class, FileSystemLogger.class, DefaultModuleFileSystem.class, @@ -179,12 +172,12 @@ public class ModuleScanContainer extends ComponentContainer { @Override protected void doAfterStart() { DefaultIndex index = getComponentByType(DefaultIndex.class); - index.setCurrentProject(module, getComponentByType(DefaultSensorStorage.class)); + index.setCurrentProject(getComponentByType(DefaultSensorStorage.class)); getComponentByType(AbstractPhaseExecutor.class).execute(module); // Free memory since module settings are no more used - module.setSettings(null); + //module.setSettings(null); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index c7c34eb64d2..de4bfe28a36 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -22,17 +22,17 @@ package org.sonar.scanner.scan; import com.google.common.annotations.VisibleForTesting; 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.Project; 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.DefaultProjectTree; -import org.sonar.scanner.ProjectConfigurator; +import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.analysis.AnalysisProperties; import org.sonar.scanner.analysis.AnalysisTempFolderProvider; import org.sonar.scanner.analysis.DefaultAnalysisMode; @@ -45,7 +45,6 @@ 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.BatchComponentCache; import org.sonar.scanner.index.DefaultIndex; import org.sonar.scanner.issue.DefaultIssueCallback; import org.sonar.scanner.issue.DefaultProjectIssues; @@ -86,7 +85,8 @@ 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.InputPathCache; +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; @@ -133,14 +133,12 @@ public class ProjectScanContainer extends ComponentContainer { EventBus.class, PhasesTimeProfiler.class, ResourceTypes.class, - DefaultProjectTree.class, ProjectReactorValidator.class, CodeColorizers.class, MetricProvider.class, - ProjectConfigurator.class, + ProjectAnalysisInfo.class, DefaultIndex.class, Storages.class, - BatchComponentCache.class, DefaultIssueCallback.class, new RulesProvider(), new ProjectRepositoriesProvider(), @@ -149,8 +147,11 @@ public class ProjectScanContainer extends ComponentContainer { new AnalysisTempFolderProvider(), // file system - InputPathCache.class, + InputComponentStore.class, PathResolver.class, + DefaultInputModuleHierarchy.class, + DefaultComponentTree.class, + BatchIdGenerator.class, // rules new ActiveRulesProvider(), @@ -228,22 +229,22 @@ public class ProjectScanContainer extends ComponentContainer { DefaultAnalysisMode analysisMode = getComponentByType(DefaultAnalysisMode.class); analysisMode.printMode(); LOG.debug("Start recursive analysis of project modules"); - DefaultProjectTree tree = getComponentByType(DefaultProjectTree.class); - scanRecursively(tree.getRootProject()); + InputModuleHierarchy tree = getComponentByType(InputModuleHierarchy.class); + scanRecursively(tree, tree.root()); if (analysisMode.isMediumTest()) { getComponentByType(ScanTaskObservers.class).notifyEndOfScanTask(); } } - private void scanRecursively(Project module) { - for (Project subModules : module.getModules()) { - scanRecursively(subModules); + private void scanRecursively(InputModuleHierarchy tree, DefaultInputModule module) { + for (DefaultInputModule child : tree.children(module)) { + scanRecursively(tree, child); } scan(module); } @VisibleForTesting - void scan(Project module) { + void scan(DefaultInputModule module) { new ModuleScanContainer(this, module).execute(); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java new file mode 100644 index 00000000000..48d90078bae --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.filesystem; + +import java.util.function.Supplier; + +public class BatchIdGenerator implements Supplier<Integer> { + private int nextBatchId = 1; + + @Override + public Integer get() { + return nextBatchId++; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ComponentIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ComponentIndexer.java deleted file mode 100644 index 4c28ad63ab9..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ComponentIndexer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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.filesystem; - -import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.fs.InputDir; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Languages; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; -import org.sonar.scanner.index.DefaultIndex; - -/** - * Index all files/directories of the module in SQ database and importing source code. - * - * @since 4.2 - */ -@ScannerSide -public class ComponentIndexer { - - private final Languages languages; - private final DefaultIndex sonarIndex; - private final Project module; - private final BatchComponentCache componentCache; - - public ComponentIndexer(Project module, Languages languages, DefaultIndex sonarIndex, BatchComponentCache componentCache) { - this.module = module; - this.languages = languages; - this.sonarIndex = sonarIndex; - this.componentCache = componentCache; - } - - public void execute(DefaultModuleFileSystem fs) { - module.setBaseDir(fs.baseDir()); - - for (InputFile inputFile : fs.inputFiles()) { - String languageKey = inputFile.language(); - boolean unitTest = InputFile.Type.TEST == inputFile.type(); - Resource sonarFile = File.create(inputFile.relativePath(), languages.get(languageKey), unitTest); - sonarIndex.index(sonarFile); - BatchComponent file = componentCache.get(sonarFile); - file.setInputComponent(inputFile); - Resource sonarDir = file.parent().resource(); - InputDir inputDir = fs.inputDir(inputFile.file().getParentFile()); - componentCache.get(sonarDir).setInputComponent(inputDir); - } - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultModuleFileSystem.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultModuleFileSystem.java index 03b9fb9285e..3baf5893654 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultModuleFileSystem.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/DefaultModuleFileSystem.java @@ -27,8 +27,8 @@ import java.util.List; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; import org.sonar.api.utils.MessageException; import org.sonar.scanner.analysis.DefaultAnalysisMode; import org.sonar.scanner.repository.ProjectRepositories; @@ -44,30 +44,28 @@ public class DefaultModuleFileSystem extends DefaultFileSystem { private List<File> sourceDirsOrFiles = Lists.newArrayList(); private List<File> testDirsOrFiles = Lists.newArrayList(); - private ComponentIndexer componentIndexer; private boolean initialized; private Charset charset = null; - public DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, Project project, - Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer, DefaultAnalysisMode mode, + public DefaultModuleFileSystem(ModuleInputComponentStore moduleInputFileCache, DefaultInputModule module, + Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, DefaultAnalysisMode mode, ProjectRepositories projectRepositories) { super(initializer.baseDir(), moduleInputFileCache); - setFields(project, settings, indexer, initializer, componentIndexer, mode, projectRepositories); + setFields(module, settings, indexer, initializer, mode, projectRepositories); } @VisibleForTesting - public DefaultModuleFileSystem(Project project, - Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer, DefaultAnalysisMode mode, + public DefaultModuleFileSystem(DefaultInputModule module, + Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, DefaultAnalysisMode mode, ProjectRepositories projectRepositories) { super(initializer.baseDir().toPath()); - setFields(project, settings, indexer, initializer, componentIndexer, mode, projectRepositories); + setFields(module, settings, indexer, initializer, mode, projectRepositories); } - private void setFields(Project project, - Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, ComponentIndexer componentIndexer, DefaultAnalysisMode mode, + private void setFields(DefaultInputModule module, + Settings settings, FileIndexer indexer, ModuleFileSystemInitializer initializer, DefaultAnalysisMode mode, ProjectRepositories projectRepositories) { - this.componentIndexer = componentIndexer; - this.moduleKey = project.getKey(); + this.moduleKey = module.key(); this.settings = settings; this.indexer = indexer; setWorkDir(initializer.workingDir()); @@ -127,9 +125,6 @@ public class DefaultModuleFileSystem extends DefaultFileSystem { } initialized = true; indexer.index(this); - if (componentIndexer != null) { - componentIndexer.execute(this); - } } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java index 046a0a448a0..4b1d99e355c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java @@ -44,13 +44,15 @@ import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultIndexedFile; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.batch.fs.InputFileFilter; import org.sonar.api.utils.MessageException; +import org.sonar.scanner.scan.DefaultComponentTree; import org.sonar.scanner.util.ProgressReport; /** - * Index input files into {@link InputPathCache}. + * Index input files into {@link InputComponentStore}. */ @ScannerSide public class FileIndexer { @@ -59,13 +61,21 @@ public class FileIndexer { private final InputFileFilter[] filters; private final boolean isAggregator; private final ExclusionFilters exclusionFilters; + private final IndexedFileBuilder indexedFileBuilder; + private final MetadataGenerator metadataGenerator; + private final DefaultComponentTree componentTree; + private final DefaultInputModule module; + private final BatchIdGenerator batchIdGenerator; + private final InputComponentStore componentStore; private ProgressReport progressReport; - private IndexedFileBuilder indexedFileBuilder; - private MetadataGenerator metadataGenerator; - public FileIndexer(ExclusionFilters exclusionFilters, IndexedFileBuilder indexedFileBuilder, MetadataGenerator inputFileBuilder, ProjectDefinition def, - InputFileFilter[] filters) { + public FileIndexer(BatchIdGenerator batchIdGenerator, InputComponentStore componentStore, DefaultInputModule module, ExclusionFilters exclusionFilters, + DefaultComponentTree componentTree, IndexedFileBuilder indexedFileBuilder, MetadataGenerator inputFileBuilder, ProjectDefinition def, InputFileFilter[] filters) { + this.batchIdGenerator = batchIdGenerator; + this.componentStore = componentStore; + this.module = module; + this.componentTree = componentTree; this.indexedFileBuilder = indexedFileBuilder; this.metadataGenerator = inputFileBuilder; this.filters = filters; @@ -73,11 +83,13 @@ public class FileIndexer { this.isAggregator = !def.getSubProjects().isEmpty(); } - public FileIndexer(ExclusionFilters exclusionFilters, IndexedFileBuilder indexedFileBuilder, MetadataGenerator inputFileBuilder, ProjectDefinition def) { - this(exclusionFilters, indexedFileBuilder, inputFileBuilder, def, new InputFileFilter[0]); + public FileIndexer(BatchIdGenerator batchIdGenerator, InputComponentStore componentStore, DefaultInputModule module, ExclusionFilters exclusionFilters, + DefaultComponentTree componentTree, IndexedFileBuilder indexedFileBuilder, MetadataGenerator inputFileBuilder, ProjectDefinition def) { + this(batchIdGenerator, componentStore, module, exclusionFilters, componentTree, indexedFileBuilder, inputFileBuilder, def, new InputFileFilter[0]); } void index(DefaultModuleFileSystem fileSystem) { + fileSystem.add(module); if (isAggregator) { // No indexing for an aggregator module return; @@ -102,7 +114,7 @@ public class FileIndexer { try { for (File dirOrFile : sources) { if (dirOrFile.isDirectory()) { - indexDirectory(fileSystem, progress, dirOrFile, type); + indexDirectory(fileSystem, progress, dirOrFile.toPath(), type); } else { indexFile(fileSystem, progress, dirOrFile.toPath(), type); } @@ -112,8 +124,8 @@ public class FileIndexer { } } - private void indexDirectory(final DefaultModuleFileSystem fileSystem, final Progress status, final File dirToIndex, final InputFile.Type type) throws IOException { - Files.walkFileTree(dirToIndex.toPath().normalize(), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, + private void indexDirectory(final DefaultModuleFileSystem fileSystem, final Progress status, final Path dirToIndex, final InputFile.Type type) throws IOException { + Files.walkFileTree(dirToIndex.normalize(), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new IndexFileVisitor(fileSystem, status, type)); } @@ -122,12 +134,10 @@ public class FileIndexer { Path realFile = sourceFile.toRealPath(LinkOption.NOFOLLOW_LINKS); DefaultIndexedFile indexedFile = indexedFileBuilder.create(realFile, type, fileSystem.baseDirPath()); if (indexedFile != null) { - if (exclusionFilters.accept(indexedFile, type)) { - InputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.readMetadata(f, fileSystem.encoding())); - if (accept(inputFile)) { - fileSystem.add(inputFile); - } - indexParentDir(fileSystem, indexedFile); + InputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.readMetadata(f, fileSystem.encoding())); + if (exclusionFilters.accept(indexedFile, type) && accept(inputFile)) { + fileSystem.add(inputFile); + indexParentDir(fileSystem, inputFile); progress.markAsIndexed(indexedFile); LOG.debug("'{}' indexed {} with language '{}'", indexedFile.relativePath(), type == Type.TEST ? "as test " : "", indexedFile.language()); } else { @@ -136,18 +146,25 @@ public class FileIndexer { } } - private static void indexParentDir(DefaultModuleFileSystem fileSystem, IndexedFile indexedFile) { - File parentDir = indexedFile.file().getParentFile(); - String relativePath = new PathResolver().relativePath(fileSystem.baseDir(), parentDir); - if (relativePath != null) { - DefaultInputDir inputDir = new DefaultInputDir(fileSystem.moduleKey(), relativePath); + private void indexParentDir(DefaultModuleFileSystem fileSystem, InputFile inputFile) { + Path parentDir = inputFile.path().getParent(); + String relativePath = new PathResolver().relativePath(fileSystem.baseDirPath(), parentDir); + if (relativePath == null) { + throw new IllegalStateException("Failed to compute relative path of file: " + inputFile); + } + + DefaultInputDir inputDir = (DefaultInputDir) componentStore.getDir(module.key(), relativePath); + if (inputDir == null) { + inputDir = new DefaultInputDir(fileSystem.moduleKey(), relativePath, batchIdGenerator.get()); inputDir.setModuleBaseDir(fileSystem.baseDirPath()); fileSystem.add(inputDir); + componentTree.index(inputDir, module); } + componentTree.index(inputFile, inputDir); } private boolean accept(InputFile indexedFile) { - // InputFileFilter extensions + // InputFileFilter extensions. Might trigger generation of metadata for (InputFileFilter filter : filters) { if (!filter.accept(indexedFile)) { LOG.debug("'{}' excluded by {}", indexedFile.relativePath(), filter.getClass().getName()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilder.java index 352065dbcb7..1db56df4c15 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilder.java @@ -37,12 +37,14 @@ public class IndexedFileBuilder { private final PathResolver pathResolver; private final LanguageDetection langDetection; private final Settings settings; + private final BatchIdGenerator idGenerator; - IndexedFileBuilder(String moduleKey, PathResolver pathResolver, Settings settings, LanguageDetection langDetection) { + IndexedFileBuilder(String moduleKey, PathResolver pathResolver, Settings settings, LanguageDetection langDetection, BatchIdGenerator idGenerator) { this.moduleKey = moduleKey; this.pathResolver = pathResolver; this.settings = settings; this.langDetection = langDetection; + this.idGenerator = idGenerator; } @CheckForNull @@ -52,7 +54,7 @@ public class IndexedFileBuilder { LOG.warn("File '{}' is ignored. It is not located in module basedir '{}'.", file.toAbsolutePath(), moduleBaseDir); return null; } - DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type); + DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, idGenerator.get()); String language = langDetection.language(indexedFile); if (language == null && !settings.getBoolean(CoreProperties.IMPORT_UNKNOWN_FILES_KEY)) { LOG.debug("'{}' language is not supported by any analyzer. Skipping it.", relativePath); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilderProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilderProvider.java index c892bf9ff8f..e0bbf85464e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilderProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/IndexedFileBuilderProvider.java @@ -26,8 +26,9 @@ import org.sonar.api.scan.filesystem.PathResolver; public class IndexedFileBuilderProvider extends ProviderAdapter { - public IndexedFileBuilder provide(ProjectDefinition def, PathResolver pathResolver, Settings settings, LanguageDetectionFactory langDetectionFactory) { - return new IndexedFileBuilder(def.getKeyWithBranch(), pathResolver, settings, langDetectionFactory.create()); + public IndexedFileBuilder provide(ProjectDefinition def, PathResolver pathResolver, Settings settings, + LanguageDetectionFactory langDetectionFactory, BatchIdGenerator idGenerator) { + return new IndexedFileBuilder(def.getKey(), pathResolver, settings, langDetectionFactory.create(), idGenerator); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputPathCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java index 2aff151cdc9..88c41a10d08 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputPathCache.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java @@ -19,23 +19,39 @@ */ package org.sonar.scanner.scan.filesystem; -import com.google.common.collect.Table; -import com.google.common.collect.TreeBasedTable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.CheckForNull; + import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.internal.DefaultInputDir; +import org.sonar.api.batch.fs.internal.DefaultInputFile; -import javax.annotation.CheckForNull; +import com.google.common.collect.Table; +import com.google.common.collect.TreeBasedTable; /** - * Cache of all files and dirs. This cache is shared amongst all project modules. Inclusion and + * Store of all files and dirs. This cache is shared amongst all project modules. Inclusion and * exclusion patterns are already applied. */ @ScannerSide -public class InputPathCache { +public class InputComponentStore { private final Table<String, String, InputFile> inputFileCache = TreeBasedTable.create(); private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create(); + private final Map<String, InputModule> inputModuleCache = new HashMap<>(); + private final Map<String, InputComponent> inputComponents = new HashMap<>(); + private InputModule root; + + public Collection<InputComponent> all() { + return inputComponents.values(); + } public Iterable<InputFile> allFiles() { return inputFileCache.values(); @@ -45,6 +61,19 @@ public class InputPathCache { return inputDirCache.values(); } + public InputComponent getByKey(String key) { + return inputComponents.get(key); + } + + public void setRoot(InputModule root) { + this.root = root; + } + + @CheckForNull + public InputModule root() { + return root; + } + public Iterable<InputFile> filesByModule(String moduleKey) { return inputFileCache.row(moduleKey).values(); } @@ -53,29 +82,35 @@ public class InputPathCache { return inputDirCache.row(moduleKey).values(); } - public InputPathCache removeModule(String moduleKey) { + public InputComponentStore removeModule(String moduleKey) { inputFileCache.row(moduleKey).clear(); inputDirCache.row(moduleKey).clear(); return this; } - public InputPathCache remove(String moduleKey, InputFile inputFile) { - inputFileCache.remove(moduleKey, inputFile.relativePath()); + public InputComponentStore remove(InputFile inputFile) { + DefaultInputFile file = (DefaultInputFile) inputFile; + inputFileCache.remove(file.moduleKey(), inputFile.relativePath()); return this; } - public InputPathCache remove(String moduleKey, InputDir inputDir) { - inputDirCache.remove(moduleKey, inputDir.relativePath()); + public InputComponentStore remove(InputDir inputDir) { + DefaultInputDir dir = (DefaultInputDir) inputDir; + inputDirCache.remove(dir.moduleKey(), inputDir.relativePath()); return this; } - public InputPathCache put(String moduleKey, InputFile inputFile) { - inputFileCache.put(moduleKey, inputFile.relativePath(), inputFile); + public InputComponentStore put(InputFile inputFile) { + DefaultInputFile file = (DefaultInputFile) inputFile; + inputFileCache.put(file.moduleKey(), inputFile.relativePath(), inputFile); + inputComponents.put(inputFile.key(), inputFile); return this; } - public InputPathCache put(String moduleKey, InputDir inputDir) { - inputDirCache.put(moduleKey, inputDir.relativePath(), inputDir); + public InputComponentStore put(InputDir inputDir) { + DefaultInputDir dir = (DefaultInputDir) inputDir; + inputDirCache.put(dir.moduleKey(), inputDir.relativePath(), inputDir); + inputComponents.put(inputDir.key(), inputDir); return this; } @@ -89,4 +124,14 @@ public class InputPathCache { return inputDirCache.get(moduleKey, relativePath); } + @CheckForNull + public InputModule getModule(String moduleKey) { + return inputModuleCache.get(moduleKey); + } + + public void put(InputModule inputModule) { + inputComponents.put(inputModule.key(), inputModule); + inputModuleCache.put(inputModule.key(), inputModule); + } + } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputFileCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStore.java index 4f421964ca7..9b4e5762fb1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputFileCache.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleInputComponentStore.java @@ -20,44 +20,54 @@ package org.sonar.scanner.scan.filesystem; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultFileSystem; @ScannerSide -public class ModuleInputFileCache extends DefaultFileSystem.Cache { +public class ModuleInputComponentStore extends DefaultFileSystem.Cache { private final String moduleKey; - private final InputPathCache inputPathCache; + private final InputComponentStore inputComponentStore; - public ModuleInputFileCache(ProjectDefinition projectDef, InputPathCache projectCache) { - this.moduleKey = projectDef.getKeyWithBranch(); - this.inputPathCache = projectCache; + public ModuleInputComponentStore(InputModule module, InputComponentStore inputComponentStore) { + this.moduleKey = module.key(); + this.inputComponentStore = inputComponentStore; } @Override public Iterable<InputFile> inputFiles() { - return inputPathCache.filesByModule(moduleKey); + return inputComponentStore.filesByModule(moduleKey); } @Override public InputFile inputFile(String relativePath) { - return inputPathCache.getFile(moduleKey, relativePath); + return inputComponentStore.getFile(moduleKey, relativePath); } @Override public InputDir inputDir(String relativePath) { - return inputPathCache.getDir(moduleKey, relativePath); + return inputComponentStore.getDir(moduleKey, relativePath); } @Override protected void doAdd(InputFile inputFile) { - inputPathCache.put(moduleKey, inputFile); + inputComponentStore.put(inputFile); } @Override protected void doAdd(InputDir inputDir) { - inputPathCache.put(moduleKey, inputDir); + inputComponentStore.put(inputDir); + } + + @Override + protected void doAdd(InputModule inputModule) { + inputComponentStore.put(inputModule); + } + + @Override + public InputModule module() { + return inputComponentStore.getModule(moduleKey); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ConsoleReport.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ConsoleReport.java index d4ddff8b2e7..94f7054a128 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ConsoleReport.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ConsoleReport.java @@ -30,7 +30,7 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.scanner.issue.IssueCache; import org.sonar.scanner.issue.tracking.TrackedIssue; -import org.sonar.scanner.scan.filesystem.InputPathCache; +import org.sonar.scanner.scan.filesystem.InputComponentStore; @Properties({ @Property(key = ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, defaultValue = "false", name = "Enable console report", @@ -47,10 +47,10 @@ public class ConsoleReport implements Reporter { private Settings settings; private IssueCache issueCache; - private InputPathCache inputPathCache; + private InputComponentStore inputPathCache; @VisibleForTesting - public ConsoleReport(Settings settings, IssueCache issueCache, InputPathCache inputPathCache) { + public ConsoleReport(Settings settings, IssueCache issueCache, InputComponentStore inputPathCache) { this.settings = settings; this.issueCache = issueCache; this.inputPathCache = inputPathCache; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReport.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReport.java index 470c2d0339e..5da5cb78345 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReport.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReport.java @@ -24,9 +24,10 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; + +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.rule.Rule; import org.sonar.api.rules.RulePriority; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.issue.tracking.TrackedIssue; public class IssuesReport { @@ -36,7 +37,7 @@ public class IssuesReport { private Date date; private boolean noFile; private final ReportSummary summary = new ReportSummary(); - private final Map<BatchComponent, ResourceReport> resourceReportsByResource = Maps.newLinkedHashMap(); + private final Map<InputComponent, ResourceReport> resourceReportsByResource = Maps.newLinkedHashMap(); public ReportSummary getSummary() { return summary; @@ -66,7 +67,7 @@ public class IssuesReport { this.noFile = noFile; } - public Map<BatchComponent, ResourceReport> getResourceReportsByResource() { + public Map<InputComponent, ResourceReport> getResourceReportsByResource() { return resourceReportsByResource; } @@ -74,25 +75,25 @@ public class IssuesReport { return new ArrayList<>(resourceReportsByResource.values()); } - public List<BatchComponent> getResourcesWithReport() { + public List<InputComponent> getResourcesWithReport() { return new ArrayList<>(resourceReportsByResource.keySet()); } - public void addIssueOnResource(BatchComponent resource, TrackedIssue issue, Rule rule, RulePriority severity) { + public void addIssueOnResource(InputComponent resource, TrackedIssue issue, Rule rule, RulePriority severity) { addResource(resource); getSummary().addIssue(issue, rule, severity); resourceReportsByResource.get(resource).addIssue(issue, rule, severity); } - public void addResolvedIssueOnResource(BatchComponent resource, Rule rule, RulePriority severity) { + public void addResolvedIssueOnResource(InputComponent resource, Rule rule, RulePriority severity) { addResource(resource); getSummary().addResolvedIssue(rule, severity); resourceReportsByResource.get(resource).addResolvedIssue(rule, severity); } - private void addResource(BatchComponent resource) { - if (!resourceReportsByResource.containsKey(resource)) { - resourceReportsByResource.put(resource, new ResourceReport(resource)); + private void addResource(InputComponent componnet) { + if (!resourceReportsByResource.containsKey(componnet)) { + resourceReportsByResource.put(componnet, new ResourceReport(componnet)); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReportBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReportBuilder.java index cd39b0750c2..7d5e82a2f8f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReportBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/IssuesReportBuilder.java @@ -24,16 +24,16 @@ import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.rule.Rule; import org.sonar.api.batch.rule.Rules; -import org.sonar.api.resources.Project; import org.sonar.api.rules.RulePriority; -import org.sonar.scanner.DefaultProjectTree; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.issue.IssueCache; import org.sonar.scanner.issue.tracking.TrackedIssue; -import org.sonar.scanner.scan.filesystem.InputPathCache; +import org.sonar.scanner.scan.filesystem.InputComponentStore; @ScannerSide public class IssuesReportBuilder { @@ -42,24 +42,25 @@ public class IssuesReportBuilder { private final IssueCache issueCache; private final Rules rules; - private final BatchComponentCache resourceCache; - private final DefaultProjectTree projectTree; - private final InputPathCache inputPathCache; + private final InputComponentStore inputComponentCache; + private final InputModuleHierarchy moduleHierarchy; + private final ProjectAnalysisInfo projectAnalysisInfo; - public IssuesReportBuilder(IssueCache issueCache, Rules rules, BatchComponentCache resourceCache, DefaultProjectTree projectTree, InputPathCache inputPathCache) { + public IssuesReportBuilder(IssueCache issueCache, Rules rules, ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, + InputComponentStore inputComponentCache) { this.issueCache = issueCache; this.rules = rules; - this.resourceCache = resourceCache; - this.projectTree = projectTree; - this.inputPathCache = inputPathCache; + this.projectAnalysisInfo = projectAnalysisInfo; + this.moduleHierarchy = moduleHierarchy; + this.inputComponentCache = inputComponentCache; } public IssuesReport buildReport() { - Project project = projectTree.getRootProject(); + DefaultInputModule project = moduleHierarchy.root(); IssuesReport issuesReport = new IssuesReport(); - issuesReport.setNoFile(!inputPathCache.allFiles().iterator().hasNext()); - issuesReport.setTitle(project.getName()); - issuesReport.setDate(project.getAnalysisDate()); + issuesReport.setNoFile(!inputComponentCache.allFiles().iterator().hasNext()); + issuesReport.setTitle(project.definition().getName()); + issuesReport.setDate(projectAnalysisInfo.analysisDate()); processIssues(issuesReport, issueCache.all()); @@ -70,7 +71,7 @@ public class IssuesReportBuilder { for (TrackedIssue issue : issues) { Rule rule = findRule(issue); RulePriority severity = RulePriority.valueOf(issue.severity()); - BatchComponent resource = resourceCache.get(issue.componentKey()); + InputComponent resource = inputComponentCache.getByKey(issue.componentKey()); if (!validate(issue, rule, resource)) { continue; } @@ -82,7 +83,7 @@ public class IssuesReportBuilder { } } - private static boolean validate(TrackedIssue issue, @Nullable Rule rule, @Nullable BatchComponent resource) { + private static boolean validate(TrackedIssue issue, @Nullable Rule rule, @Nullable InputComponent resource) { if (rule == null) { LOG.warn("Unknow rule for issue {}", issue); return false; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/JSONReport.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/JSONReport.java index 05cc7f08717..f1ce2013147 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/JSONReport.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/JSONReport.java @@ -43,11 +43,12 @@ import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.rule.Rule; import org.sonar.api.batch.rule.Rules; import org.sonar.api.config.Settings; import org.sonar.api.platform.Server; -import org.sonar.api.resources.Project; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.text.JsonWriter; import org.sonar.scanner.issue.IssueCache; @@ -55,7 +56,7 @@ import org.sonar.scanner.issue.tracking.TrackedIssue; import org.sonar.scanner.protocol.input.ScannerInput; import org.sonar.scanner.protocol.input.ScannerInput.User; import org.sonar.scanner.repository.user.UserRepositoryLoader; -import org.sonar.scanner.scan.filesystem.InputPathCache; +import org.sonar.scanner.scan.filesystem.InputComponentStore; @Properties({ @Property( @@ -72,12 +73,14 @@ public class JSONReport implements Reporter { private final Server server; private final Rules rules; private final IssueCache issueCache; - private final InputPathCache fileCache; - private final Project rootModule; + private final InputComponentStore fileCache; + private final DefaultInputModule rootModule; private final UserRepositoryLoader userRepository; + private final InputModuleHierarchy moduleHierarchy; - public JSONReport(Settings settings, FileSystem fileSystem, Server server, Rules rules, IssueCache issueCache, - Project rootModule, InputPathCache fileCache, UserRepositoryLoader userRepository) { + public JSONReport(InputModuleHierarchy moduleHierarchy, Settings settings, FileSystem fileSystem, Server server, Rules rules, IssueCache issueCache, + DefaultInputModule rootModule, InputComponentStore fileCache, UserRepositoryLoader userRepository) { + this.moduleHierarchy = moduleHierarchy; this.settings = settings; this.fileSystem = fileSystem; this.server = server; @@ -186,13 +189,13 @@ public class JSONReport implements Reporter { json.endArray(); } - private static void writeJsonModuleComponents(JsonWriter json, Project module) { + private void writeJsonModuleComponents(JsonWriter json, DefaultInputModule module) { json .beginObject() - .prop("key", module.getEffectiveKey()) - .prop("path", module.getPath()) + .prop("key", module.key()) + .prop("path", moduleHierarchy.relativePath(module)) .endObject(); - for (Project subModule : module.getModules()) { + for (DefaultInputModule subModule : moduleHierarchy.children(module)) { writeJsonModuleComponents(json, subModule); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ResourceReport.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ResourceReport.java index e0bf1df43ca..fb6782b3d91 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ResourceReport.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/ResourceReport.java @@ -26,13 +26,19 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; + +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputDir; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputModule; +import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.rule.Rule; import org.sonar.api.rules.RulePriority; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.issue.tracking.TrackedIssue; public final class ResourceReport { - private final BatchComponent resource; + private final InputComponent component; private final IssueVariation total = new IssueVariation(); private final Map<ReportRuleKey, RuleReport> ruleReportByRuleKey = Maps.newHashMap(); @@ -42,24 +48,41 @@ public final class ResourceReport { private Map<Rule, AtomicInteger> issuesByRule = Maps.newHashMap(); private Map<RulePriority, AtomicInteger> issuesBySeverity = Maps.newHashMap(); - public ResourceReport(BatchComponent resource) { - this.resource = resource; + public ResourceReport(InputComponent component) { + this.component = component; } - public BatchComponent getResourceNode() { - return resource; + public InputComponent getResourceNode() { + return component; } public String getName() { - return resource.resource().getName(); + if (component instanceof InputPath) { + InputPath inputPath = (InputPath) component; + return inputPath.path().getFileName().toString(); + } else if (component instanceof InputModule) { + DefaultInputModule module = (DefaultInputModule) component; + return module.definition().getName(); + } + throw new IllegalStateException("Unknown component type: " + component.getClass()); } public String getKey() { - return resource.inputComponent().key(); + return component.key(); } + /** + * Must match one of the png in the resources, under org/scanner/scan/report/issuesreport_files + */ public String getType() { - return resource.resource().getScope(); + if (component instanceof InputFile) { + return "FIL"; + } else if (component instanceof InputDir) { + return "DIR"; + } else if (component instanceof InputModule) { + return "PRJ"; + } + throw new IllegalStateException("Unknown component type: " + component.getClass()); } public IssueVariation getTotal() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/SourceProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/SourceProvider.java index 6587e24933c..3bd94acd7c2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/SourceProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/SourceProvider.java @@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; -import org.sonar.scanner.index.BatchComponent; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; @ScannerSide public class SourceProvider { @@ -42,13 +42,13 @@ public class SourceProvider { this.fs = fs; } - public List<String> getEscapedSource(BatchComponent component) { + public List<String> getEscapedSource(DefaultInputComponent component) { if (!component.isFile()) { // Folder return Collections.emptyList(); } try { - InputFile inputFile = (InputFile) component.inputComponent(); + InputFile inputFile = (InputFile) component; List<String> lines = FileUtils.readLines(inputFile.file(), fs.encoding()); List<String> escapedLines = new ArrayList<>(lines.size()); for (String line : lines) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/DefaultBlameOutput.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/DefaultBlameOutput.java index 4cbb59f995f..cc56fadfd19 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/DefaultBlameOutput.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/DefaultBlameOutput.java @@ -31,12 +31,11 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.scm.BlameCommand.BlameOutput; import org.sonar.api.batch.scm.BlameLine; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Builder; import org.sonar.scanner.util.ProgressReport; @@ -50,15 +49,13 @@ class DefaultBlameOutput implements BlameOutput { private static final Pattern ACCENT_CODES = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); private final ScannerReportWriter writer; - private final BatchComponentCache componentCache; private final Set<InputFile> allFilesToBlame = new HashSet<>(); private ProgressReport progressReport; private int count; private int total; - DefaultBlameOutput(ScannerReportWriter writer, BatchComponentCache componentCache, List<InputFile> filesToBlame) { + DefaultBlameOutput(ScannerReportWriter writer, List<InputFile> filesToBlame) { this.writer = writer; - this.componentCache = componentCache; this.allFilesToBlame.addAll(filesToBlame); count = 0; total = filesToBlame.size(); @@ -77,9 +74,9 @@ class DefaultBlameOutput implements BlameOutput { return; } - BatchComponent batchComponent = componentCache.get(file); Builder scmBuilder = ScannerReport.Changesets.newBuilder(); - scmBuilder.setComponentRef(batchComponent.batchId()); + DefaultInputFile inputFile = (DefaultInputFile) file; + scmBuilder.setComponentRef(inputFile.batchId()); Map<String, Integer> changesetsIdByRevision = new HashMap<>(); int lineId = 1; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmSensor.java index 0783726ac94..3f00b5b5c58 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmSensor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmSensor.java @@ -27,13 +27,12 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Status; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Builder; import org.sonar.scanner.report.ReportPublisher; @@ -48,16 +47,14 @@ public final class ScmSensor implements Sensor { private final ScmConfiguration configuration; private final FileSystem fs; private final ProjectRepositories projectRepositories; - private final BatchComponentCache componentCache; private final ReportPublisher publishReportJob; public ScmSensor(ProjectDefinition projectDefinition, ScmConfiguration configuration, - ProjectRepositories projectRepositories, FileSystem fs, BatchComponentCache componentCache, ReportPublisher publishReportJob) { + ProjectRepositories projectRepositories, FileSystem fs, ReportPublisher publishReportJob) { this.projectDefinition = projectDefinition; this.configuration = configuration; this.projectRepositories = projectRepositories; this.fs = fs; - this.componentCache = componentCache; this.publishReportJob = publishReportJob; } @@ -81,7 +78,7 @@ public final class ScmSensor implements Sensor { if (!filesToBlame.isEmpty()) { String key = configuration.provider().key(); LOG.info("SCM provider for this project is: " + key); - DefaultBlameOutput output = new DefaultBlameOutput(publishReportJob.getWriter(), componentCache, filesToBlame); + DefaultBlameOutput output = new DefaultBlameOutput(publishReportJob.getWriter(), filesToBlame); try { configuration.provider().blameCommand().blame(new DefaultBlameInput(fs, filesToBlame), output); } catch (Exception e) { @@ -106,17 +103,16 @@ public final class ScmSensor implements Sensor { if (StringUtils.isEmpty(fileData.revision())) { addIfNotEmpty(filesToBlame, f); } else { - askToCopyDataFromPreviousAnalysis(f); + askToCopyDataFromPreviousAnalysis((DefaultInputFile) f); } } } return filesToBlame; } - private void askToCopyDataFromPreviousAnalysis(InputFile f) { - BatchComponent batchComponent = componentCache.get(f); + private void askToCopyDataFromPreviousAnalysis(DefaultInputFile f) { Builder scmBuilder = ScannerReport.Changesets.newBuilder(); - scmBuilder.setComponentRef(batchComponent.batchId()); + scmBuilder.setComponentRef(f.batchId()); scmBuilder.setCopyFromPrevious(true); publishReportJob.getWriter().writeComponentChangesets(scmBuilder.build()); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java index ec7b8cfe5b6..8600ec3fdab 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java @@ -23,6 +23,7 @@ import java.io.Serializable; import org.sonar.api.SonarRuntime; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.SensorContext; @@ -156,4 +157,9 @@ public class DefaultSensorContext implements SensorContext { public void addContextProperty(String key, String value) { sensorStorage.storeProperty(key, value); } + + @Override + public void markForPublishing(InputFile inputFile) { + + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java index 8beba97adae..051565781cd 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java @@ -33,6 +33,7 @@ import java.util.stream.Stream; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; @@ -54,7 +55,6 @@ import org.sonar.duplications.block.Block; import org.sonar.duplications.internal.pmd.PmdBlockChunker; import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.issue.ModuleIssues; import org.sonar.scanner.protocol.output.FileStructure; import org.sonar.scanner.protocol.output.ScannerReport; @@ -142,7 +142,6 @@ public class DefaultSensorStorage implements SensorStorage { private final MetricFinder metricFinder; private final ModuleIssues moduleIssues; private final CoverageExclusions coverageExclusions; - private final BatchComponentCache componentCache; private final ReportPublisher reportPublisher; private final MeasureCache measureCache; private final SonarCpdBlockIndex index; @@ -156,14 +155,13 @@ public class DefaultSensorStorage implements SensorStorage { public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, Settings settings, - CoverageExclusions coverageExclusions, BatchComponentCache componentCache, ReportPublisher reportPublisher, + CoverageExclusions coverageExclusions, ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index, ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics) { this.metricFinder = metricFinder; this.moduleIssues = moduleIssues; this.settings = settings; this.coverageExclusions = coverageExclusions; - this.componentCache = componentCache; this.reportPublisher = reportPublisher; this.measureCache = measureCache; this.index = index; @@ -352,8 +350,8 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(DefaultHighlighting highlighting) { ScannerReportWriter writer = reportPublisher.getWriter(); - InputFile inputFile = highlighting.inputFile(); - int componentRef = componentCache.get(inputFile).batchId(); + DefaultInputFile inputFile = (DefaultInputFile) highlighting.inputFile(); + int componentRef = inputFile.batchId(); if (writer.hasComponentData(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, componentRef)) { throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + inputFile.absolutePath()); } @@ -376,7 +374,8 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(DefaultSymbolTable symbolTable) { ScannerReportWriter writer = reportPublisher.getWriter(); - int componentRef = componentCache.get(symbolTable.inputFile()).batchId(); + DefaultInputFile inputFile = (DefaultInputFile) symbolTable.inputFile(); + int componentRef = inputFile.batchId(); if (writer.hasComponentData(FileStructure.Domain.SYMBOLS, componentRef)) { throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile().absolutePath()); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/CodeColorizerSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/CodeColorizerSensor.java index 3b5f2c5ec44..fb030a3d75a 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/CodeColorizerSensor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/CodeColorizerSensor.java @@ -22,10 +22,10 @@ package org.sonar.scanner.source; import org.sonar.api.batch.Phase; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.report.ReportPublisher; @@ -33,12 +33,10 @@ import org.sonar.scanner.report.ReportPublisher; public final class CodeColorizerSensor implements Sensor { private final ReportPublisher reportPublisher; - private final BatchComponentCache resourceCache; private final CodeColorizers codeColorizers; - public CodeColorizerSensor(ReportPublisher reportPublisher, BatchComponentCache resourceCache, CodeColorizers codeColorizers) { + public CodeColorizerSensor(ReportPublisher reportPublisher, CodeColorizers codeColorizers) { this.reportPublisher = reportPublisher; - this.resourceCache = resourceCache; this.codeColorizers = codeColorizers; } @@ -52,9 +50,9 @@ public final class CodeColorizerSensor implements Sensor { FileSystem fs = context.fileSystem(); for (InputFile f : fs.inputFiles(fs.predicates().all())) { ScannerReportReader reader = new ScannerReportReader(reportPublisher.getReportDir()); - int batchId = resourceCache.get(f).batchId(); + DefaultInputFile inputFile = (DefaultInputFile) f; String language = f.language(); - if (reader.hasSyntaxHighlighting(batchId) || language == null) { + if (reader.hasSyntaxHighlighting(inputFile.batchId()) || language == null) { continue; } codeColorizers.toSyntaxHighlighting(f.file(), fs.encoding(), language, context.newHighlighting().onFile(f)); |