diff options
author | Julien Lancelot <julien.lancelot@gmail.com> | 2012-12-03 09:16:04 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@gmail.com> | 2012-12-03 12:44:01 +0100 |
commit | 534b76c9e499860eca7003a6496f0e21484c976f (patch) | |
tree | 049ba785d53fd988d47dcc54b8bea82788e6cb3b /sonar-batch/src/main | |
parent | 852abadb6d99037d951f025e20b7a376c2f4a83e (diff) | |
download | sonarqube-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')
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; + } + +} |