From 03fd91021257124849677eb12e6b3330c5755c6d Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 19 Oct 2012 11:02:02 +0200 Subject: [PATCH] SONAR-3887 add org.sonar.api.utils.DatabaseSemaphore --- .../org/sonar/plugins/core/CorePlugin.java | 3 +- .../plugins/core/DatabaseSemaphoreImpl.java | 43 +++++++++++++++++++ .../core/DatabaseSemaphoreImplTest.java | 41 ++++++++++++++++++ .../sonar/core/persistence/SemaphoreDao.java | 14 +++--- .../core/persistence/SemaphoreMapper.java | 4 +- .../core/persistence/SemaphoreMapper.xml | 4 +- .../core/persistence/SemaphoreDaoTest.java | 16 +++---- .../sonar/api/utils/DatabaseSemaphore.java | 36 ++++++++++++++++ 8 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DatabaseSemaphoreImpl.java create mode 100644 plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/DatabaseSemaphoreImplTest.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/utils/DatabaseSemaphore.java diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 074d8d3873e..cce3cd6870c 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -374,11 +374,12 @@ import java.util.List; public final class CorePlugin extends SonarPlugin { @SuppressWarnings("unchecked") - public List> getExtensions() { + public List getExtensions() { return ImmutableList.of( DefaultResourceTypes.class, UserManagedMetrics.class, ProjectFileSystemLogger.class, + DatabaseSemaphoreImpl.class, // maven MavenInitializer.class, diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DatabaseSemaphoreImpl.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DatabaseSemaphoreImpl.java new file mode 100644 index 00000000000..5c992b4ed35 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/DatabaseSemaphoreImpl.java @@ -0,0 +1,43 @@ +/* + * 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.plugins.core; + +import org.sonar.api.utils.DatabaseSemaphore; +import org.sonar.core.persistence.SemaphoreDao; + +/** + * @since 3.4 + */ +public class DatabaseSemaphoreImpl implements DatabaseSemaphore { + + private SemaphoreDao dao; + + public DatabaseSemaphoreImpl(SemaphoreDao dao) { + this.dao = dao; + } + + public boolean acquire(String name, int maxDurationInSeconds) { + return dao.acquire(name, maxDurationInSeconds); + } + + public void release(String name) { + dao.release(name); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/DatabaseSemaphoreImplTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/DatabaseSemaphoreImplTest.java new file mode 100644 index 00000000000..c640159ba67 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/DatabaseSemaphoreImplTest.java @@ -0,0 +1,41 @@ +/* + * 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.plugins.core; + +import org.junit.Test; +import org.sonar.core.persistence.SemaphoreDao; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class DatabaseSemaphoreImplTest { + + @Test + public void should_be_a_bridge_over_dao() { + SemaphoreDao dao = mock(SemaphoreDao.class); + DatabaseSemaphoreImpl impl = new DatabaseSemaphoreImpl(dao); + + impl.acquire("do-xxx", 50000); + verify(dao).acquire("do-xxx", 50000); + + impl.release("do-xxx"); + verify(dao).release("do-xxx"); + } +} 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 2519823fadc..fd92ccc3a0f 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 @@ -37,34 +37,34 @@ public class SemaphoreDao { this.mybatis = mybatis; } - public boolean lock(String name, int durationInSeconds) { + public boolean acquire(String name, int maxDurationInSeconds) { Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Semaphore name must not be empty"); - Preconditions.checkArgument(durationInSeconds > 0, "Semaphore duration must be positive"); + Preconditions.checkArgument(maxDurationInSeconds > 0, "Semaphore max duration must be positive"); SqlSession session = mybatis.openSession(); try { SemaphoreMapper mapper = session.getMapper(SemaphoreMapper.class); initialize(name, session, mapper); - return doLock(name, durationInSeconds, session, mapper); + return doAcquire(name, maxDurationInSeconds, session, mapper); } finally { MyBatis.closeQuietly(session); } } - public void unlock(String name) { + public void release(String name) { Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Semaphore name must not be empty"); SqlSession session = mybatis.openSession(); try { - session.getMapper(SemaphoreMapper.class).unlock(name); + session.getMapper(SemaphoreMapper.class).release(name); session.commit(); } finally { MyBatis.closeQuietly(session); } } - private boolean doLock(String name, int durationInSeconds, SqlSession session, SemaphoreMapper mapper) { + private boolean doAcquire(String name, int durationInSeconds, SqlSession session, SemaphoreMapper mapper) { Date lockedBefore = DateUtils.addSeconds(mapper.now(), -durationInSeconds); - boolean ok = mapper.lock(name, lockedBefore) == 1; + boolean ok = mapper.acquire(name, lockedBefore) == 1; session.commit(); return ok; } 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 a2be689e1c6..d77e8cd3c3f 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,9 +27,9 @@ public interface SemaphoreMapper { int initialize(@Param("name") String name, @Param("lockedAt") Date lockedAt); - int lock(@Param("name") String name, @Param("lockedBefore") Date lockedBefore); + int acquire(@Param("name") String name, @Param("lockedBefore") Date lockedBefore); Date now(); - void unlock(String name); + void release(String 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 df8dac1d124..ea5f9f1102b 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 @@ -12,14 +12,14 @@ select current_timestamp - + update semaphores set updated_at = current_timestamp, locked_at = current_timestamp where name=#{name} AND locked_at < #{lockedBefore} - + delete from semaphores where name=#{id} 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 46617b2830b..6abc912b896 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 @@ -36,9 +36,9 @@ import static org.fest.assertions.Assertions.assertThat; public class SemaphoreDaoTest extends AbstractDaoTestCase { @Test - public void create_and_lock_semaphore() throws Exception { + public void create_and_acquire_semaphore() throws Exception { SemaphoreDao dao = new SemaphoreDao(getMyBatis()); - assertThat(dao.lock("foo", 60)).isTrue(); + assertThat(dao.acquire("foo", 60)).isTrue(); Semaphore semaphore = selectSemaphore("foo"); assertThat(semaphore).isNotNull(); @@ -47,7 +47,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { assertThat(isRecent(semaphore.updatedAt, 60)).isTrue(); assertThat(isRecent(semaphore.lockedAt, 60)).isTrue(); - dao.unlock("foo"); + dao.release("foo"); assertThat(selectSemaphore("foo")).isNull(); } @@ -55,7 +55,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { public void fail_to_acquire_locked_semaphore() throws Exception { setupData("old_semaphore"); SemaphoreDao dao = new SemaphoreDao(getMyBatis()); - assertThat(dao.lock("foo", Integer.MAX_VALUE)).isFalse(); + assertThat(dao.acquire("foo", Integer.MAX_VALUE)).isFalse(); Semaphore semaphore = selectSemaphore("foo"); assertThat(semaphore).isNotNull(); @@ -69,7 +69,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { public void acquire_long_locked_semaphore() throws Exception { setupData("old_semaphore"); SemaphoreDao dao = new SemaphoreDao(getMyBatis()); - assertThat(dao.lock("foo", 60)).isTrue(); + assertThat(dao.acquire("foo", 60)).isTrue(); Semaphore semaphore = selectSemaphore("foo"); assertThat(semaphore).isNotNull(); @@ -83,8 +83,8 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { public void test_concurrent_locks() throws Exception { SemaphoreDao dao = new SemaphoreDao(getMyBatis()); - for (int tests = 0; tests < 5000; tests++) { - dao.unlock("my-lock"); + for (int tests = 0; tests < 5; tests++) { + dao.release("my-lock"); int size = 5; CyclicBarrier barrier = new CyclicBarrier(size); CountDownLatch latch = new CountDownLatch(size); @@ -152,7 +152,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { try { barrier.await(); for (int i = 0; i < 100; i++) { - if (dao.lock("my-lock", 60 * 5)) { + if (dao.acquire("my-lock", 60 * 5)) { locks.incrementAndGet(); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DatabaseSemaphore.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DatabaseSemaphore.java new file mode 100644 index 00000000000..f01a6e6f25f --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DatabaseSemaphore.java @@ -0,0 +1,36 @@ +/* + * 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.api.utils; + +import org.sonar.api.BatchComponent; +import org.sonar.api.ServerComponent; + +/** + * A semaphore shared among all the processes that can connect to the central database. + * + * @since 3.4 + */ +public interface DatabaseSemaphore extends BatchComponent, ServerComponent { + + boolean acquire(String name, int maxDurationInSeconds); + + void release(String name); + +} -- 2.39.5