import org.sonar.api.measures.Metric;
import org.sonar.api.measures.PropertiesBuilder;
import org.sonar.api.utils.DateUtils;
+import org.sonar.batch.util.ProgressReport;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
class DefaultBlameOutput implements BlameOutput {
private final SensorContext context;
private final Set<InputFile> allFilesToBlame = new HashSet<InputFile>();
+ private ProgressReport progressReport;
+ private int count;
+ private int total;
DefaultBlameOutput(SensorContext context, List<InputFile> filesToBlame) {
this.context = context;
this.allFilesToBlame.addAll(filesToBlame);
+ count = 0;
+ total = filesToBlame.size();
+ progressReport = new ProgressReport("Report about progress of SCM blame", TimeUnit.SECONDS.toMillis(10));
+ progressReport.start(total + " files to be analyzed");
}
@Override
- public void blameResult(InputFile file, List<BlameLine> lines) {
+ public synchronized void blameResult(InputFile file, List<BlameLine> lines) {
Preconditions.checkNotNull(file);
Preconditions.checkNotNull(lines);
Preconditions.checkArgument(allFilesToBlame.contains(file), "It was not expected to blame file " + file.relativePath());
}
ScmSensor.saveMeasures(context, file, authors.buildData(), dates.buildData(), revisions.buildData());
allFilesToBlame.remove(file);
+ count++;
+ progressReport.message(count + "/" + total + " files analyzed, last one was " + file.absolutePath());
}
private String normalizeString(@Nullable String inputString) {
return new PropertiesBuilder<Integer, String>(metric);
}
- public Set<InputFile> remainingFiles() {
- return allFilesToBlame;
+ public void finish() {
+ if (!allFilesToBlame.isEmpty()) {
+ throw new IllegalStateException("Some files were not blamed");
+ }
+ progressReport.stop(count + "/" + count + " files analyzed");
}
}
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.batch.scm.ScmProvider;
import org.sonar.api.config.Settings;
+import org.sonar.batch.phases.Phases;
import org.sonar.core.DryRunIncompatible;
+import javax.annotation.Nullable;
+
import java.util.LinkedHashMap;
import java.util.Map;
private final ProjectReactor projectReactor;
private final Settings settings;
private final Map<String, ScmProvider> providerPerKey = new LinkedHashMap<String, ScmProvider>();
+ private final Phases phases;
private ScmProvider provider;
- public ScmConfiguration(ProjectReactor projectReactor, Settings settings, ScmProvider... providers) {
+ public ScmConfiguration(ProjectReactor projectReactor, Settings settings, @Nullable Phases phases, ScmProvider... providers) {
this.projectReactor = projectReactor;
this.settings = settings;
+ this.phases = phases;
for (ScmProvider scmProvider : providers) {
providerPerKey.put(scmProvider.key(), scmProvider);
}
}
+ // Scan 2
+ public ScmConfiguration(ProjectReactor projectReactor, Settings settings, ScmProvider... providers) {
+ this(projectReactor, settings, null, providers);
+ }
+
+ public ScmConfiguration(ProjectReactor projectReactor, Settings settings, Phases phases) {
+ this(projectReactor, settings, phases, new ScmProvider[0]);
+ }
+
+ // Scan2
public ScmConfiguration(ProjectReactor projectReactor, Settings settings) {
- this(projectReactor, settings, new ScmProvider[0]);
+ this(projectReactor, settings, null, new ScmProvider[0]);
}
@Override
public void start() {
+ if (phases != null && !phases.isEnabled(Phases.Phase.SENSOR)) {
+ return;
+ }
if (isDisabled()) {
LOG.debug("SCM Step is disabled by configuration");
return;
TimeProfiler profiler = new TimeProfiler().start("Retrieve SCM blame information");
DefaultBlameOutput output = new DefaultBlameOutput(context, filesToBlame);
configuration.provider().blameCommand().blame(new DefaultBlameInput(fs, filesToBlame), output);
- if (!output.remainingFiles().isEmpty()) {
- throw new IllegalStateException("Some files were not blamed");
- }
+ output.finish();
profiler.stop();
}
}
}
}
- /**
- * This method is synchronized since it is allowed for plugins to compute blame in parallel.
- */
- static synchronized void saveMeasures(SensorContext context, InputFile f, String scmAuthorsByLine, String scmLastCommitDatetimesByLine, String scmRevisionsByLine) {
+ static void saveMeasures(SensorContext context, InputFile f, String scmAuthorsByLine, String scmLastCommitDatetimesByLine, String scmRevisionsByLine) {
((DefaultMeasure<String>) context.<String>newMeasure()
.onFile(f)
.forMetric(CoreMetrics.SCM_AUTHORS_BY_LINE)
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProgressReport implements Runnable {
+
+ private final long period;
+ private final Logger logger;
+ private String message = "";
+ private final Thread thread;
+ private String stopMessage = "";
+
+ public ProgressReport(String threadName, long period, Logger logger) {
+ this.period = period;
+ this.logger = logger;
+ thread = new Thread(this);
+ thread.setName(threadName);
+ }
+
+ public ProgressReport(String threadName, long period) {
+ this(threadName, period, LoggerFactory.getLogger(ProgressReport.class));
+ }
+
+ @Override
+ public void run() {
+ while (!Thread.interrupted()) {
+ try {
+ Thread.sleep(period);
+ log(message);
+ } catch (InterruptedException e) {
+ thread.interrupt();
+ }
+ }
+ log(stopMessage);
+ }
+
+ public void start(String startMessage) {
+ log(startMessage);
+ thread.start();
+ }
+
+ public void message(String message) {
+ this.message = message;
+ }
+
+ public void stop(String stopMessage) {
+ this.stopMessage = stopMessage;
+ thread.interrupt();
+ }
+
+ public void join() throws InterruptedException {
+ thread.join();
+ }
+
+ private void log(String message) {
+ synchronized (logger) {
+ logger.info(message);
+ logger.notifyAll();
+ }
+ }
+
+}