aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch/src/main
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@gmail.com>2012-12-03 09:16:04 +0100
committerJulien Lancelot <julien.lancelot@gmail.com>2012-12-03 12:44:01 +0100
commit534b76c9e499860eca7003a6496f0e21484c976f (patch)
tree049ba785d53fd988d47dcc54b8bea82788e6cb3b /sonar-batch/src/main
parent852abadb6d99037d951f025e20b7a376c2f4a83e (diff)
downloadsonarqube-534b76c9e499860eca7003a6496f0e21484c976f.tar.gz
sonarqube-534b76c9e499860eca7003a6496f0e21484c976f.zip
SONAR-3306 Use a semaphore to prevent launching several analysis of the same project at the same time
Diffstat (limited to 'sonar-batch/src/main')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java1
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/CheckSemaphore.java101
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/DurationLabel.java139
3 files changed, 241 insertions, 0 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java
index de3fdcfffb0..949030df4f6 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java
@@ -103,6 +103,7 @@ public class BatchModule extends Module {
container.addSingleton(DefaultUserFinder.class);
container.addSingleton(ResourceTypes.class);
container.addSingleton(MetricProvider.class);
+ container.addSingleton(CheckSemaphore.class);
}
private void registerDatabaseComponents() {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/CheckSemaphore.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/CheckSemaphore.java
new file mode 100644
index 00000000000..c3f7a6086e4
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/CheckSemaphore.java
@@ -0,0 +1,101 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.bootstrap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.SonarException;
+import org.sonar.batch.ProjectTree;
+import org.sonar.core.persistence.Lock;
+import org.sonar.core.persistence.SemaphoreDao;
+
+public class CheckSemaphore {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CheckSemaphore.class);
+
+ private final SemaphoreDao semaphoreDao;
+ private final ProjectTree projectTree;
+ private final Settings settings;
+
+ public CheckSemaphore(SemaphoreDao semaphoreDao, ProjectTree projectTree, Settings settings) {
+ this.semaphoreDao = semaphoreDao;
+ this.projectTree = projectTree;
+ this.settings = settings;
+ }
+
+ public void start() {
+ if (!isInDryRunMode()) {
+ Lock lock = acquire();
+ if (!lock.isAcquired()) {
+ LOG.error(getErrorMessage(lock));
+ throw new SonarException("The project is already been analysing.");
+ }
+ }
+ }
+
+ private String getErrorMessage(Lock lock) {
+ long duration = lock.getDurationSinceLocked();
+ DurationLabel durationLabel = new DurationLabel();
+ String durationDisplay = durationLabel.label(duration);
+
+ return "It looks like an analysis of '"+ getProject().getName() +"' is already running (started "+ durationDisplay +"). " +
+ "If this is not the case, it probably means that previous analysis was interrupted " +
+ "and you should then force a re-run by using the option '"+ CoreProperties.FORCE_ANALYSIS +"=true'.";
+ }
+
+ public void stop() {
+ if (!isInDryRunMode()) {
+ release();
+ }
+ }
+
+ private Lock acquire() {
+ LOG.debug("Acquire semaphore on project : {}", getProject());
+ if (!isForceAnalyseActivated()) {
+ return semaphoreDao.acquire(getSemaphoreKey());
+ } else {
+ return semaphoreDao.acquire(getSemaphoreKey(), 0);
+ }
+ }
+
+ private void release() {
+ LOG.debug("Release semaphore on project : {}", getProject());
+ semaphoreDao.release(getSemaphoreKey());
+ }
+
+ private String getSemaphoreKey() {
+ return "batch-" + getProject().getKey();
+ }
+
+ private Project getProject() {
+ return projectTree.getRootProject();
+ }
+
+ private boolean isInDryRunMode() {
+ return settings.getBoolean(CoreProperties.DRY_RUN);
+ }
+
+ private boolean isForceAnalyseActivated() {
+ return settings.getBoolean(CoreProperties.FORCE_ANALYSIS);
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DurationLabel.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DurationLabel.java
new file mode 100644
index 00000000000..bc3263fa472
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DurationLabel.java
@@ -0,0 +1,139 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.bootstrap;
+
+import java.text.MessageFormat;
+
+public class DurationLabel {
+
+ private String prefixAgo = null;
+ private String suffixAgo = "ago";
+ private String seconds = "less than a minute";
+ private String minute = "about a minute";
+ private String minutes = "{0} minutes";
+ private String hour = "about an hour";
+ private String hours = "{0} hours";
+ private String day = "a day";
+ private String days = "{0} days";
+ private String month = "about a month";
+ private String months = "{0} months";
+ private String year = "about a year";
+ private String years = "{0} years";
+
+ public String label(long durationInMillis) {
+ double seconds = durationInMillis / 1000;
+ double minutes = seconds / 60;
+ double hours = minutes / 60;
+ double days = hours / 24;
+ double years = days / 365;
+
+ final String time;
+ if (seconds < 45) {
+ time = this.seconds;
+ } else if (seconds < 90) {
+ time = this.minute;
+ } else if (minutes < 45) {
+ time = MessageFormat.format(this.minutes, Math.round(minutes));
+ } else if (minutes < 90) {
+ time = this.hour;
+ } else if (hours < 24) {
+ time = MessageFormat.format(this.hours, Math.round(hours));
+ } else if (hours < 48) {
+ time = this.day;
+ } else if (days < 30) {
+ time = MessageFormat.format(this.days, Math.floor(days));
+ } else if (days < 60) {
+ time = this.month;
+ } else if (days < 365) {
+ time = MessageFormat.format(this.months, Math.floor(days / 30));
+ } else if (years < 2) {
+ time = this.year;
+ } else {
+ time = MessageFormat.format(this.years, Math.floor(years));
+ }
+
+ return join(prefixAgo, time, suffixAgo);
+ }
+
+ public String join(String prefix, String time, String suffix) {
+ StringBuilder joined = new StringBuilder();
+ if (prefix != null && prefix.length() > 0) {
+ joined.append(prefix).append(' ');
+ }
+ joined.append(time);
+ if (suffix != null && suffix.length() > 0) {
+ joined.append(' ').append(suffix);
+ }
+ return joined.toString();
+ }
+
+ public String getPrefixAgo() {
+ return prefixAgo;
+ }
+
+ public String getSuffixAgo() {
+ return suffixAgo;
+ }
+
+ public String getSeconds() {
+ return seconds;
+ }
+
+ public String getMinute() {
+ return minute;
+ }
+
+ public String getMinutes() {
+ return minutes;
+ }
+
+ public String getHour() {
+ return hour;
+ }
+
+ public String getHours() {
+ return hours;
+ }
+
+ public String getDay() {
+ return day;
+ }
+
+ public String getDays() {
+ return days;
+ }
+
+ public String getMonth() {
+ return month;
+ }
+
+ public String getMonths() {
+ return months;
+ }
+
+ public String getYear() {
+ return year;
+ }
+
+ public String getYears() {
+ return years;
+ }
+
+}