001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * SonarQube is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.batch.scan;
021    
022    import com.google.common.annotations.VisibleForTesting;
023    import org.sonar.api.BatchComponent;
024    import org.sonar.api.CoreProperties;
025    import org.sonar.api.batch.InstantiationStrategy;
026    import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
027    import org.sonar.api.batch.bootstrap.ProjectReactor;
028    import org.sonar.api.config.Settings;
029    import org.sonar.api.platform.ComponentContainer;
030    import org.sonar.api.resources.Languages;
031    import org.sonar.api.resources.Project;
032    import org.sonar.api.scan.filesystem.PathResolver;
033    import org.sonar.api.utils.SonarException;
034    import org.sonar.batch.DefaultFileLinesContextFactory;
035    import org.sonar.batch.DefaultResourceCreationLock;
036    import org.sonar.batch.ProjectConfigurator;
037    import org.sonar.batch.ProjectTree;
038    import org.sonar.batch.bootstrap.ExtensionInstaller;
039    import org.sonar.batch.bootstrap.ExtensionMatcher;
040    import org.sonar.batch.bootstrap.ExtensionUtils;
041    import org.sonar.batch.bootstrap.MetricProvider;
042    import org.sonar.batch.components.PeriodsDefinition;
043    import org.sonar.batch.debt.DebtModelProvider;
044    import org.sonar.batch.debt.IssueChangelogDebtCalculator;
045    import org.sonar.batch.duplication.BlockCache;
046    import org.sonar.batch.duplication.DuplicationCache;
047    import org.sonar.batch.index.Caches;
048    import org.sonar.batch.index.ComponentDataCache;
049    import org.sonar.batch.index.ComponentDataPersister;
050    import org.sonar.batch.index.DefaultIndex;
051    import org.sonar.batch.index.DefaultPersistenceManager;
052    import org.sonar.batch.index.DefaultResourcePersister;
053    import org.sonar.batch.index.DependencyPersister;
054    import org.sonar.batch.index.DuplicationPersister;
055    import org.sonar.batch.index.EventPersister;
056    import org.sonar.batch.index.LinkPersister;
057    import org.sonar.batch.index.MeasurePersister;
058    import org.sonar.batch.index.ResourceCache;
059    import org.sonar.batch.index.ResourceKeyMigration;
060    import org.sonar.batch.index.SnapshotCache;
061    import org.sonar.batch.index.SourcePersister;
062    import org.sonar.batch.issue.DefaultProjectIssues;
063    import org.sonar.batch.issue.DeprecatedViolations;
064    import org.sonar.batch.issue.IssueCache;
065    import org.sonar.batch.issue.IssuePersister;
066    import org.sonar.batch.issue.ScanIssueStorage;
067    import org.sonar.batch.languages.DefaultLanguagesReferential;
068    import org.sonar.batch.phases.GraphPersister;
069    import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
070    import org.sonar.batch.referential.ProjectReferentialsProvider;
071    import org.sonar.batch.rule.RulesProvider;
072    import org.sonar.batch.scan.filesystem.InputPathCache;
073    import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
074    import org.sonar.batch.scan.maven.MavenPluginExecutor;
075    import org.sonar.batch.scan.measure.MeasureCache;
076    import org.sonar.batch.source.HighlightableBuilder;
077    import org.sonar.batch.source.SymbolizableBuilder;
078    import org.sonar.core.component.ScanGraph;
079    import org.sonar.core.issue.IssueNotifications;
080    import org.sonar.core.issue.IssueUpdater;
081    import org.sonar.core.issue.workflow.FunctionExecutor;
082    import org.sonar.core.issue.workflow.IssueWorkflow;
083    import org.sonar.core.notification.DefaultNotificationManager;
084    import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel;
085    import org.sonar.core.test.TestPlanBuilder;
086    import org.sonar.core.test.TestPlanPerspectiveLoader;
087    import org.sonar.core.test.TestableBuilder;
088    import org.sonar.core.test.TestablePerspectiveLoader;
089    import org.sonar.core.user.DefaultUserFinder;
090    
091    public class ProjectScanContainer extends ComponentContainer {
092      public ProjectScanContainer(ComponentContainer taskContainer) {
093        super(taskContainer);
094      }
095    
096      @Override
097      protected void doBeforeStart() {
098        projectBootstrap();
099        addBatchComponents();
100        fixMavenExecutor();
101        addBatchExtensions();
102        Settings settings = getComponentByType(Settings.class);
103        if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) {
104          add(PhasesSumUpTimeProfiler.class);
105        }
106      }
107    
108      private void projectBootstrap() {
109        // Views pass a custom ProjectReactor
110        ProjectReactor reactor = getComponentByType(ProjectReactor.class);
111        if (reactor == null) {
112          // OK, not present, so look for a deprecated custom ProjectBootstrapper for old versions of SQ Runner
113          ProjectBootstrapper bootstrapper = getComponentByType(ProjectBootstrapper.class);
114          Settings settings = getComponentByType(Settings.class);
115          if (bootstrapper == null
116            // Starting from Maven plugin 2.3 then only DefaultProjectBootstrapper should be used.
117            || "true".equals(settings.getString("sonar.mojoUseRunner"))) {
118            // Use default SonarRunner project bootstrapper
119            ProjectReactorBuilder builder = getComponentByType(ProjectReactorBuilder.class);
120            reactor = builder.execute();
121          } else {
122            reactor = bootstrapper.bootstrap();
123          }
124          if (reactor == null) {
125            throw new SonarException(bootstrapper + " has returned null as ProjectReactor");
126          }
127          add(reactor);
128        }
129      }
130    
131      private void addBatchComponents() {
132        add(
133          new ProjectReferentialsProvider(),
134          DefaultResourceCreationLock.class,
135          DefaultPersistenceManager.class,
136          DependencyPersister.class,
137          EventPersister.class,
138          LinkPersister.class,
139          MeasurePersister.class,
140          DuplicationPersister.class,
141          DefaultResourcePersister.class,
142          SourcePersister.class,
143          DefaultNotificationManager.class,
144          MetricProvider.class,
145          ProjectConfigurator.class,
146          DefaultIndex.class,
147          ResourceKeyMigration.class,
148          DefaultFileLinesContextFactory.class,
149          ProjectLock.class,
150          LastSnapshots.class,
151          Caches.class,
152          SnapshotCache.class,
153          ResourceCache.class,
154          ComponentDataCache.class,
155          ComponentDataPersister.class,
156          DefaultUserFinder.class,
157    
158          // file system
159          InputPathCache.class,
160          PathResolver.class,
161    
162          // issues
163          IssueUpdater.class,
164          FunctionExecutor.class,
165          IssueWorkflow.class,
166          DeprecatedViolations.class,
167          IssueCache.class,
168          ScanIssueStorage.class,
169          IssuePersister.class,
170          IssueNotifications.class,
171          DefaultProjectIssues.class,
172          IssueChangelogDebtCalculator.class,
173    
174          // tests
175          TestPlanPerspectiveLoader.class,
176          TestablePerspectiveLoader.class,
177          TestPlanBuilder.class,
178          TestableBuilder.class,
179          ScanGraph.create(),
180          GraphPersister.class,
181    
182          // lang
183          Languages.class,
184          DefaultLanguagesReferential.class,
185          HighlightableBuilder.class,
186          SymbolizableBuilder.class,
187    
188          // technical debt
189          DefaultTechnicalDebtModel.class,
190    
191          // Differential periods
192          PeriodsDefinition.class,
193    
194          // Measures
195          MeasureCache.class,
196    
197          // Rules
198          new RulesProvider(),
199          new DebtModelProvider(),
200    
201          // Duplications
202          BlockCache.class,
203          DuplicationCache.class,
204    
205          ProjectSettings.class);
206      }
207    
208      private void fixMavenExecutor() {
209        if (getComponentByType(MavenPluginExecutor.class) == null) {
210          add(FakeMavenPluginExecutor.class);
211        }
212      }
213    
214      private void addBatchExtensions() {
215        getComponentByType(ExtensionInstaller.class).install(this, new BatchExtensionFilter());
216      }
217    
218      @Override
219      protected void doAfterStart() {
220        ProjectTree tree = getComponentByType(ProjectTree.class);
221        scanRecursively(tree.getRootProject());
222      }
223    
224      private void scanRecursively(Project module) {
225        for (Project subModules : module.getModules()) {
226          scanRecursively(subModules);
227        }
228        scan(module);
229      }
230    
231      @VisibleForTesting
232      void scan(Project module) {
233        new ModuleScanContainer(this, module).execute();
234      }
235    
236      static class BatchExtensionFilter implements ExtensionMatcher {
237        public boolean accept(Object extension) {
238          return ExtensionUtils.isType(extension, BatchComponent.class)
239            && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH);
240        }
241      }
242    }