From 8c2acfef07959a8e718246cc7ed0c4a79967b139 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Mon, 11 Feb 2013 17:58:56 +0100 Subject: [PATCH] SONAR-4053 Automatically release semaphores when outdated --- .../sonar/batch/bootstrap/ProjectLock.java | 14 +--- .../sonar/batch/bootstrap/TaskContainer.java | 2 + .../batch/bootstrap/ProjectLockTest.java | 23 +------ .../sonar/core/persistence/SemaphoreDao.java | 51 +++++++++----- .../core/persistence/SemaphoreMapper.java | 3 +- .../core/persistence/SemaphoreUpdater.java | 66 ++++++++++++++++++ .../core/persistence/SemaphoresImpl.java | 11 ++- .../core/persistence/SemaphoreMapper.xml | 10 ++- .../core/persistence/SemaphoreDaoTest.java | 23 ++++++- .../persistence/SemaphoreUpdaterTest.java | 67 +++++++++++++++++++ .../core/persistence/SemaphoresImplTest.java | 5 +- .../java/org/sonar/api/CoreProperties.java | 5 -- .../java/org/sonar/api/utils/Semaphores.java | 12 ++-- .../org/sonar/server/platform/Platform.java | 2 + 14 files changed, 226 insertions(+), 68 deletions(-) create mode 100644 sonar-core/src/main/java/org/sonar/core/persistence/SemaphoreUpdater.java create mode 100644 sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreUpdaterTest.java 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> 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} - - AND locked_at < #{lockedBefore} + + AND updated_at < #{updatedBefore} @@ -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} + + update semaphores + set updated_at = current_timestamp + where name=#{name} + + 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 @@ -333,11 +333,6 @@ public interface CoreProperties { */ String DRY_RUN = "sonar.dryRun"; - /** - * @since 3.4 - */ - String FORCE_ANALYSIS = "sonar.forceAnalysis"; - /** * @since 3.5 */ 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(); } -- 2.39.5