public final class CorePlugin extends SonarPlugin {
@SuppressWarnings("unchecked")
- public List<Class<? extends Extension>> getExtensions() {
+ public List getExtensions() {
return ImmutableList.of(
DefaultResourceTypes.class,
UserManagedMetrics.class,
ProjectFileSystemLogger.class,
+ DatabaseSemaphoreImpl.class,
// maven
MavenInitializer.class,
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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");
+ }
+}
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;
}
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);
}
select current_timestamp
</select>
- <update id="lock" parameterType="map">
+ <update id="acquire" parameterType="map">
update semaphores
set updated_at = current_timestamp, locked_at = current_timestamp
where name=#{name}
AND locked_at < #{lockedBefore}
</update>
- <delete id="unlock" parameterType="String">
+ <delete id="release" parameterType="String">
delete from semaphores where name=#{id}
</delete>
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();
assertThat(isRecent(semaphore.updatedAt, 60)).isTrue();
assertThat(isRecent(semaphore.lockedAt, 60)).isTrue();
- dao.unlock("foo");
+ dao.release("foo");
assertThat(selectSemaphore("foo")).isNull();
}
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();
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();
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);
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();
}
}
--- /dev/null
+/*
+ * 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);
+
+}