aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2013-02-11 17:58:56 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2013-02-11 18:00:25 +0100
commit8c2acfef07959a8e718246cc7ed0c4a79967b139 (patch)
tree0caa10ff0e81cedd76418de1f52042d24bd7d029
parentaf5b5a9f2e10349c3190f744adfea9c4cd6e8308 (diff)
downloadsonarqube-8c2acfef07959a8e718246cc7ed0c4a79967b139.tar.gz
sonarqube-8c2acfef07959a8e718246cc7ed0c4a79967b139.zip
SONAR-4053 Automatically release semaphores when outdated
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java14
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java23
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreDao.java51
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreMapper.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreUpdater.java66
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/SemaphoresImpl.java11
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/SemaphoreMapper.xml10
-rw-r--r--sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java23
-rw-r--r--sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreUpdaterTest.java67
-rw-r--r--sonar-core/src/test/java/org/sonar/core/persistence/SemaphoresImplTest.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/Semaphores.java12
-rw-r--r--sonar-server/src/main/java/org/sonar/server/platform/Platform.java2
14 files changed, 226 insertions, 68 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java
index b0bd127b6d7..8a4d2148d6d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java
@@ -58,9 +58,7 @@ public class ProjectLock {
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'.";
+ return "It looks like an analysis of '" + getProject().getName() + "' is already running (started " + durationDisplay + ").";
}
public void stop() {
@@ -71,11 +69,7 @@ public class ProjectLock {
private Semaphores.Semaphore acquire() {
LOG.debug("Acquire semaphore on project : {}, with key {}", getProject(), getSemaphoreKey());
- if (shouldForce()) {
- // In force mode, we acquire the lock regardless there's a existing lock or not
- return semaphores.acquire(getSemaphoreKey(), 0);
- }
- return semaphores.acquire(getSemaphoreKey());
+ return semaphores.acquire(getSemaphoreKey(), 15, 10);
}
private void release() {
@@ -94,8 +88,4 @@ public class ProjectLock {
private boolean isInDryRunMode() {
return settings.getBoolean(CoreProperties.DRY_RUN);
}
-
- private boolean shouldForce() {
- return settings.getBoolean(CoreProperties.FORCE_ANALYSIS);
- }
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
index 3b754d15b34..83fe47a2b02 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
@@ -58,6 +58,7 @@ import org.sonar.core.notification.DefaultNotificationManager;
import org.sonar.core.persistence.DaoUtils;
import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.persistence.SemaphoreUpdater;
import org.sonar.core.persistence.SemaphoresImpl;
import org.sonar.core.resource.DefaultResourcePermissions;
import org.sonar.core.rule.CacheRuleFinder;
@@ -104,6 +105,7 @@ public class TaskContainer extends Container {
container.addSingleton(CacheMetricFinder.class);
container.addSingleton(DefaultUserFinder.class);
container.addSingleton(ResourceTypes.class);
+ container.addSingleton(SemaphoreUpdater.class);
container.addSingleton(SemaphoresImpl.class);
container.addSingleton(PastSnapshotFinderByDate.class);
container.addSingleton(PastSnapshotFinderByDays.class);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java
index f47061a7c0b..9172cd2796a 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java
@@ -28,13 +28,9 @@ import org.sonar.api.utils.Semaphores;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.ProjectTree;
-import java.util.Date;
-
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -56,7 +52,6 @@ public class ProjectLockTest {
projectTree = mock(ProjectTree.class);
settings = new Settings();
setDryRunMode(false);
- setForceMode(false);
project = new Project("my-project-key");
when(projectTree.getRootProject()).thenReturn(project);
@@ -65,23 +60,15 @@ public class ProjectLockTest {
@Test
public void shouldAcquireSemaphore() {
- when(semaphores.acquire(anyString())).thenReturn(new Semaphores.Semaphore().setLocked(true));
+ when(semaphores.acquire(anyString(), anyInt(), anyInt())).thenReturn(new Semaphores.Semaphore().setLocked(true));
projectLock.start();
- verify(semaphores).acquire("batch-my-project-key");
- }
-
- @Test
- public void shouldAcquireSemaphoreIfForceAnalyseActivated() {
- setForceMode(true);
- when(semaphores.acquire("batch-my-project-key", 0)).thenReturn(new Semaphores.Semaphore().setLocked(true));
-
- projectLock.start();
+ verify(semaphores).acquire("batch-my-project-key", 15, 10);
}
@Test(expected = SonarException.class)
public void shouldNotAcquireSemaphoreIfTheProjectIsAlreadyBeenAnalysing() {
- when(semaphores.acquire(anyString())).thenReturn(new Semaphores.Semaphore().setLocked(false).setDurationSinceLocked(1234L));
+ when(semaphores.acquire(anyString(), anyInt(), anyInt())).thenReturn(new Semaphores.Semaphore().setLocked(false).setDurationSinceLocked(1234L));
projectLock.start();
}
@@ -110,8 +97,4 @@ public class ProjectLockTest {
settings.setProperty(CoreProperties.DRY_RUN, isInDryRunMode);
}
- private void setForceMode(boolean isInForcedMode) {
- settings.setProperty(CoreProperties.FORCE_ANALYSIS, isInForcedMode);
- }
-
}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreDao.java b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreDao.java
index 3e36545012b..c2465fa81b7 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreDao.java
@@ -38,24 +38,28 @@ public class SemaphoreDao {
this.mybatis = mybatis;
}
- public Semaphores.Semaphore acquire(String name, int maxDurationInSeconds) {
+ public synchronized Semaphores.Semaphore acquire(String name, int maxAgeInSeconds) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Semaphore name must not be empty");
- Preconditions.checkArgument(maxDurationInSeconds >= 0, "Semaphore max duration must be positive: " + maxDurationInSeconds);
+ Preconditions.checkArgument(maxAgeInSeconds >= 0, "Semaphore max age must be positive: " + maxAgeInSeconds);
SqlSession session = mybatis.openSession();
try {
SemaphoreMapper mapper = session.getMapper(SemaphoreMapper.class);
- Date lockedAt = org.sonar.api.utils.DateUtils.parseDate("2001-01-01");
- createDto(name, lockedAt, session);
- boolean isAcquired = doAcquire(name, maxDurationInSeconds, session, mapper);
SemaphoreDto semaphore = selectSemaphore(name, session);
- return createLock(semaphore, session, isAcquired);
+ Date now = mapper.now();
+ if (semaphore != null) {
+ boolean isAcquired = acquireIfOutdated(name, maxAgeInSeconds, session, mapper);
+ return createLock(semaphore, session, isAcquired);
+ } else {
+ semaphore = createDto(name, now, session);
+ return createLock(semaphore, session, true);
+ }
} finally {
MyBatis.closeQuietly(session);
}
}
- public Semaphores.Semaphore acquire(String name) {
+ public synchronized Semaphores.Semaphore acquire(String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Semaphore name must not be empty");
SqlSession session = mybatis.openSession();
@@ -74,6 +78,19 @@ public class SemaphoreDao {
}
}
+ public void update(Semaphores.Semaphore semaphore) {
+ Preconditions.checkArgument(semaphore != null, "Semaphore must not be null");
+
+ SqlSession session = mybatis.openSession();
+ try {
+ SemaphoreMapper mapper = session.getMapper(SemaphoreMapper.class);
+ mapper.update(semaphore.getName());
+ session.commit();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
public void release(String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Semaphore name must not be empty");
SqlSession session = mybatis.openSession();
@@ -85,9 +102,9 @@ public class SemaphoreDao {
}
}
- private boolean doAcquire(String name, int durationInSeconds, SqlSession session, SemaphoreMapper mapper) {
- Date lockedBefore = DateUtils.addSeconds(mapper.now(), -durationInSeconds);
- boolean ok = mapper.acquire(name, lockedBefore) == 1;
+ private boolean acquireIfOutdated(String name, int maxAgeInSeconds, SqlSession session, SemaphoreMapper mapper) {
+ Date updatedBefore = DateUtils.addSeconds(mapper.now(), -maxAgeInSeconds);
+ boolean ok = mapper.acquire(name, updatedBefore) == 1;
session.commit();
return ok;
}
@@ -96,8 +113,8 @@ public class SemaphoreDao {
try {
SemaphoreMapper mapper = session.getMapper(SemaphoreMapper.class);
SemaphoreDto semaphore = new SemaphoreDto()
- .setName(name)
- .setLockedAt(lockedAt);
+ .setName(name)
+ .setLockedAt(lockedAt);
mapper.initialize(semaphore);
session.commit();
return semaphore;
@@ -110,11 +127,11 @@ public class SemaphoreDao {
private Semaphores.Semaphore createLock(SemaphoreDto dto, SqlSession session, boolean acquired) {
Semaphores.Semaphore semaphore = new Semaphores.Semaphore()
- .setName(dto.getName())
- .setLocked(acquired)
- .setLocketAt(dto.getLockedAt())
- .setCreatedAt(dto.getCreatedAt())
- .setUpdatedAt(dto.getUpdatedAt());
+ .setName(dto.getName())
+ .setLocked(acquired)
+ .setLocketAt(dto.getLockedAt())
+ .setCreatedAt(dto.getCreatedAt())
+ .setUpdatedAt(dto.getUpdatedAt());
if (!acquired) {
semaphore.setDurationSinceLocked(getDurationSinceLocked(dto, session));
}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreMapper.java b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreMapper.java
index 566708e0c0a..f9594e66709 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreMapper.java
@@ -27,7 +27,7 @@ public interface SemaphoreMapper {
int initialize(SemaphoreDto semaphore);
- int acquire(@Param("name") String name, @Param("lockedBefore") Date lockedBefore);
+ int acquire(@Param("name") String name, @Param("updatedBefore") Date updatedBefore);
Date now();
@@ -35,4 +35,5 @@ public interface SemaphoreMapper {
SemaphoreDto selectSemaphore(@Param("name") String name);
+ void update(String name);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreUpdater.java b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreUpdater.java
new file mode 100644
index 00000000000..9001f88d706
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreUpdater.java
@@ -0,0 +1,66 @@
+/*
+ * 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.core.persistence;
+
+import com.google.common.collect.Maps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.Semaphores;
+
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @since 3.5
+ */
+public class SemaphoreUpdater {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SemaphoreUpdater.class);
+
+ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+
+ private SemaphoreDao dao;
+
+ private Map<String, ScheduledFuture<?>> handlers = Maps.newHashMap();
+
+ public SemaphoreUpdater(SemaphoreDao dao) {
+ this.dao = dao;
+ }
+
+ public void scheduleForUpdate(final Semaphores.Semaphore semaphore, int updatePeriodInSeconds) {
+ final Runnable updater = new Runnable() {
+ public void run() {
+ LOG.debug("Updating semaphore " + semaphore.getName());
+ dao.update(semaphore);
+ }
+ };
+ final ScheduledFuture<?> updateHandle = scheduler.scheduleWithFixedDelay(updater, updatePeriodInSeconds, updatePeriodInSeconds, TimeUnit.SECONDS);
+ handlers.put(semaphore.getName(), updateHandle);
+ }
+
+ public void stopUpdate(final String name) {
+ if (handlers.containsKey(name)) {
+ handlers.get(name).cancel(false);
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoresImpl.java b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoresImpl.java
index b69cff90448..da92972c450 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoresImpl.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/SemaphoresImpl.java
@@ -27,13 +27,17 @@ import org.sonar.api.utils.Semaphores;
public class SemaphoresImpl implements Semaphores {
private SemaphoreDao dao;
+ private SemaphoreUpdater updater;
- public SemaphoresImpl(SemaphoreDao dao) {
+ public SemaphoresImpl(SemaphoreDao dao, SemaphoreUpdater updater) {
this.dao = dao;
+ this.updater = updater;
}
- public Semaphore acquire(String name, int maxDurationInSeconds) {
- return dao.acquire(name, maxDurationInSeconds);
+ public Semaphore acquire(String name, int maxAgeInSeconds, int updatePeriodInSeconds) {
+ Semaphore semaphore = dao.acquire(name, maxAgeInSeconds);
+ updater.scheduleForUpdate(semaphore, updatePeriodInSeconds);
+ return semaphore;
}
public Semaphore acquire(String name) {
@@ -41,6 +45,7 @@ public class SemaphoresImpl implements Semaphores {
}
public void release(String name) {
+ updater.stopUpdate(name);
dao.release(name);
}
}
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/SemaphoreMapper.xml b/sonar-core/src/main/resources/org/sonar/core/persistence/SemaphoreMapper.xml
index d1a8e6f48e2..4b9fbb134f4 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/SemaphoreMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/SemaphoreMapper.xml
@@ -28,8 +28,8 @@
update semaphores
set updated_at = current_timestamp, locked_at = current_timestamp
where name=#{name}
- <if test="lockedBefore != null">
- AND locked_at &lt; #{lockedBefore}
+ <if test="updatedBefore != null">
+ AND updated_at &lt; #{updatedBefore}
</if>
</update>
@@ -41,5 +41,11 @@
select s.id, s.name as name, s.locked_at as lockedAt, s.created_at as createdAt, s.updated_at as updatedAt from semaphores s where s.name=#{name}
</select>
+ <update id="update" parameterType="String">
+ update semaphores
+ set updated_at = current_timestamp
+ where name=#{name}
+ </update>
+
</mapper>
diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java
index ccfcb6336c3..d9e713bdde9 100644
--- a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java
@@ -58,7 +58,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase {
@Test
public void should_fail_to_acquire_if_negative_timeout() {
thrown.expect(IllegalArgumentException.class);
- thrown.expectMessage("Semaphore max duration must be positive: -5000");
+ thrown.expectMessage("Semaphore max age must be positive: -5000");
SemaphoreDao dao = new SemaphoreDao(getMyBatis());
dao.acquire("foo", -5000);
@@ -91,7 +91,25 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase {
}
@Test
- public void create_and_acquire_semaphore_when_timeout_is_zeo() throws Exception {
+ public void create_and_acquire_and_update_semaphore() throws Exception {
+ Semaphores.Semaphore lock = dao.acquire("foo", 60);
+ assertThat(lock.isLocked()).isTrue();
+ assertThat(lock.getDurationSinceLocked()).isNull();
+
+ SemaphoreDto semaphore = selectSemaphore("foo");
+ assertThat(semaphore.getCreatedAt().getTime()).isEqualTo(semaphore.getUpdatedAt().getTime());
+
+ dao.update(lock);
+
+ semaphore = selectSemaphore("foo");
+ assertThat(semaphore.getCreatedAt().getTime()).isLessThan(semaphore.getUpdatedAt().getTime());
+
+ dao.release("foo");
+ assertThat(selectSemaphore("foo")).isNull();
+ }
+
+ @Test
+ public void create_and_acquire_semaphore_when_maxage_is_zeo() throws Exception {
Semaphores.Semaphore lock = dao.acquire("foo", 0);
assertThat(lock.isLocked()).isTrue();
assertThat(lock.getDurationSinceLocked()).isNull();
@@ -255,6 +273,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase {
this.barrier = barrier;
}
+ @Override
public void run() {
try {
barrier.await();
diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreUpdaterTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreUpdaterTest.java
new file mode 100644
index 00000000000..dac8bf7919c
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreUpdaterTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.core.persistence;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.Semaphores;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+public class SemaphoreUpdaterTest extends AbstractDaoTestCase {
+
+ private SemaphoreUpdater updater;
+ private SemaphoreDao dao;
+
+ @Before
+ public void before() {
+ dao = mock(SemaphoreDao.class);
+ updater = new SemaphoreUpdater(dao);
+ }
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testScheduleUpdate() throws Exception {
+ Semaphores.Semaphore semaphore = new Semaphores.Semaphore().setName("foo");
+ updater.scheduleForUpdate(semaphore, 1);
+
+ Thread.sleep(1000);
+
+ verify(dao).update(semaphore);
+ }
+
+ @Test
+ public void testCancelUpdate() throws Exception {
+ Semaphores.Semaphore semaphore = new Semaphores.Semaphore().setName("foo");
+ updater.scheduleForUpdate(semaphore, 1);
+ updater.stopUpdate("foo");
+
+ Thread.sleep(1000);
+
+ verify(dao, never()).update(semaphore);
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoresImplTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoresImplTest.java
index a09b49a3048..cf50826de26 100644
--- a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoresImplTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoresImplTest.java
@@ -33,12 +33,13 @@ public class SemaphoresImplTest {
@Test
public void should_be_a_bridge_over_dao() {
SemaphoreDao dao = mock(SemaphoreDao.class);
+ SemaphoreUpdater updater = mock(SemaphoreUpdater.class);
Semaphores.Semaphore semaphore = new Semaphores.Semaphore();
when(dao.acquire(anyString(), anyInt())).thenReturn(semaphore);
- SemaphoresImpl impl = new SemaphoresImpl(dao);
+ SemaphoresImpl impl = new SemaphoresImpl(dao, updater);
- impl.acquire("do-xxx", 50000);
+ impl.acquire("do-xxx", 50000, 10);
verify(dao).acquire("do-xxx", 50000);
impl.acquire("do-xxx");
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
index 47027d2108c..67d20ac2f59 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
@@ -334,11 +334,6 @@ public interface CoreProperties {
String DRY_RUN = "sonar.dryRun";
/**
- * @since 3.4
- */
- String FORCE_ANALYSIS = "sonar.forceAnalysis";
-
- /**
* @since 3.5
*/
String TASK = "sonar.task";
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Semaphores.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Semaphores.java
index 1b3a7116fb2..d977250d8ec 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Semaphores.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Semaphores.java
@@ -35,19 +35,23 @@ public interface Semaphores extends BatchComponent, ServerComponent {
/**
* Try to acquire a semaphore for a given duration.
* The semaphore is acquired if it's unlocked or if the max locking duration is reached.
+ * When the lock is acquired there will be a periodic ping of the
+ * server to update the semaphore and avoid it to be considered as
+ * outdated.
*
* @param name the key of the semaphore
- * @param maxDurationInSeconds the max duration in seconds the semaphore will be acquired. The value zero forces the semaphore to be acquired, whatever its status.
+ * @param maxAgeInSeconds the max duration in seconds the semaphore will be considered unlocked if it was not updated. The value zero forces the semaphore to be acquired, whatever its status.
+ * @param updatePeriodInSeconds the period in seconds the semaphore will be updated.
* @return the semaphore, whatever its status (locked or unlocked). Can't be null.
*/
- Semaphore acquire(String name, int maxDurationInSeconds);
+ Semaphore acquire(String name, int maxAgeInSeconds, int updatePeriodInSeconds);
/**
* Try to acquire a semaphore.
- * The lock will be acquired only if there's no existing lock.
+ * The semaphore will be acquired only if there's no existing lock.
*
* @param name the key of the semaphore
- * @return a lock containing information if the lock could be acquired or not, the duration since locked, etc.
+ * @return the semaphore, whatever its status (locked or unlocked). Can't be null.
*/
Semaphore acquire(String name);
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
index 6d1a852c9a6..fee24b7f1eb 100644
--- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
+++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
@@ -52,6 +52,7 @@ import org.sonar.core.persistence.DatabaseVersion;
import org.sonar.core.persistence.DefaultDatabase;
import org.sonar.core.persistence.DryRunDatabaseFactory;
import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.persistence.SemaphoreUpdater;
import org.sonar.core.persistence.SemaphoresImpl;
import org.sonar.core.qualitymodel.DefaultModelFinder;
import org.sonar.core.resource.DefaultResourcePermissions;
@@ -188,6 +189,7 @@ public final class Platform {
rootContainer.addSingleton(RuleI18nManager.class);
rootContainer.addSingleton(GwtI18n.class);
rootContainer.addSingleton(DryRunDatabaseFactory.class);
+ rootContainer.addSingleton(SemaphoreUpdater.class);
rootContainer.addSingleton(SemaphoresImpl.class);
rootContainer.startComponents();
}