diff --git a/CHANGELOG.md b/CHANGELOG.md index ae84089240..5289e76b64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Added +- Add "sonia.scm.restart.wait" to set wait in milliseconds before restarting scm-server ([#1308](https://github.com/scm-manager/scm-manager/pull/1308)) + ### Fixed - Fix detection of markdown files for files having content does not start with '#' ([#1306](https://github.com/scm-manager/scm-manager/pull/1306)) - Fix broken markdown rendering ([#1303](https://github.com/scm-manager/scm-manager/pull/1303)) diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java index cb85852f83..9e2a547f49 100644 --- a/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java +++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java @@ -31,9 +31,8 @@ import javax.inject.Singleton; @Singleton public class DefaultRestarter implements Restarter { - - private ScmEventBus eventBus; - private RestartStrategy strategy; + private final ScmEventBus eventBus; + private final RestartStrategy strategy; @Inject public DefaultRestarter() { diff --git a/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java b/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java index 0504a62c59..79b4be07fd 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java @@ -61,6 +61,7 @@ import static java.util.Comparator.comparing; class MigrationWizardServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(MigrationWizardServlet.class); + static final String PROPERTY_WAIT_TIME = "sonia.scm.restart-migration.wait"; private final XmlRepositoryV1UpdateStep repositoryV1UpdateStep; private final DefaultMigrationStrategyDAO migrationStrategyDao; @@ -129,7 +130,7 @@ class MigrationWizardServlet extends HttpServlet { repositoryLineEntries.stream() .forEach( - entry-> { + entry -> { String id = entry.getId(); String protocol = entry.getType(); String originalName = entry.getOriginalName(); @@ -145,7 +146,7 @@ class MigrationWizardServlet extends HttpServlet { if (restarter.isSupported()) { respondWithTemplate(resp, model, "templates/repository-migration-restart.mustache"); - restarter.restart(MigrationWizardServlet.class, "wrote migration data"); + new Thread(this::restart).start(); } else { respondWithTemplate(resp, model, "templates/repository-migration-manual-restart.mustache"); LOG.error("Restarting is not supported on this platform."); @@ -153,6 +154,19 @@ class MigrationWizardServlet extends HttpServlet { } } + private void restart() { + String wait = System.getProperty(PROPERTY_WAIT_TIME); + if (!Strings.isNullOrEmpty(wait)) { + try { + Thread.sleep(Long.parseLong(wait)); + } catch (InterruptedException e) { + LOG.error("error on waiting before restart", e); + Thread.currentThread().interrupt(); + } + } + restarter.restart(MigrationWizardServlet.class, "wrote migration data"); + } + private List getRepositoryLineEntries() { List repositoriesWithoutMigrationStrategies = repositoryV1UpdateStep.getRepositoriesWithoutMigrationStrategies(); diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java index a137d7a947..c80ce208ac 100644 --- a/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java @@ -23,7 +23,6 @@ */ package sonia.scm.lifecycle; -import com.github.legman.Subscribe; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -32,8 +31,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.event.ScmEventBus; -import javax.swing.*; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.verify; @@ -72,7 +69,7 @@ class DefaultRestarterTest { @Test void shouldThrowRestartNotSupportedException() { - DefaultRestarter restarter = new DefaultRestarter(eventBus,null); + DefaultRestarter restarter = new DefaultRestarter(eventBus, null); assertThrows( RestartNotSupportedException.class, () -> restarter.restart(DefaultRestarterTest.class, "test") ); diff --git a/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java b/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java index c50f2e6927..d579e8beb7 100644 --- a/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java +++ b/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java @@ -21,9 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.update; +import com.google.common.base.Stopwatch; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -37,13 +38,19 @@ import sonia.scm.update.repository.XmlRepositoryV1UpdateStep; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.time.Duration; import java.util.Collections; import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static sonia.scm.update.MigrationWizardServlet.PROPERTY_WAIT_TIME; @ExtendWith(MockitoExtension.class) class MigrationWizardServletTest { @@ -262,4 +269,58 @@ class MigrationWizardServletTest { verify(migrationStrategyDao).set("id", "git", "name", MigrationStrategy.COPY, "namespace", "name"); } + + @Test + void shouldRestartAfterValidMigration() throws InterruptedException { + + when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn( + Collections.singletonList(new V1Repository("id", "git", "name")) + ); + doReturn("namespace").when(request).getParameter("namespace-id"); + doReturn("name").when(request).getParameter("name-id"); + doReturn("COPY").when(request).getParameter("strategy-id"); + + final CountDownLatch countDownLatch = new CountDownLatch(1); + when(restarter.isSupported()).thenReturn(true); + doAnswer(ic -> { + countDownLatch.countDown(); + return null; + }).when(restarter).restart(any(), any()); + + servlet.doPost(request, response); + + boolean restarted = countDownLatch.await(500L, TimeUnit.MILLISECONDS); + + assertThat(restarted).isTrue(); + } + + @Test + void shouldRestartAfterValidMigrationWithSystemProperty() throws InterruptedException { + System.setProperty(PROPERTY_WAIT_TIME, "100"); + Stopwatch stopwatch = Stopwatch.createStarted(); + + when(updateStep.getRepositoriesWithoutMigrationStrategies()).thenReturn( + Collections.singletonList(new V1Repository("id", "git", "name")) + ); + doReturn("namespace").when(request).getParameter("namespace-id"); + doReturn("name").when(request).getParameter("name-id"); + doReturn("COPY").when(request).getParameter("strategy-id"); + + final CountDownLatch countDownLatch = new CountDownLatch(1); + when(restarter.isSupported()).thenReturn(true); + doAnswer(ic -> { + stopwatch.stop(); + countDownLatch.countDown(); + return null; + }).when(restarter).restart(any(), any()); + + servlet.doPost(request, response); + + boolean restarted = countDownLatch.await(750L, TimeUnit.MILLISECONDS); + + assertThat(stopwatch.elapsed()).isGreaterThanOrEqualTo(Duration.ofMillis(100L)); + assertThat(restarted).isTrue(); + + System.clearProperty(PROPERTY_WAIT_TIME); + } }