From af07212b764549c16cd252a9311bb68c642e8179 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 14 Dec 2012 18:27:29 +0100 Subject: SONAR-3887 refactor semaphore API --- .../org/sonar/batch/bootstrap/BatchModule.java | 6 +- .../org/sonar/batch/bootstrap/CheckSemaphore.java | 103 --------------- .../org/sonar/batch/bootstrap/ProjectLock.java | 100 ++++++++++++++ .../sonar/batch/bootstrap/CheckSemaphoreTest.java | 144 --------------------- .../org/sonar/batch/bootstrap/ProjectLockTest.java | 117 +++++++++++++++++ 5 files changed, 220 insertions(+), 250 deletions(-) delete mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrap/CheckSemaphore.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java delete mode 100644 sonar-batch/src/test/java/org/sonar/batch/bootstrap/CheckSemaphoreTest.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java (limited to 'sonar-batch') 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 94448f6f16f..bedc8578223 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 @@ -48,7 +48,7 @@ import org.sonar.core.i18n.RuleI18nManager; import org.sonar.core.metric.CacheMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.persistence.DaoUtils; -import org.sonar.core.persistence.DatabaseSemaphoreImpl; +import org.sonar.core.persistence.SemaphoresImpl; import org.sonar.core.persistence.DatabaseVersion; import org.sonar.core.persistence.MyBatis; import org.sonar.core.resource.DefaultResourcePermissions; @@ -104,8 +104,8 @@ public class BatchModule extends Module { container.addSingleton(DefaultUserFinder.class); container.addSingleton(ResourceTypes.class); container.addSingleton(MetricProvider.class); - container.addSingleton(DatabaseSemaphoreImpl.class); - container.addSingleton(CheckSemaphore.class); + container.addSingleton(SemaphoresImpl.class); + container.addSingleton(ProjectLock.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 deleted file mode 100644 index 9bc4b65e621..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/CheckSemaphore.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.DatabaseSemaphore; -import org.sonar.api.utils.SonarException; -import org.sonar.batch.ProjectTree; - -import static org.sonar.api.utils.DatabaseSemaphore.Lock; - -public class CheckSemaphore { - - private static final Logger LOG = LoggerFactory.getLogger(CheckSemaphore.class); - - private final DatabaseSemaphore databaseSemaphore; - private final ProjectTree projectTree; - private final Settings settings; - - public CheckSemaphore(DatabaseSemaphore databaseSemaphore, ProjectTree projectTree, Settings settings) { - this.databaseSemaphore = databaseSemaphore; - 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 : {}, with key {}", getProject(), getSemaphoreKey()); - if (!isForceAnalyseActivated()) { - return databaseSemaphore.acquire(getSemaphoreKey()); - } else { - // In force mode, we acquire the lock regardless there's a existing lock or not - return databaseSemaphore.acquire(getSemaphoreKey(), 0); - } - } - - private void release() { - LOG.debug("Release semaphore on project : {}, with key {}", getProject(), getSemaphoreKey()); - databaseSemaphore.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/ProjectLock.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java new file mode 100644 index 00000000000..33a4620a4fc --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectLock.java @@ -0,0 +1,100 @@ +/* + * 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.Semaphores; +import org.sonar.api.utils.SonarException; +import org.sonar.batch.ProjectTree; + +public class ProjectLock { + + private static final Logger LOG = LoggerFactory.getLogger(ProjectLock.class); + + private final Semaphores semaphores; + private final ProjectTree projectTree; + private final Settings settings; + + public ProjectLock(Semaphores semaphores, ProjectTree projectTree, Settings settings) { + this.semaphores = semaphores; + this.projectTree = projectTree; + this.settings = settings; + } + + public void start() { + if (!isInDryRunMode()) { + Semaphores.Semaphore semaphore = acquire(); + if (!semaphore.isLocked()) { + LOG.error(getErrorMessage(semaphore)); + throw new SonarException("The project is already been analysing."); + } + } + } + + private String getErrorMessage(Semaphores.Semaphore semaphore) { + long duration = semaphore.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 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()); + } + + private void release() { + LOG.debug("Release semaphore on project : {}, with key {}", getProject(), getSemaphoreKey()); + semaphores.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 shouldForce() { + return settings.getBoolean(CoreProperties.FORCE_ANALYSIS); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/CheckSemaphoreTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/CheckSemaphoreTest.java deleted file mode 100644 index e3d09868d9a..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/CheckSemaphoreTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.junit.Before; -import org.junit.Test; -import org.sonar.api.CoreProperties; -import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; -import org.sonar.api.utils.DatabaseSemaphore; -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.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.sonar.api.utils.DatabaseSemaphore.Lock; - -public class CheckSemaphoreTest { - - private CheckSemaphore checkSemaphore; - - private DatabaseSemaphore databaseSemaphore; - private ProjectTree projectTree; - private Settings settings; - - private Project project; - private Lock lock; - - @Before - public void setUp() { - lock = mock(Lock.class); - - databaseSemaphore = mock(DatabaseSemaphore.class); - when(databaseSemaphore.acquire(anyString())).thenReturn(lock); - when(databaseSemaphore.acquire(anyString(), anyInt())).thenReturn(lock); - - projectTree = mock(ProjectTree.class); - settings = new Settings(); - setDryRunMode(false); - setForceMode(false); - - project = new Project("key", "branch", "name"); - when(projectTree.getRootProject()).thenReturn(project); - - checkSemaphore = new CheckSemaphore(databaseSemaphore, projectTree, settings); - } - - @Test - public void shouldAcquireSemaphore() { - when(lock.isAcquired()).thenReturn(true); - checkSemaphore.start(); - - verify(databaseSemaphore).acquire(anyString()); - } - - @Test - public void shouldUseProjectKeyInTheKeyOfTheSemaphore() { - project = new Project("key"); - when(projectTree.getRootProject()).thenReturn(project); - - when(lock.isAcquired()).thenReturn(true); - checkSemaphore.start(); - - verify(databaseSemaphore).acquire("batch-key"); - } - - @Test - public void shouldUseProjectKeyAndBranchIfExistingInTheKeyOfTheSemaphore() { - when(lock.isAcquired()).thenReturn(true); - checkSemaphore.start(); - - verify(databaseSemaphore).acquire("batch-key:branch"); - } - - @Test - public void shouldAcquireSemaphoreIfForceAnalyseActivated() { - setForceMode(true); - when(lock.isAcquired()).thenReturn(true); - checkSemaphore.start(); - verify(databaseSemaphore).acquire(anyString(), anyInt()); - } - - @Test(expected = SonarException.class) - public void shouldNotAcquireSemaphoreIfTheProjectIsAlreadyBeenAnalysing() { - when(lock.getLocketAt()).thenReturn(new Date()); - when(lock.isAcquired()).thenReturn(false); - checkSemaphore.start(); - verify(databaseSemaphore, never()).acquire(anyString()); - } - - @Test - public void shouldNotAcquireSemaphoreInDryRunMode() { - setDryRunMode(true); - settings = new Settings().setProperty(CoreProperties.DRY_RUN, true); - checkSemaphore.start(); - verify(databaseSemaphore, never()).acquire(anyString()); - verify(databaseSemaphore, never()).acquire(anyString(), anyInt()); - } - - @Test - public void shouldReleaseSemaphore() { - checkSemaphore.stop(); - verify(databaseSemaphore).release(anyString()); - } - - @Test - public void shouldNotReleaseSemaphoreInDryRunMode() { - setDryRunMode(true); - checkSemaphore.stop(); - verify(databaseSemaphore, never()).release(anyString()); - } - - private void setDryRunMode(boolean isInDryRunMode) { - settings.setProperty(CoreProperties.DRY_RUN, isInDryRunMode); - } - - private void setForceMode(boolean isInForcedMode) { - settings.setProperty(CoreProperties.FORCE_ANALYSIS, isInForcedMode); - } - -} 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 new file mode 100644 index 00000000000..f47061a7c0b --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ProjectLockTest.java @@ -0,0 +1,117 @@ +/* + * 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.junit.Before; +import org.junit.Test; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Project; +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; + +public class ProjectLockTest { + + private ProjectLock projectLock; + + private Semaphores semaphores; + private ProjectTree projectTree; + private Settings settings; + + private Project project; + + @Before + public void setUp() { + semaphores = mock(Semaphores.class); + + projectTree = mock(ProjectTree.class); + settings = new Settings(); + setDryRunMode(false); + setForceMode(false); + project = new Project("my-project-key"); + when(projectTree.getRootProject()).thenReturn(project); + + projectLock = new ProjectLock(semaphores, projectTree, settings); + } + + @Test + public void shouldAcquireSemaphore() { + when(semaphores.acquire(anyString())).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(); + } + + @Test(expected = SonarException.class) + public void shouldNotAcquireSemaphoreIfTheProjectIsAlreadyBeenAnalysing() { + when(semaphores.acquire(anyString())).thenReturn(new Semaphores.Semaphore().setLocked(false).setDurationSinceLocked(1234L)); + projectLock.start(); + } + + @Test + public void shouldNotAcquireSemaphoreInDryRunMode() { + setDryRunMode(true); + settings = new Settings().setProperty(CoreProperties.DRY_RUN, true); + projectLock.start(); + verifyZeroInteractions(semaphores); + } + + @Test + public void shouldReleaseSemaphore() { + projectLock.stop(); + verify(semaphores).release("batch-my-project-key"); + } + + @Test + public void shouldNotReleaseSemaphoreInDryRunMode() { + setDryRunMode(true); + projectLock.stop(); + verifyZeroInteractions(semaphores); + } + + private void setDryRunMode(boolean isInDryRunMode) { + settings.setProperty(CoreProperties.DRY_RUN, isInDryRunMode); + } + + private void setForceMode(boolean isInForcedMode) { + settings.setProperty(CoreProperties.FORCE_ANALYSIS, isInForcedMode); + } + +} -- cgit v1.2.3