From 56b8dbdb22466c72cf8ef85056cac385a228f23b Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Wed, 12 Feb 2020 12:13:10 +0100
Subject: [PATCH 01/18] Reimplement restarting of scm-manager
SCM-Manager tries now to figure out which is the best strategy for the restart.
It chooses from one of the following strategies:
* PosixRestartStrategy which uses native LibC
* ExitRestartStrategy uses System.exit and relies on external mechanism to start again
* InjectionContextRestartStrategy destroys and re initializes the injection context
---
scm-webapp/pom.xml | 14 +++
.../scm/lifecycle/BootstrapContextFilter.java | 9 +-
.../java/sonia/scm/lifecycle/CLibrary.java | 22 ++++
.../scm/lifecycle/ExitRestartStrategy.java | 52 +++++++++
.../InjectionContextRestartStrategy.java | 6 +-
.../scm/lifecycle/PosixRestartStrategy.java | 52 +++++++++
.../RestartNotSupportedException.java | 10 ++
.../sonia/scm/lifecycle/RestartStrategy.java | 13 ++-
.../scm/lifecycle/RestartStrategyFactory.java | 66 ++++++++++++
.../lifecycle/ExitRestartStrategyTest.java | 75 +++++++++++++
.../scm/lifecycle/RestartStrategyTest.java | 101 ++++++++++++++++++
11 files changed, 412 insertions(+), 8 deletions(-)
create mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java
create mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
create mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
create mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
create mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
create mode 100644 scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
create mode 100644 scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml
index 924e176024..b71d4043c2 100644
--- a/scm-webapp/pom.xml
+++ b/scm-webapp/pom.xml
@@ -295,6 +295,20 @@
2.7.0
+
+
+
+ org.kohsuke
+ akuma
+ 1.10
+
+
+
+ net.java.dev.jna
+ jna
+ 5.5.0
+
+
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java
index 1d642d9c66..388ec7bb73 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java
@@ -42,6 +42,7 @@ import sonia.scm.event.ScmEventBus;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletException;
+import java.util.Optional;
//~--- JDK imports ------------------------------------------------------------
@@ -100,8 +101,12 @@ public class BootstrapContextFilter extends GuiceFilter {
if (filterConfig == null) {
LOG.error("filter config is null, scm-manager is not initialized");
} else {
- RestartStrategy restartStrategy = RestartStrategy.get(webAppClassLoader);
- restartStrategy.restart(new GuiceInjectionContext());
+ Optional restartStrategy = RestartStrategy.get(webAppClassLoader);
+ if (restartStrategy.isPresent()) {
+ restartStrategy.get().restart(new GuiceInjectionContext());
+ } else {
+ LOG.warn("restarting is not supported by the underlying platform");
+ }
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java
new file mode 100644
index 0000000000..91b82ce3d1
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java
@@ -0,0 +1,22 @@
+package sonia.scm.lifecycle;
+
+/**
+ * Interface for native c library.
+ */
+@SuppressWarnings({
+ "squid:S1214", // usage as constant is common practice for jna
+ "squid:S1191" // use of sun.* classes is required for jna
+})
+interface CLibrary extends com.sun.jna.Library {
+ CLibrary LIBC = com.sun.jna.Native.load("c", CLibrary.class);
+
+ int F_GETFD = 1;
+ int F_SETFD = 2;
+ int FD_CLOEXEC = 1;
+
+ int getdtablesize();
+ int fcntl(int fd, int command);
+ int fcntl(int fd, int command, int flags);
+ int execvp(String file, com.sun.jna.StringArray args);
+ String strerror(int errno);
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
new file mode 100644
index 0000000000..dfa3d72f82
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
@@ -0,0 +1,52 @@
+package sonia.scm.lifecycle;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.function.IntConsumer;
+
+/**
+ * {@link RestartStrategy} which tears down the scm-manager context and
+ * then exists the java process with {@link System#exit(int)}.
+ *
+ * This is useful if an external mechanism is able to restart the process after it has exited.
+ */
+class ExitRestartStrategy implements RestartStrategy {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ExitRestartStrategy.class);
+
+ static final String NAME = "exit";
+
+ static final String PROPERTY_EXIT_CODE = "sonia.scm.restart.exit-code";
+
+ private IntConsumer exiter = System::exit;
+
+ ExitRestartStrategy() {
+ }
+
+ @VisibleForTesting
+ void setExiter(IntConsumer exiter) {
+ this.exiter = exiter;
+ }
+
+ @Override
+ public void restart(InjectionContext context) {
+ int exitCode = determineExitCode();
+
+ LOG.warn("destroy injection context");
+ context.destroy();
+
+ LOG.warn("exit scm-manager with exit code {}", exitCode);
+ exiter.accept(exitCode);
+ }
+
+ private int determineExitCode() {
+ String exitCodeAsString = System.getProperty(PROPERTY_EXIT_CODE, "0");
+ try {
+ return Integer.parseInt(exitCodeAsString);
+ } catch (NumberFormatException ex) {
+ throw new RestartNotSupportedException("invalid exit code " + exitCodeAsString, ex);
+ }
+ }
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
index 2db60580b1..e78e7b615c 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
@@ -10,9 +10,11 @@ import sonia.scm.event.ShutdownEventBusEvent;
import java.util.concurrent.atomic.AtomicLong;
/**
- * Restart strategy implementation which destroy the injection context and re initialize it.
+ * Restart strategy which tries to free, every resource used by the context, starts gc and re initializes the context.
*/
-public class InjectionContextRestartStrategy implements RestartStrategy {
+class InjectionContextRestartStrategy implements RestartStrategy {
+
+ static final String NAME = "context";
private static final String DISABLE_RESTART_PROPERTY = "sonia.scm.restart.disable";
private static final String WAIT_PROPERTY = "sonia.scm.restart.wait";
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
new file mode 100644
index 0000000000..d7d64b9e6d
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
@@ -0,0 +1,52 @@
+package sonia.scm.lifecycle;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import static sonia.scm.lifecycle.CLibrary.*;
+
+/**
+ * Restart strategy which uses execvp from libc. This strategy is only supported on posix base operating systems.
+ */
+class PosixRestartStrategy implements RestartStrategy {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PosixRestartStrategy.class);
+
+ PosixRestartStrategy() {
+ }
+
+ @Override
+ public void restart(InjectionContext context) {
+ LOG.warn("destroy injection context");
+ context.destroy();
+
+ LOG.warn("restart scm-manager jvm process");
+ try {
+ restart();
+ } catch (IOException e) {
+ LOG.error("failed to collect java vm arguments", e);
+ LOG.error("we will now exit the java process");
+ System.exit(1);
+ }
+ }
+
+ @SuppressWarnings("squid:S1191") // use of sun.* classes is required for jna)
+ private static void restart() throws IOException {
+ com.sun.akuma.JavaVMArguments args = com.sun.akuma.JavaVMArguments.current();
+ args.remove("--daemon");
+
+ int sz = LIBC.getdtablesize();
+ for(int i=3; i get(ClassLoader webAppClassLoader) {
+ return Optional.ofNullable(RestartStrategyFactory.create(webAppClassLoader));
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
new file mode 100644
index 0000000000..a6fb371aa7
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
@@ -0,0 +1,66 @@
+package sonia.scm.lifecycle;
+
+import com.google.common.base.Strings;
+import sonia.scm.PlatformType;
+import sonia.scm.util.SystemUtil;
+
+final class RestartStrategyFactory {
+
+ /**
+ * System property to load a specific restart strategy.
+ */
+ static final String PROPERTY_STRATEGY = "sonia.scm.lifecycle.restart-strategy";
+
+ /**
+ * No restart supported.
+ */
+ static final String STRATEGY_NONE = "none";
+
+ private RestartStrategyFactory() {
+ }
+
+ /**
+ * Returns the configured strategy or {@code null} if restart is not supported.
+ *
+ * @param webAppClassLoader root webapp classloader
+ * @return configured strategy or {@code null}
+ */
+ static RestartStrategy create(ClassLoader webAppClassLoader) {
+ String property = System.getProperty(PROPERTY_STRATEGY);
+ if (Strings.isNullOrEmpty(property)) {
+ return forPlatform();
+ }
+ return fromProperty(webAppClassLoader, property);
+ }
+
+ private static RestartStrategy fromProperty(ClassLoader webAppClassLoader, String property) {
+ if (STRATEGY_NONE.equalsIgnoreCase(property)) {
+ return null;
+ } else if (ExitRestartStrategy.NAME.equalsIgnoreCase(property)) {
+ return new ExitRestartStrategy();
+ } else if (InjectionContextRestartStrategy.NAME.equalsIgnoreCase(property)) {
+ return new InjectionContextRestartStrategy(webAppClassLoader);
+ } else {
+ return fromClassName(property);
+ }
+ }
+
+ private static RestartStrategy fromClassName(String property) {
+ try {
+ Class extends RestartStrategy> rsClass = Class.forName(property).asSubclass(RestartStrategy.class);
+ return rsClass.getConstructor().newInstance();
+ } catch (Exception e) {
+ throw new RestartNotSupportedException("failed to create restart strategy from property", e);
+ }
+ }
+
+ private static RestartStrategy forPlatform() {
+ // we do not use SystemUtil here, to allow testing
+ String osName = System.getProperty(SystemUtil.PROPERTY_OSNAME);
+ PlatformType platform = PlatformType.createPlatformType(osName);
+ if (platform.isPosix()) {
+ return new PosixRestartStrategy();
+ }
+ return null;
+ }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
new file mode 100644
index 0000000000..078e218b75
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
@@ -0,0 +1,75 @@
+package sonia.scm.lifecycle;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.function.IntConsumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.verify;
+
+@ExtendWith(MockitoExtension.class)
+class ExitRestartStrategyTest {
+
+ @Mock
+ private RestartStrategy.InjectionContext context;
+
+ private ExitRestartStrategy strategy;
+ private CapturingExiter exiter;
+
+ @BeforeEach
+ void setUpStrategy() {
+ strategy = new ExitRestartStrategy();
+ exiter = new CapturingExiter();
+ strategy.setExiter(exiter);
+ }
+
+ @Test
+ void shouldTearDownContextAndThenExit() {
+ strategy.restart(context);
+
+ verify(context).destroy();
+ assertThat(exiter.getExitCode()).isEqualTo(0);
+ }
+
+ @Test
+ void shouldUseExitCodeFromSystemProperty() {
+ System.setProperty(ExitRestartStrategy.PROPERTY_EXIT_CODE, "42");
+ try {
+ strategy.restart(context);
+
+ verify(context).destroy();
+ assertThat(exiter.getExitCode()).isEqualTo(42);
+ } finally {
+ System.clearProperty(ExitRestartStrategy.PROPERTY_EXIT_CODE);
+ }
+ }
+
+ @Test
+ void shouldThrowExceptionForNonNumericExitCode() {
+ System.setProperty(ExitRestartStrategy.PROPERTY_EXIT_CODE, "xyz");
+ try {
+ assertThrows(RestartNotSupportedException.class, () -> strategy.restart(context));
+ } finally {
+ System.clearProperty(ExitRestartStrategy.PROPERTY_EXIT_CODE);
+ }
+ }
+
+ private static class CapturingExiter implements IntConsumer {
+
+ private int exitCode = -1;
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ @Override
+ public void accept(int exitCode) {
+ this.exitCode = exitCode;
+ }
+ }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
new file mode 100644
index 0000000000..914c506aa2
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
@@ -0,0 +1,101 @@
+package sonia.scm.lifecycle;
+
+import com.google.common.base.Strings;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import sonia.scm.util.SystemUtil;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class RestartStrategyTest {
+ private final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+ @Test
+ void shouldReturnRestartStrategyFromSystemProperty() {
+ withStrategy(TestingRestartStrategy.class.getName(), (rs) -> {
+ assertThat(rs).containsInstanceOf(TestingRestartStrategy.class);
+ });
+ }
+
+ @Test
+ void shouldThrowExceptionForNonStrategyClass() {
+ withStrategy(RestartStrategyTest.class.getName(), () -> {
+ assertThrows(RestartNotSupportedException.class, () -> RestartStrategy.get(classLoader));
+ });
+ }
+
+ @Test
+ void shouldReturnEmpty() {
+ withStrategy(RestartStrategyFactory.STRATEGY_NONE, (rs) -> {
+ assertThat(rs).isEmpty();
+ });
+ }
+
+ @Test
+ void shouldReturnEmptyForUnknownOs() {
+ withSystemProperty(SystemUtil.PROPERTY_OSNAME, "hitchhiker-os", () -> {
+ Optional restartStrategy = RestartStrategy.get(classLoader);
+ assertThat(restartStrategy).isEmpty();
+ });
+ }
+
+ @Test
+ void shouldReturnExitRestartStrategy() {
+ withStrategy(ExitRestartStrategy.NAME, (rs) -> {
+ assertThat(rs).containsInstanceOf(ExitRestartStrategy.class);
+ });
+ }
+
+ @Test
+ void shouldReturnInjectionContextRestartStrategy() {
+ withStrategy(InjectionContextRestartStrategy.NAME, (rs) -> {
+ assertThat(rs).containsInstanceOf(InjectionContextRestartStrategy.class);
+ });
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "linux", "darwin", "solaris", "freebsd", "openbsd" })
+ void shouldReturnPosixRestartStrategyForPosixBased(String os) {
+ withSystemProperty(SystemUtil.PROPERTY_OSNAME, os, () -> {
+ Optional restartStrategy = RestartStrategy.get(classLoader);
+ assertThat(restartStrategy).containsInstanceOf(PosixRestartStrategy.class);
+ });
+ }
+
+ private void withStrategy(String strategy, Consumer> consumer) {
+ withStrategy(strategy, () -> {
+ consumer.accept(RestartStrategy.get(classLoader));
+ });
+ }
+
+ private void withStrategy(String strategy, Runnable runnable) {
+ withSystemProperty(RestartStrategyFactory.PROPERTY_STRATEGY, strategy, runnable);
+ }
+
+ private void withSystemProperty(String key, String value, Runnable runnable) {
+ String oldValue = System.getProperty(key);
+ System.setProperty(key, value);
+ try {
+ runnable.run();
+ } finally {
+ if (Strings.isNullOrEmpty(oldValue)) {
+ System.clearProperty(key);
+ } else {
+ System.setProperty(key, oldValue);
+ }
+ }
+ }
+
+ public static class TestingRestartStrategy implements RestartStrategy {
+ @Override
+ public void restart(InjectionContext context) {
+
+ }
+ }
+
+}
From bca34b829dac43bc654f9a4e40ef59a69bd09759 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Wed, 12 Feb 2020 12:36:37 +0100
Subject: [PATCH 02/18] use SimpleClassLoaderLifeCycle by default
---
scm-it/pom.xml | 4 ----
scm-webapp/pom.xml | 8 --------
.../lifecycle/InjectionContextRestartStrategy.java | 2 ++
.../classloading/ClassLoaderLifeCycle.java | 12 ++++++------
.../ClassLoaderLifeCycleWithLeakPrevention.java | 2 ++
.../classloading/ClassLoaderLifeCycleTest.java | 13 ++++++++++++-
6 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/scm-it/pom.xml b/scm-it/pom.xml
index 9064a39fc1..c38c73ce3e 100644
--- a/scm-it/pom.xml
+++ b/scm-it/pom.xml
@@ -200,10 +200,6 @@
java.awt.headlesstrue
-
- sonia.scm.classloading.lifecycle
- simple
- /scm
diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml
index b71d4043c2..ba3805b094 100644
--- a/scm-webapp/pom.xml
+++ b/scm-webapp/pom.xml
@@ -734,10 +734,6 @@
scm.stage${scm.stage}
-
- sonia.scm.classloading.lifecycle
- simple
- ${project.basedir}/src/main/conf/jetty.xml0
@@ -823,10 +819,6 @@
scm.hometarget/scm-it
-
- sonia.scm.classloading.lifecycle
- simple
- ${project.basedir}/src/main/conf/jetty.xml0
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
index e78e7b615c..a2ea3b7f9d 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
@@ -11,6 +11,8 @@ import java.util.concurrent.atomic.AtomicLong;
/**
* Restart strategy which tries to free, every resource used by the context, starts gc and re initializes the context.
+ * Warning: This strategy should only be used with an classloader lifecycle which protects the
+ * created plugin classloader from classloader leaks.
*/
class InjectionContextRestartStrategy implements RestartStrategy {
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java
index fb7d991c1e..683dbc8fbb 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java
@@ -19,17 +19,17 @@ public abstract class ClassLoaderLifeCycle implements LifeCycle {
private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderLifeCycle.class);
@VisibleForTesting
- static final String PROPERTY = "sonia.scm.classloading.lifecycle";
+ static final String PROPERTY = "sonia.scm.lifecycle.classloading";
public static ClassLoaderLifeCycle create() {
ClassLoader webappClassLoader = Thread.currentThread().getContextClassLoader();
String implementation = System.getProperty(PROPERTY);
- if (SimpleClassLoaderLifeCycle.NAME.equalsIgnoreCase(implementation)) {
- LOG.info("create new simple ClassLoaderLifeCycle");
- return new SimpleClassLoaderLifeCycle(webappClassLoader);
+ if (ClassLoaderLifeCycleWithLeakPrevention.NAME.equalsIgnoreCase(implementation)) {
+ LOG.info("create new ClassLoaderLifeCycle with leak prevention");
+ return new ClassLoaderLifeCycleWithLeakPrevention(webappClassLoader);
}
- LOG.info("create new ClassLoaderLifeCycle with leak prevention");
- return new ClassLoaderLifeCycleWithLeakPrevention(webappClassLoader);
+ LOG.info("create new simple ClassLoaderLifeCycle");
+ return new SimpleClassLoaderLifeCycle(webappClassLoader);
}
private final ClassLoader webappClassLoader;
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java
index e2e7a032b7..471f4ebc9f 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java
@@ -26,6 +26,8 @@ import static se.jiderhamn.classloader.leak.prevention.cleanup.ShutdownHookClean
*/
final class ClassLoaderLifeCycleWithLeakPrevention extends ClassLoaderLifeCycle {
+ public static final String NAME = "with-leak-prevention";
+
private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderLifeCycleWithLeakPrevention.class);
private Deque classLoaders = new ArrayDeque<>();
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java
index 619b09940e..9ca3a765d1 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java
@@ -17,9 +17,20 @@ class ClassLoaderLifeCycleTest {
}
}
+ @Test
+ void shouldCreateWithLeakPreventionClassLoader() {
+ System.setProperty(ClassLoaderLifeCycle.PROPERTY, ClassLoaderLifeCycleWithLeakPrevention.NAME);
+ try {
+ ClassLoaderLifeCycle classLoaderLifeCycle = ClassLoaderLifeCycle.create();
+ assertThat(classLoaderLifeCycle).isInstanceOf(ClassLoaderLifeCycleWithLeakPrevention.class);
+ } finally {
+ System.clearProperty(ClassLoaderLifeCycle.PROPERTY);
+ }
+ }
+
@Test
void shouldCreateDefaultClassLoader() {
ClassLoaderLifeCycle classLoaderLifeCycle = ClassLoaderLifeCycle.create();
- assertThat(classLoaderLifeCycle).isInstanceOf(ClassLoaderLifeCycleWithLeakPrevention.class);
+ assertThat(classLoaderLifeCycle).isInstanceOf(SimpleClassLoaderLifeCycle.class);
}
}
From de3db6252e13502455aa3c743bb460d19c612790 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Wed, 12 Feb 2020 14:45:13 +0100
Subject: [PATCH 03/18] implemented restarter to move control over the restart
process to the core
---
.../sonia/scm/lifecycle/RestartEvent.java | 42 +++--------
.../RestartNotSupportedException.java | 4 ++
.../java/sonia/scm/lifecycle/Restarter.java | 25 +++++++
.../scm/lifecycle/RestartEventFactory.java | 15 ++++
.../sonia/scm/lifecycle/DefaultRestarter.java | 41 +++++++++++
.../lifecycle/modules/BootstrapModule.java | 4 ++
.../scm/plugin/DefaultPluginManager.java | 20 ++----
.../scm/update/MigrationWizardServlet.java | 12 +++-
.../scm/lifecycle/DefaultRestarterTest.java | 69 +++++++++++++++++++
.../scm/plugin/DefaultPluginManagerTest.java | 30 ++++----
.../update/MigrationWizardServletTest.java | 5 +-
.../sonia/scm/web/i18n/I18nServletTest.java | 3 +-
12 files changed, 201 insertions(+), 69 deletions(-)
rename {scm-webapp => scm-core}/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java (80%)
create mode 100644 scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
create mode 100644 scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java
create mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java
create mode 100644 scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java
diff --git a/scm-core/src/main/java/sonia/scm/lifecycle/RestartEvent.java b/scm-core/src/main/java/sonia/scm/lifecycle/RestartEvent.java
index 1978cb9d7c..1b692d8081 100644
--- a/scm-core/src/main/java/sonia/scm/lifecycle/RestartEvent.java
+++ b/scm-core/src/main/java/sonia/scm/lifecycle/RestartEvent.java
@@ -36,63 +36,37 @@ package sonia.scm.lifecycle;
import sonia.scm.event.Event;
/**
- * This event can be used to force a restart of the webapp context. The restart
- * event is useful during plugin development, because we don't have to restart
- * the whole server, to see our changes. The restart event could also be used
- * to install or upgrade plugins.
- *
- * But the restart event should be used carefully, because the whole context
- * will be restarted and that process could take some time.
- *
+ * This event indicates a forced restart of scm-manager.
* @author Sebastian Sdorra
* @since 2.0.0
*/
@Event
-public class RestartEvent
-{
+public class RestartEvent {
- /**
- * Constructs ...
- *
- *
- * @param cause
- * @param reason
- */
- public RestartEvent(Class> cause, String reason)
- {
+ private final Class> cause;
+ private final String reason;
+
+ RestartEvent(Class> cause, String reason) {
this.cause = cause;
this.reason = reason;
}
- //~--- get methods ----------------------------------------------------------
-
/**
* The class which has fired the restart event.
*
- *
* @return class which has fired the restart event
*/
- public Class> getCause()
- {
+ public Class> getCause() {
return cause;
}
/**
* Returns the reason for the restart.
*
- *
* @return reason for restart
*/
- public String getReason()
- {
+ public String getReason() {
return reason;
}
- //~--- fields ---------------------------------------------------------------
-
- /** cause of restart */
- private final Class> cause;
-
- /** reason for restart */
- private final String reason;
}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java b/scm-core/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
similarity index 80%
rename from scm-webapp/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
rename to scm-core/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
index 53ab75ff90..062f639096 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
+++ b/scm-core/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
@@ -4,6 +4,10 @@ package sonia.scm.lifecycle;
* Exception is thrown if a restart is not supported or a restart strategy is misconfigured.
*/
public class RestartNotSupportedException extends RuntimeException {
+ RestartNotSupportedException(String message) {
+ super(message);
+ }
+
RestartNotSupportedException(String message, Throwable cause) {
super(message, cause);
}
diff --git a/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java b/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
new file mode 100644
index 0000000000..547d26e391
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
@@ -0,0 +1,25 @@
+package sonia.scm.lifecycle;
+
+/**
+ * {@link Restarter} is able to restart scm-manager.
+ *
+ * @since 2.0.0
+ */
+public interface Restarter {
+
+ /**
+ * Return {@code true} if restarting scm-manager is supported.
+ *
+ * @return {@code true} if restart is supported
+ */
+ boolean isSupported();
+
+ /**
+ * Issues a restart. The method will fire a {@link RestartEvent} to notify the system about the upcoming restart.
+ * If restarting is not supported by the underlying platform a {@link RestartNotSupportedException} is thrown.
+ *
+ * @param cause cause of the restart. This should be the class which calls this method.
+ * @param reason reason for the required restart.
+ */
+ void restart(Class> cause, String reason);
+}
diff --git a/scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java b/scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java
new file mode 100644
index 0000000000..59d27fe4c2
--- /dev/null
+++ b/scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java
@@ -0,0 +1,15 @@
+package sonia.scm.lifecycle;
+
+/**
+ * Creates restart events for testing.
+ * This is required, because the constructor of {@link RestartEvent} is package private.
+ */
+public final class RestartEventFactory {
+
+ private RestartEventFactory(){}
+
+ public static RestartEvent create(Class> cause, String reason) {
+ return new RestartEvent(cause, reason);
+ }
+
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java
new file mode 100644
index 0000000000..0a787d239a
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java
@@ -0,0 +1,41 @@
+package sonia.scm.lifecycle;
+
+import com.google.common.annotations.VisibleForTesting;
+import sonia.scm.event.ScmEventBus;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
+public class DefaultRestarter implements Restarter {
+
+ private ScmEventBus eventBus;
+ private RestartStrategy strategy;
+
+ @Inject
+ public DefaultRestarter() {
+ this(
+ ScmEventBus.getInstance(),
+ RestartStrategy.get(Thread.currentThread().getContextClassLoader()).orElse(null)
+ );
+ }
+
+ @VisibleForTesting
+ DefaultRestarter(ScmEventBus eventBus, RestartStrategy strategy) {
+ this.eventBus = eventBus;
+ this.strategy = strategy;
+ }
+
+ @Override
+ public boolean isSupported() {
+ return strategy != null;
+ }
+
+ @Override
+ public void restart(Class> cause, String reason) {
+ if (!isSupported()) {
+ throw new RestartNotSupportedException("restarting is not supported");
+ }
+ eventBus.post(new RestartEvent(cause, reason));
+ }
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/BootstrapModule.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/BootstrapModule.java
index 06f6462af0..5ad623f8a4 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/BootstrapModule.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/modules/BootstrapModule.java
@@ -9,6 +9,8 @@ import sonia.scm.SCMContext;
import sonia.scm.SCMContextProvider;
import sonia.scm.io.DefaultFileSystem;
import sonia.scm.io.FileSystem;
+import sonia.scm.lifecycle.DefaultRestarter;
+import sonia.scm.lifecycle.Restarter;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.repository.xml.MetadataStore;
@@ -61,6 +63,8 @@ public class BootstrapModule extends AbstractModule {
bind(FileSystem.class, DefaultFileSystem.class);
+ bind(Restarter.class, DefaultRestarter.class);
+
// note CipherUtil uses an other generator
bind(CipherHandler.class).toInstance(CipherUtil.getInstance().getCipherHandler());
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
index f57d932e1e..731b7b12d5 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
@@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
import sonia.scm.NotFoundException;
import sonia.scm.event.ScmEventBus;
import sonia.scm.lifecycle.RestartEvent;
+import sonia.scm.lifecycle.Restarter;
import sonia.scm.version.Version;
import javax.inject.Inject;
@@ -68,20 +69,21 @@ public class DefaultPluginManager implements PluginManager {
private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginManager.class);
- private final ScmEventBus eventBus;
private final PluginLoader loader;
private final PluginCenter center;
private final PluginInstaller installer;
+ private final Restarter restarter;
+
private final Collection pendingInstallQueue = new ArrayList<>();
private final Collection pendingUninstallQueue = new ArrayList<>();
private final PluginDependencyTracker dependencyTracker = new PluginDependencyTracker();
@Inject
- public DefaultPluginManager(ScmEventBus eventBus, PluginLoader loader, PluginCenter center, PluginInstaller installer) {
- this.eventBus = eventBus;
+ public DefaultPluginManager(PluginLoader loader, PluginCenter center, PluginInstaller installer, Restarter restarter) {
this.loader = loader;
this.center = center;
this.installer = installer;
+ this.restarter = restarter;
this.computeInstallationDependencies();
}
@@ -242,16 +244,8 @@ public class DefaultPluginManager implements PluginManager {
}
}
- @VisibleForTesting
- void triggerRestart(String cause) {
- new Thread(() -> {
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- eventBus.post(new RestartEvent(PluginManager.class, cause));
- }).start();
+ private void triggerRestart(String cause) {
+ restarter.restart(PluginManager.class, cause);
}
private void cancelPending(List pendingInstallations) {
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 b0ee63c0e8..b518bef698 100644
--- a/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java
+++ b/scm-webapp/src/main/java/sonia/scm/update/MigrationWizardServlet.java
@@ -12,6 +12,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.event.ScmEventBus;
import sonia.scm.lifecycle.RestartEvent;
+import sonia.scm.lifecycle.Restarter;
import sonia.scm.update.repository.DefaultMigrationStrategyDAO;
import sonia.scm.update.repository.MigrationStrategy;
import sonia.scm.update.repository.V1Repository;
@@ -41,11 +42,13 @@ class MigrationWizardServlet extends HttpServlet {
private final XmlRepositoryV1UpdateStep repositoryV1UpdateStep;
private final DefaultMigrationStrategyDAO migrationStrategyDao;
+ private final Restarter restarter;
@Inject
- MigrationWizardServlet(XmlRepositoryV1UpdateStep repositoryV1UpdateStep, DefaultMigrationStrategyDAO migrationStrategyDao) {
+ MigrationWizardServlet(XmlRepositoryV1UpdateStep repositoryV1UpdateStep, DefaultMigrationStrategyDAO migrationStrategyDao, Restarter restarter) {
this.repositoryV1UpdateStep = repositoryV1UpdateStep;
this.migrationStrategyDao = migrationStrategyDao;
+ this.restarter = restarter;
}
@Override
@@ -121,7 +124,12 @@ class MigrationWizardServlet extends HttpServlet {
ThreadContext.bind(new Subject.Builder(new DefaultSecurityManager()).authenticated(false).buildSubject());
- ScmEventBus.getInstance().post(new RestartEvent(MigrationWizardServlet.class, "wrote migration data"));
+ if (restarter.isSupported()) {
+ restarter.restart(MigrationWizardServlet.class, "wrote migration data");
+ } else {
+ LOG.error("Restarting is not supported on this platform.");
+ LOG.error("Please do a manual restart");
+ }
}
private List getRepositoryLineEntries() {
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java
new file mode 100644
index 0000000000..822b7fad64
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java
@@ -0,0 +1,69 @@
+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;
+import org.mockito.Captor;
+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;
+
+@ExtendWith(MockitoExtension.class)
+class DefaultRestarterTest {
+
+ @Mock
+ private ScmEventBus eventBus;
+
+ @Captor
+ private ArgumentCaptor eventCaptor;
+
+ @Test
+ void shouldLoadStrategyOnCreation() {
+ System.setProperty(RestartStrategyFactory.PROPERTY_STRATEGY, ExitRestartStrategy.NAME);
+ try {
+ DefaultRestarter restarter = new DefaultRestarter();
+ assertThat(restarter.isSupported()).isTrue();
+ } finally {
+ System.clearProperty(RestartStrategyFactory.PROPERTY_STRATEGY);
+ }
+ }
+
+ @Test
+ void shouldReturnFalseIfRestartStrategyIsNotAvailable() {
+ DefaultRestarter restarter = new DefaultRestarter(eventBus, null);
+ assertThat(restarter.isSupported()).isFalse();
+ }
+
+ @Test
+ void shouldReturnTrueIfRestartStrategyIsAvailable() {
+ DefaultRestarter restarter = new DefaultRestarter();
+ assertThat(restarter.isSupported()).isTrue();
+ }
+
+ @Test
+ void shouldThrowRestartNotSupportedException() {
+ DefaultRestarter restarter = new DefaultRestarter(eventBus,null);
+ assertThrows(
+ RestartNotSupportedException.class, () -> restarter.restart(DefaultRestarterTest.class, "test")
+ );
+ }
+
+ @Test
+ void shouldFireRestartEvent() {
+ DefaultRestarter restarter = new DefaultRestarter(eventBus, new ExitRestartStrategy());
+ restarter.restart(DefaultRestarterTest.class, "testing");
+
+ verify(eventBus).post(eventCaptor.capture());
+
+ RestartEvent event = eventCaptor.getValue();
+ assertThat(event.getCause()).isEqualTo(DefaultRestarterTest.class);
+ assertThat(event.getReason()).isEqualTo("testing");
+ }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
index f6aa1d7301..ed575734f2 100644
--- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
@@ -12,10 +12,12 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junitpioneer.jupiter.TempDirectory;
import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.NotFoundException;
import sonia.scm.ScmConstraintViolationException;
+import sonia.scm.lifecycle.Restarter;
import java.io.IOException;
import java.nio.file.Files;
@@ -54,13 +56,15 @@ class DefaultPluginManagerTest {
@Mock
private PluginInstaller installer;
+ @Mock
+ private Restarter restarter;
+
+ @InjectMocks
private DefaultPluginManager manager;
@Mock
private Subject subject;
- private boolean restartTriggered = false;
-
@BeforeEach
void mockInstaller() {
lenient().when(installer.install(any())).then(ic -> {
@@ -69,16 +73,6 @@ class DefaultPluginManagerTest {
});
}
- @BeforeEach
- void createPluginManagerToTestWithCapturedRestart() {
- manager = new DefaultPluginManager(null, loader, center, installer) { // event bus is only used in restart and this is replaced here
- @Override
- void triggerRestart(String cause) {
- restartTriggered = true;
- }
- };
- }
-
@Nested
class WithAdminPermissions {
@@ -185,7 +179,7 @@ class DefaultPluginManagerTest {
manager.install("scm-git-plugin", false);
verify(installer).install(git);
- assertThat(restartTriggered).isFalse();
+ verify(restarter, never()).restart(any(), any());
}
@Test
@@ -263,7 +257,7 @@ class DefaultPluginManagerTest {
manager.install("scm-git-plugin", true);
verify(installer).install(git);
- assertThat(restartTriggered).isTrue();
+ verify(restarter).restart(any(), any());
}
@Test
@@ -272,7 +266,7 @@ class DefaultPluginManagerTest {
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(gitInstalled));
manager.install("scm-git-plugin", true);
- assertThat(restartTriggered).isFalse();
+ verify(restarter, never()).restart(any(), any());
}
@Test
@@ -294,14 +288,14 @@ class DefaultPluginManagerTest {
manager.install("scm-review-plugin", false);
manager.executePendingAndRestart();
- assertThat(restartTriggered).isTrue();
+ verify(restarter).restart(any(), any());
}
@Test
void shouldNotSendRestartEventWithoutPendingPlugins() {
manager.executePendingAndRestart();
- assertThat(restartTriggered).isFalse();
+ verify(restarter, never()).restart(any(), any());
}
@Test
@@ -452,7 +446,7 @@ class DefaultPluginManagerTest {
manager.executePendingAndRestart();
- assertThat(restartTriggered).isTrue();
+ verify(restarter).restart(any(), any());
}
@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 683c446af7..286e8855f3 100644
--- a/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/update/MigrationWizardServletTest.java
@@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import sonia.scm.lifecycle.Restarter;
import sonia.scm.update.repository.DefaultMigrationStrategyDAO;
import sonia.scm.update.repository.MigrationStrategy;
import sonia.scm.update.repository.V1Repository;
@@ -27,6 +28,8 @@ class MigrationWizardServletTest {
XmlRepositoryV1UpdateStep updateStep;
@Mock
DefaultMigrationStrategyDAO migrationStrategyDao;
+ @Mock
+ Restarter restarter;
@Mock
HttpServletRequest request;
@@ -40,7 +43,7 @@ class MigrationWizardServletTest {
@BeforeEach
void initServlet() {
- servlet = new MigrationWizardServlet(updateStep, migrationStrategyDao) {
+ servlet = new MigrationWizardServlet(updateStep, migrationStrategyDao, restarter) {
@Override
void respondWithTemplate(HttpServletResponse resp, Map model, String templateName) {
renderedTemplateName = templateName;
diff --git a/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java b/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java
index f177d44e63..e060d7a2e8 100644
--- a/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java
@@ -20,6 +20,7 @@ import sonia.scm.lifecycle.RestartEvent;
import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager;
import sonia.scm.event.ScmEventBus;
+import sonia.scm.lifecycle.RestartEventFactory;
import sonia.scm.plugin.PluginLoader;
import javax.servlet.http.HttpServletRequest;
@@ -114,7 +115,7 @@ public class I18nServletTest {
public void shouldCleanCacheOnRestartEvent() {
ScmEventBus.getInstance().register(servlet);
- ScmEventBus.getInstance().post(new RestartEvent(I18nServlet.class, "Restart to reload the plugin resources"));
+ ScmEventBus.getInstance().post(RestartEventFactory.create(I18nServlet.class, "Restart to reload the plugin resources"));
verify(cache).clear();
}
From b97dfd7dedd15582cadb271dcadaac644bec3bc9 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Tue, 24 Mar 2020 10:19:10 +0100
Subject: [PATCH 04/18] added missing license headers
---
.../RestartNotSupportedException.java | 23 +++++++++++++++++++
.../java/sonia/scm/lifecycle/Restarter.java | 23 +++++++++++++++++++
.../scm/lifecycle/RestartEventFactory.java | 23 +++++++++++++++++++
.../java/sonia/scm/lifecycle/CLibrary.java | 23 +++++++++++++++++++
.../sonia/scm/lifecycle/DefaultRestarter.java | 23 +++++++++++++++++++
.../scm/lifecycle/ExitRestartStrategy.java | 23 +++++++++++++++++++
.../scm/lifecycle/PosixRestartStrategy.java | 23 +++++++++++++++++++
.../scm/lifecycle/RestartStrategyFactory.java | 23 +++++++++++++++++++
.../scm/lifecycle/DefaultRestarterTest.java | 23 +++++++++++++++++++
.../lifecycle/ExitRestartStrategyTest.java | 23 +++++++++++++++++++
.../scm/lifecycle/RestartStrategyTest.java | 23 +++++++++++++++++++
11 files changed, 253 insertions(+)
diff --git a/scm-core/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java b/scm-core/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
index 062f639096..7b560f7222 100644
--- a/scm-core/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
+++ b/scm-core/src/main/java/sonia/scm/lifecycle/RestartNotSupportedException.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
/**
diff --git a/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java b/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
index 547d26e391..0b6c18f0a9 100644
--- a/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
+++ b/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
/**
diff --git a/scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java b/scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java
index 59d27fe4c2..d58897dd40 100644
--- a/scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java
+++ b/scm-test/src/main/java/sonia/scm/lifecycle/RestartEventFactory.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
/**
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java
index 91b82ce3d1..cee163eb8b 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/CLibrary.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
/**
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 0a787d239a..cb85852f83 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/DefaultRestarter.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
import com.google.common.annotations.VisibleForTesting;
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
index dfa3d72f82..d8eb2fd22a 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
import com.google.common.annotations.VisibleForTesting;
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
index d7d64b9e6d..6d5477dd78 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
import org.slf4j.Logger;
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
index a6fb371aa7..e522f3ad41 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
import com.google.common.base.Strings;
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 822b7fad64..a137d7a947 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/DefaultRestarterTest.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
import com.github.legman.Subscribe;
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
index 078e218b75..8fba403120 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
import org.junit.jupiter.api.BeforeEach;
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
index 914c506aa2..3301e46151 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
@@ -1,3 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
package sonia.scm.lifecycle;
import com.google.common.base.Strings;
From 651a9a561a73775caa686754ba20503373735cd7 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Tue, 24 Mar 2020 13:05:41 +0100
Subject: [PATCH 05/18] fix test execution on intellij
---
pom.xml | 7 +++++++
scm-test/pom.xml | 16 +++++++++++-----
2 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/pom.xml b/pom.xml
index c3f6cb139f..c38bb5ef84 100644
--- a/pom.xml
+++ b/pom.xml
@@ -363,6 +363,13 @@
test
+
+ junit
+ junit
+ 4.13
+ test
+
+
org.hamcresthamcrest-core
diff --git a/scm-test/pom.xml b/scm-test/pom.xml
index 05d773a6f8..a24e048f55 100644
--- a/scm-test/pom.xml
+++ b/scm-test/pom.xml
@@ -38,7 +38,7 @@
scm-test2.0.0-SNAPSHOTscm-test
-
+
@@ -53,6 +53,12 @@
2.0.0-SNAPSHOT
+
+ junit
+ junit
+ compile
+
+
com.github.sdorrashiro-unit
@@ -88,17 +94,17 @@
-
+
-
+
-
+
maven.tmatesoft.comtmatesoft release repositoryhttps://maven.tmatesoft.com/content/repositories/releases
-
+
From 2873c44b5264cc8bbf05bbd9a7532ee9e72105da Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Tue, 24 Mar 2020 15:01:39 +0100
Subject: [PATCH 06/18] show restart checkbox only if restarting is supported
---
scm-ui/ui-webapp/public/locales/de/admin.json | 3 +-
scm-ui/ui-webapp/public/locales/en/admin.json | 3 +-
.../admin/plugins/components/PluginModal.tsx | 26 +++++++++++-----
.../scm/api/v2/resources/PluginDtoMapper.java | 23 +++++++++++---
.../PluginDtoCollectionMapperTest.java | 23 ++++++++++++--
.../api/v2/resources/PluginDtoMapperTest.java | 31 ++++++++++++++++++-
6 files changed, 93 insertions(+), 16 deletions(-)
diff --git a/scm-ui/ui-webapp/public/locales/de/admin.json b/scm-ui/ui-webapp/public/locales/de/admin.json
index e65a10eb85..b2ffbfe282 100644
--- a/scm-ui/ui-webapp/public/locales/de/admin.json
+++ b/scm-ui/ui-webapp/public/locales/de/admin.json
@@ -69,7 +69,8 @@
"restartNotification": "Der SCM-Manager Kontext sollte nur neu gestartet werden, wenn aktuell niemand damit arbeitet.",
"executePending": "Die folgenden Plugin-Änderungen werden ausgeführt. Anschließend wird der SCM-Manager Kontext neu gestartet.",
"cancelPending": "Die folgenden Plugin-Änderungen werden abgebrochen und zurückgesetzt.",
- "updateAllInfo": "Die folgenden Plugins werden aktualisiert. Die Änderungen werden nach dem nächsten Neustart wirksam."
+ "updateAllInfo": "Die folgenden Plugins werden aktualisiert. Die Änderungen werden nach dem nächsten Neustart wirksam.",
+ "manualRestartRequired": "Nach dem die Plugin-Änderung durchgeführt wurde, muss SCM-Manager neu gestartet werden."
}
},
"repositoryRole": {
diff --git a/scm-ui/ui-webapp/public/locales/en/admin.json b/scm-ui/ui-webapp/public/locales/en/admin.json
index 5e7e44dcd2..a244a495b5 100644
--- a/scm-ui/ui-webapp/public/locales/en/admin.json
+++ b/scm-ui/ui-webapp/public/locales/en/admin.json
@@ -69,7 +69,8 @@
"restartNotification": "You should only restart the scm-manager context if no one else is currently working with it.",
"executePending": "The following plugin changes will be executed and after that the scm-manager context will be restarted.",
"cancelPending": "The following plugin changes will be canceled.",
- "updateAllInfo": "The following plugin changes will be executed. You need to restart the scm-manager to make these changes effective."
+ "updateAllInfo": "The following plugin changes will be executed. You need to restart the scm-manager to make these changes effective.",
+ "manualRestartRequired": "After the plugin change has been made, scm-manager must be restarted."
}
},
"repositoryRole": {
diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/PluginModal.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/PluginModal.tsx
index 7c1d12a6b2..1074588e59 100644
--- a/scm-ui/ui-webapp/src/admin/plugins/components/PluginModal.tsx
+++ b/scm-ui/ui-webapp/src/admin/plugins/components/PluginModal.tsx
@@ -214,8 +214,25 @@ class PluginModal extends React.Component {
});
};
- render() {
+ createRestartSectionContent = () => {
const { restart } = this.state;
+ const { plugin, pluginAction, t } = this.props;
+
+ if (plugin._links[pluginAction + "WithRestart"]) {
+ return (
+
+ );
+ } else {
+ return {t("plugins.modal.manualRestartRequired")};
+ }
+ };
+
+ render() {
const { plugin, pluginAction, onClose, t } = this.props;
const body = (
@@ -262,12 +279,7 @@ class PluginModal extends React.Component {
-
+ {this.createRestartSectionContent()}
{this.renderNotifications()}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java
index 8304487732..ff2522c352 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java
@@ -21,12 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Links;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
+import sonia.scm.lifecycle.Restarter;
import sonia.scm.plugin.AvailablePlugin;
import sonia.scm.plugin.InstalledPlugin;
import sonia.scm.plugin.Plugin;
@@ -47,6 +48,9 @@ public abstract class PluginDtoMapper {
@Inject
private ResourceLinks resourceLinks;
+ @Inject
+ private Restarter restarter;
+
public abstract void map(PluginInformation plugin, @MappingTarget PluginDto dto);
public PluginDto mapInstalled(InstalledPlugin plugin, List availablePlugins) {
@@ -78,12 +82,20 @@ public abstract class PluginDtoMapper {
.self(information.getName()));
if (!plugin.isPending() && PluginPermissions.manage().isPermitted()) {
- links.single(link("install", resourceLinks.availablePlugin().install(information.getName())));
+ String href = resourceLinks.availablePlugin().install(information.getName());
+ appendLink(links, "install", href);
}
return new PluginDto(links.build());
}
+ private void appendLink(Links.Builder links, String name, String href) {
+ links.single(link(name, href));
+ if (restarter.isSupported()) {
+ links.single(link(name + "WithRestart", href + "?restart=true"));
+ }
+ }
+
private PluginDto createDtoForInstalled(InstalledPlugin plugin, List availablePlugins) {
PluginInformation information = plugin.getDescriptor().getInformation();
Optional availablePlugin = checkForUpdates(plugin, availablePlugins);
@@ -96,13 +108,16 @@ public abstract class PluginDtoMapper {
&& !availablePlugin.get().isPending()
&& PluginPermissions.manage().isPermitted()
) {
- links.single(link("update", resourceLinks.availablePlugin().install(information.getName())));
+ String href = resourceLinks.availablePlugin().install(information.getName());
+ appendLink(links, "update", href);
}
+
if (plugin.isUninstallable()
&& (!availablePlugin.isPresent() || !availablePlugin.get().isPending())
&& PluginPermissions.manage().isPermitted()
) {
- links.single(link("uninstall", resourceLinks.installedPlugin().uninstall(information.getName())));
+ String href = resourceLinks.installedPlugin().uninstall(information.getName());
+ appendLink(links, "uninstall", href);
}
PluginDto dto = new PluginDto(links.build());
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapperTest.java
index 0029296882..a3fa801a22 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapperTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapperTest.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;
@@ -36,6 +36,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import sonia.scm.lifecycle.Restarter;
import sonia.scm.plugin.AvailablePlugin;
import sonia.scm.plugin.AvailablePluginDescriptor;
import sonia.scm.plugin.InstalledPlugin;
@@ -59,6 +60,9 @@ class PluginDtoCollectionMapperTest {
ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("/"));
+ @Mock
+ private Restarter restarter;
+
@InjectMocks
PluginDtoMapperImpl pluginDtoMapper;
@@ -142,7 +146,7 @@ class PluginDtoCollectionMapperTest {
}
@Test
- void shouldAddInstallLinkForNewVersionWhenPermitted() {
+ void shouldAddUpdateLinkForNewVersionWhenPermitted() {
when(subject.isPermitted("plugin:manage")).thenReturn(true);
PluginDtoCollectionMapper mapper = new PluginDtoCollectionMapper(resourceLinks, pluginDtoMapper, manager);
@@ -154,6 +158,21 @@ class PluginDtoCollectionMapperTest {
assertThat(plugin.getLinks().getLinkBy("update")).isNotEmpty();
}
+ @Test
+ void shouldAddUpdateWithRestartLinkForNewVersionWhenPermitted() {
+ when(restarter.isSupported()).thenReturn(true);
+ when(subject.isPermitted("plugin:manage")).thenReturn(true);
+ PluginDtoCollectionMapper mapper = new PluginDtoCollectionMapper(resourceLinks, pluginDtoMapper, manager);
+
+ HalRepresentation result = mapper.mapInstalled(
+ singletonList(createInstalledPlugin("scm-some-plugin", "1")),
+ singletonList(createAvailablePlugin("scm-some-plugin", "2")));
+
+ PluginDto plugin = getPluginDtoFromResult(result);
+ assertThat(plugin.getLinks().getLinkBy("update")).isNotEmpty();
+ assertThat(plugin.getLinks().getLinkBy("updateWithRestart")).isNotEmpty();
+ }
+
@Test
void shouldSetInstalledPluginPendingWhenCorrespondingAvailablePluginIsPending() {
when(subject.isPermitted("plugin:manage")).thenReturn(true);
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java
index c4ccbe521d..51a7001140 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.api.v2.resources;
import com.google.common.collect.ImmutableSet;
@@ -35,6 +35,7 @@ import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import sonia.scm.lifecycle.Restarter;
import sonia.scm.plugin.AvailablePlugin;
import sonia.scm.plugin.AvailablePluginDescriptor;
import sonia.scm.plugin.InstalledPlugin;
@@ -56,6 +57,9 @@ class PluginDtoMapperTest {
@SuppressWarnings("unused") // Is injected
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("https://hitchhiker.com/"));
+ @Mock
+ private Restarter restarter;
+
@InjectMocks
private PluginDtoMapperImpl mapper;
@@ -122,6 +126,7 @@ class PluginDtoMapperTest {
PluginDto dto = mapper.mapAvailable(plugin);
assertThat(dto.getLinks().getLinkBy("install")).isEmpty();
+ assertThat(dto.getLinks().getLinkBy("installWithRestart")).isEmpty();
}
@Test
@@ -134,6 +139,17 @@ class PluginDtoMapperTest {
.isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/install");
}
+ @Test
+ void shouldAppendInstallWithRestartLink() {
+ when(restarter.isSupported()).thenReturn(true);
+ when(subject.isPermitted("plugin:manage")).thenReturn(true);
+ AvailablePlugin plugin = createAvailable(createPluginInformation());
+
+ PluginDto dto = mapper.mapAvailable(plugin);
+ assertThat(dto.getLinks().getLinkBy("installWithRestart").get().getHref())
+ .isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/install?restart=true");
+ }
+
@Test
void shouldReturnMiscellaneousIfCategoryIsNull() {
PluginInformation information = createPluginInformation();
@@ -162,4 +178,17 @@ class PluginDtoMapperTest {
assertThat(dto.getLinks().getLinkBy("uninstall").get().getHref())
.isEqualTo("https://hitchhiker.com/v2/plugins/installed/scm-cas-plugin/uninstall");
}
+
+ @Test
+ void shouldAppendUninstallWithRestartLink() {
+ when(restarter.isSupported()).thenReturn(true);
+ when(subject.isPermitted("plugin:manage")).thenReturn(true);
+
+ InstalledPlugin plugin = createInstalled(createPluginInformation());
+ when(plugin.isUninstallable()).thenReturn(true);
+
+ PluginDto dto = mapper.mapInstalled(plugin, emptyList());
+ assertThat(dto.getLinks().getLinkBy("uninstallWithRestart").get().getHref())
+ .isEqualTo("https://hitchhiker.com/v2/plugins/installed/scm-cas-plugin/uninstall?restart=true");
+ }
}
From fbc86e7d333790974bcf8c56d98cb46883c560e8 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Tue, 24 Mar 2020 15:58:33 +0100
Subject: [PATCH 07/18] show execute and restart button only if restarting is
supported
---
scm-ui/ui-webapp/public/locales/de/admin.json | 9 +-
scm-ui/ui-webapp/public/locales/en/admin.json | 9 +-
.../components/ExecutePendingModal.tsx | 61 +----------
.../components/PendingPluginsQueue.tsx | 66 ++++++++++++
.../plugins/components/ShowPendingModal.tsx | 70 ++++++++++++
.../plugins/containers/PluginsOverview.tsx | 101 ++++++++++++------
.../v2/resources/PendingPluginResource.java | 11 +-
.../resources/PendingPluginResourceTest.java | 26 ++++-
8 files changed, 252 insertions(+), 101 deletions(-)
create mode 100644 scm-ui/ui-webapp/src/admin/plugins/components/PendingPluginsQueue.tsx
create mode 100644 scm-ui/ui-webapp/src/admin/plugins/components/ShowPendingModal.tsx
diff --git a/scm-ui/ui-webapp/public/locales/de/admin.json b/scm-ui/ui-webapp/public/locales/de/admin.json
index b2ffbfe282..a60b01046c 100644
--- a/scm-ui/ui-webapp/public/locales/de/admin.json
+++ b/scm-ui/ui-webapp/public/locales/de/admin.json
@@ -30,6 +30,7 @@
"installedNavLink": "Installiert",
"availableNavLink": "Verfügbar"
},
+ "showPending": "Änderungen anzeigen",
"executePending": "Änderungen ausführen",
"outdatedPlugins": "{{count}} veraltetes Plugin",
"outdatedPlugins_plural": "{{count}} veraltete Plugins",
@@ -55,6 +56,7 @@
"executeAndRestart": "Ausführen und Neustarten",
"updateAll": "Alle Plugins aktualisieren",
"abort": "Abbrechen",
+ "close": "Schließen",
"author": "Autor",
"version": "Version",
"currentVersion": "Installierte Version",
@@ -66,11 +68,12 @@
"uninstalledNotification": "Das Plugin wurde erfolgreich installiert. Um Änderungen an der UI zu sehen, muss die Seite neu geladen werden:",
"executedChangesNotification": "Die Plugin Änderungen wurden erfolgreich durchgeführt. Um Änderungen an der UI zu sehen, muss die Seite neu geladen werden:",
"reload": "jetzt neu laden",
- "restartNotification": "Der SCM-Manager Kontext sollte nur neu gestartet werden, wenn aktuell niemand damit arbeitet.",
- "executePending": "Die folgenden Plugin-Änderungen werden ausgeführt. Anschließend wird der SCM-Manager Kontext neu gestartet.",
+ "restartNotification": "Der SCM-Manager sollte nur neu gestartet werden, wenn aktuell niemand damit arbeitet.",
+ "executePending": "Die folgenden Plugin-Änderungen werden ausgeführt. Anschließend wird SCM-Manager neu gestartet.",
"cancelPending": "Die folgenden Plugin-Änderungen werden abgebrochen und zurückgesetzt.",
"updateAllInfo": "Die folgenden Plugins werden aktualisiert. Die Änderungen werden nach dem nächsten Neustart wirksam.",
- "manualRestartRequired": "Nach dem die Plugin-Änderung durchgeführt wurde, muss SCM-Manager neu gestartet werden."
+ "manualRestartRequired": "Nach dem die Plugin-Änderung durchgeführt wurde, muss SCM-Manager neu gestartet werden.",
+ "showPending": "Um die folgenden Plugin-Änderungen auszuführen muss SCM-Manager neu gestartet werden."
}
},
"repositoryRole": {
diff --git a/scm-ui/ui-webapp/public/locales/en/admin.json b/scm-ui/ui-webapp/public/locales/en/admin.json
index a244a495b5..953cf335b4 100644
--- a/scm-ui/ui-webapp/public/locales/en/admin.json
+++ b/scm-ui/ui-webapp/public/locales/en/admin.json
@@ -30,6 +30,7 @@
"installedNavLink": "Installed",
"availableNavLink": "Available"
},
+ "showPending": "Show changes",
"executePending": "Execute changes",
"outdatedPlugins": "{{count}} outdated plugin",
"outdatedPlugins_plural": "{{count}} outdated plugins",
@@ -55,6 +56,7 @@
"executeAndRestart": "Execute and Restart",
"updateAll": "Update all plugins",
"abort": "Abort",
+ "close": "Close",
"author": "Author",
"version": "Version",
"currentVersion": "Installed version",
@@ -66,11 +68,12 @@
"uninstalledNotification": "Successfully uninstalled plugin. You have to reload the page, to see ui changes:",
"executedChangesNotification": "Successfully executed plugin changes. You have to reload the page, to see ui changes:",
"reload": "reload now",
- "restartNotification": "You should only restart the scm-manager context if no one else is currently working with it.",
- "executePending": "The following plugin changes will be executed and after that the scm-manager context will be restarted.",
+ "restartNotification": "You should only restart scm-manager if no one else is currently working with it.",
+ "executePending": "The following plugin changes will be executed and after that the scm-manager will be restarted.",
"cancelPending": "The following plugin changes will be canceled.",
"updateAllInfo": "The following plugin changes will be executed. You need to restart the scm-manager to make these changes effective.",
- "manualRestartRequired": "After the plugin change has been made, scm-manager must be restarted."
+ "manualRestartRequired": "After the plugin change has been made, scm-manager must be restarted.",
+ "showPending": "To execute the following plugin changes, scm-manager must be restarted."
}
},
"repositoryRole": {
diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/ExecutePendingModal.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/ExecutePendingModal.tsx
index c0b35a4027..fbec769aaa 100644
--- a/scm-ui/ui-webapp/src/admin/plugins/components/ExecutePendingModal.tsx
+++ b/scm-ui/ui-webapp/src/admin/plugins/components/ExecutePendingModal.tsx
@@ -27,6 +27,7 @@ import { PendingPlugins } from "@scm-manager/ui-types";
import { WithTranslation, withTranslation } from "react-i18next";
import waitForRestart from "./waitForRestart";
import SuccessNotification from "./SuccessNotification";
+import PendingPluginsQueue from "./PendingPluginsQueue";
type Props = WithTranslation & {
onClose: () => void;
@@ -85,70 +86,14 @@ class ExecutePendingModal extends React.Component {
});
};
- renderInstallQueue = () => {
- const { pendingPlugins, t } = this.props;
- return (
- <>
- {pendingPlugins._embedded && pendingPlugins._embedded.new.length > 0 && (
- <>
- {t("plugins.modal.installQueue")}
-
diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/PendingPluginsQueue.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/PendingPluginsQueue.tsx
new file mode 100644
index 0000000000..f8fd298674
--- /dev/null
+++ b/scm-ui/ui-webapp/src/admin/plugins/components/PendingPluginsQueue.tsx
@@ -0,0 +1,66 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import React, { FC } from "react";
+import { PendingPlugins } from "@scm-manager/ui-types";
+import { useTranslation } from "react-i18next";
+
+type Props = {
+ pendingPlugins: PendingPlugins;
+};
+
+type SectionProps = Props & {
+ type: string;
+ label: string;
+};
+
+const Section: FC = ({ pendingPlugins, type, label }) => {
+ const plugins = pendingPlugins?._embedded[type];
+ if (!plugins || plugins.length === 0) {
+ return null;
+ }
+ return (
+ <>
+ {label}
+
+ {plugins.map(plugin => (
+
{plugin.name}
+ ))}
+
+ >
+ );
+};
+
+const PendingPluginsQueue: FC = ({ pendingPlugins }) => {
+ const [t] = useTranslation("admin");
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default PendingPluginsQueue;
diff --git a/scm-ui/ui-webapp/src/admin/plugins/components/ShowPendingModal.tsx b/scm-ui/ui-webapp/src/admin/plugins/components/ShowPendingModal.tsx
new file mode 100644
index 0000000000..d28b51f927
--- /dev/null
+++ b/scm-ui/ui-webapp/src/admin/plugins/components/ShowPendingModal.tsx
@@ -0,0 +1,70 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+import React, { FC } from "react";
+import { Button, Modal, Notification } from "@scm-manager/ui-components";
+import { PendingPlugins } from "@scm-manager/ui-types";
+import { useTranslation } from "react-i18next";
+import PendingPluginsQueue from "./PendingPluginsQueue";
+
+
+type ModalBodyProps = {
+ pendingPlugins: PendingPlugins;
+};
+
+const ModalBody: FC = ({ pendingPlugins }) => {
+ const [t] = useTranslation("admin");
+ return (
+ <>
+
+ {{/content}}
+
+{{/layout}}
From 4ad01c210f82419c8dc77ed0b70172c00abe966a Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Wed, 25 Mar 2020 08:38:13 +0100
Subject: [PATCH 09/18] remove error prone InjectionContextRestartStrategy
---
scm-webapp/pom.xml | 8 -
.../InjectionContextRestartStrategy.java | 121 -----------
.../scm/lifecycle/RestartStrategyFactory.java | 21 +-
.../classloading/ClassLoaderLifeCycle.java | 11 +-
...lassLoaderLifeCycleWithLeakPrevention.java | 189 ------------------
.../classloading/LoggingAdapter.java | 68 -------
.../InjectionContextRestartStrategyTest.java | 119 -----------
.../scm/lifecycle/RestartStrategyTest.java | 31 ++-
.../ClassLoaderLifeCycleTest.java | 24 +--
...LoaderLifeCycleWithLeakPreventionTest.java | 140 -------------
10 files changed, 41 insertions(+), 691 deletions(-)
delete mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
delete mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java
delete mode 100644 scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/LoggingAdapter.java
delete mode 100644 scm-webapp/src/test/java/sonia/scm/lifecycle/InjectionContextRestartStrategyTest.java
delete mode 100644 scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPreventionTest.java
diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml
index 17025a6a46..24ba16333a 100644
--- a/scm-webapp/pom.xml
+++ b/scm-webapp/pom.xml
@@ -312,14 +312,6 @@
1.23
-
-
-
- se.jiderhamn.classloader-leak-prevention
- classloader-leak-prevention-core
- 2.7.0
-
-
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
deleted file mode 100644
index 89c2334b69..0000000000
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/InjectionContextRestartStrategy.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2020-present Cloudogu GmbH and Contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package sonia.scm.lifecycle;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sonia.scm.event.RecreateEventBusEvent;
-import sonia.scm.event.ScmEventBus;
-import sonia.scm.event.ShutdownEventBusEvent;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * Restart strategy which tries to free, every resource used by the context, starts gc and re initializes the context.
- * Warning: This strategy should only be used with an classloader lifecycle which protects the
- * created plugin classloader from classloader leaks.
- */
-class InjectionContextRestartStrategy implements RestartStrategy {
-
- static final String NAME = "context";
-
- private static final String DISABLE_RESTART_PROPERTY = "sonia.scm.restart.disable";
- private static final String WAIT_PROPERTY = "sonia.scm.restart.wait";
- private static final String DISABLE_GC_PROPERTY = "sonia.scm.restart.disable-gc";
-
- private static final AtomicLong INSTANCE_COUNTER = new AtomicLong();
- private static final Logger LOG = LoggerFactory.getLogger(InjectionContextRestartStrategy.class);
-
- private boolean restartEnabled = !Boolean.getBoolean(DISABLE_RESTART_PROPERTY);
- private long waitInMs = Integer.getInteger(WAIT_PROPERTY, 250);
- private boolean gcEnabled = !Boolean.getBoolean(DISABLE_GC_PROPERTY);
-
- private final ClassLoader webAppClassLoader;
-
- InjectionContextRestartStrategy(ClassLoader webAppClassLoader) {
- this.webAppClassLoader = webAppClassLoader;
- }
-
- @VisibleForTesting
- void setWaitInMs(long waitInMs) {
- this.waitInMs = waitInMs;
- }
-
- @VisibleForTesting
- void setGcEnabled(boolean gcEnabled) {
- this.gcEnabled = gcEnabled;
- }
-
- @Override
- public void restart(InjectionContext context) {
- stop(context);
- if (restartEnabled) {
- start(context);
- } else {
- LOG.warn("restarting context is disabled");
- }
- }
-
- @SuppressWarnings("squid:S1215") // suppress explicit gc call warning
- private void start(InjectionContext context) {
- LOG.debug("use WebAppClassLoader as ContextClassLoader, to avoid ClassLoader leaks");
- Thread.currentThread().setContextClassLoader(webAppClassLoader);
-
- LOG.warn("send recreate eventbus event");
- ScmEventBus.getInstance().post(new RecreateEventBusEvent());
-
- // restart context delayed, to avoid timing problems
- new Thread(() -> {
- try {
- if (gcEnabled){
- LOG.info("call gc to clean up memory from old instances");
- System.gc();
- }
-
- LOG.info("wait {}ms before re starting the context", waitInMs);
- Thread.sleep(waitInMs);
-
- LOG.warn("reinitialize injection context");
- context.initialize();
-
- LOG.debug("register injection context on new eventbus");
- ScmEventBus.getInstance().register(context);
- } catch ( Exception ex) {
- LOG.error("failed to restart", ex);
- }
- }, "Delayed-Restart-" + INSTANCE_COUNTER.incrementAndGet()).start();
- }
-
- private void stop(InjectionContext context) {
- LOG.warn("destroy injection context");
- context.destroy();
-
- if (!restartEnabled) {
- // shutdown eventbus, but do this only if restart is disabled
- ScmEventBus.getInstance().post(new ShutdownEventBusEvent());
- }
- }
-}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
index e522f3ad41..4520bb3ddf 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategyFactory.java
@@ -27,6 +27,8 @@ import com.google.common.base.Strings;
import sonia.scm.PlatformType;
import sonia.scm.util.SystemUtil;
+import java.lang.reflect.Constructor;
+
final class RestartStrategyFactory {
/**
@@ -61,22 +63,29 @@ final class RestartStrategyFactory {
return null;
} else if (ExitRestartStrategy.NAME.equalsIgnoreCase(property)) {
return new ExitRestartStrategy();
- } else if (InjectionContextRestartStrategy.NAME.equalsIgnoreCase(property)) {
- return new InjectionContextRestartStrategy(webAppClassLoader);
} else {
- return fromClassName(property);
+ return fromClassName(property, webAppClassLoader);
}
}
- private static RestartStrategy fromClassName(String property) {
+ private static RestartStrategy fromClassName(String className, ClassLoader classLoader) {
try {
- Class extends RestartStrategy> rsClass = Class.forName(property).asSubclass(RestartStrategy.class);
- return rsClass.getConstructor().newInstance();
+ Class extends RestartStrategy> rsClass = Class.forName(className).asSubclass(RestartStrategy.class);
+ return createInstance(rsClass, classLoader);
} catch (Exception e) {
throw new RestartNotSupportedException("failed to create restart strategy from property", e);
}
}
+ private static RestartStrategy createInstance(Class extends RestartStrategy> rsClass, ClassLoader classLoader) throws InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException, NoSuchMethodException {
+ try {
+ Constructor extends RestartStrategy> constructor = rsClass.getConstructor(ClassLoader.class);
+ return constructor.newInstance(classLoader);
+ } catch (NoSuchMethodException ex) {
+ return rsClass.getConstructor().newInstance();
+ }
+ }
+
private static RestartStrategy forPlatform() {
// we do not use SystemUtil here, to allow testing
String osName = System.getProperty(SystemUtil.PROPERTY_OSNAME);
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java
index 5305193daf..e9c739e22f 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycle.java
@@ -21,10 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.lifecycle.classloading;
-import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.lifecycle.LifeCycle;
@@ -42,16 +41,8 @@ public abstract class ClassLoaderLifeCycle implements LifeCycle {
private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderLifeCycle.class);
- @VisibleForTesting
- static final String PROPERTY = "sonia.scm.lifecycle.classloading";
-
public static ClassLoaderLifeCycle create() {
ClassLoader webappClassLoader = Thread.currentThread().getContextClassLoader();
- String implementation = System.getProperty(PROPERTY);
- if (ClassLoaderLifeCycleWithLeakPrevention.NAME.equalsIgnoreCase(implementation)) {
- LOG.info("create new ClassLoaderLifeCycle with leak prevention");
- return new ClassLoaderLifeCycleWithLeakPrevention(webappClassLoader);
- }
LOG.info("create new simple ClassLoaderLifeCycle");
return new SimpleClassLoaderLifeCycle(webappClassLoader);
}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java
deleted file mode 100644
index f8bed61e53..0000000000
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPrevention.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2020-present Cloudogu GmbH and Contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package sonia.scm.lifecycle.classloading;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
-import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventorFactory;
-import se.jiderhamn.classloader.leak.prevention.cleanup.IIOServiceProviderCleanUp;
-import se.jiderhamn.classloader.leak.prevention.cleanup.MBeanCleanUp;
-import se.jiderhamn.classloader.leak.prevention.cleanup.ShutdownHookCleanUp;
-import se.jiderhamn.classloader.leak.prevention.cleanup.StopThreadsCleanUp;
-import se.jiderhamn.classloader.leak.prevention.preinit.AwtToolkitInitiator;
-import se.jiderhamn.classloader.leak.prevention.preinit.Java2dDisposerInitiator;
-import se.jiderhamn.classloader.leak.prevention.preinit.Java2dRenderQueueInitiator;
-import se.jiderhamn.classloader.leak.prevention.preinit.SunAwtAppContextInitiator;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.ArrayDeque;
-import java.util.Deque;
-
-import static se.jiderhamn.classloader.leak.prevention.cleanup.ShutdownHookCleanUp.SHUTDOWN_HOOK_WAIT_MS_DEFAULT;
-
-/**
- * Creates and shutdown SCM-Manager ClassLoaders with ClassLoader leak detection.
- */
-final class ClassLoaderLifeCycleWithLeakPrevention extends ClassLoaderLifeCycle {
-
- public static final String NAME = "with-leak-prevention";
-
- private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderLifeCycleWithLeakPrevention.class);
-
- private Deque classLoaders = new ArrayDeque<>();
-
- private final ClassLoaderLeakPreventorFactory classLoaderLeakPreventorFactory;
-
- private ClassLoaderAppendListener classLoaderAppendListener = new ClassLoaderAppendListener() {
- @Override
- public C apply(C classLoader) {
- return classLoader;
- }
- };
-
- ClassLoaderLifeCycleWithLeakPrevention(ClassLoader webappClassLoader) {
- this(webappClassLoader, createClassLoaderLeakPreventorFactory(webappClassLoader));
- }
-
- ClassLoaderLifeCycleWithLeakPrevention(ClassLoader webappClassLoader, ClassLoaderLeakPreventorFactory classLoaderLeakPreventorFactory) {
- super(webappClassLoader);
- this.classLoaderLeakPreventorFactory = classLoaderLeakPreventorFactory;
- }
-
- private static ClassLoaderLeakPreventorFactory createClassLoaderLeakPreventorFactory(ClassLoader webappClassLoader) {
- // Should threads tied to the web app classloader be forced to stop at application shutdown?
- boolean stopThreads = Boolean.getBoolean("ClassLoaderLeakPreventor.stopThreads");
-
- // Should Timer threads tied to the web app classloader be forced to stop at application shutdown?
- boolean stopTimerThreads = Boolean.getBoolean("ClassLoaderLeakPreventor.stopTimerThreads");
-
- // Should shutdown hooks registered from the application be executed at application shutdown?
- boolean executeShutdownHooks = Boolean.getBoolean("ClassLoaderLeakPreventor.executeShutdownHooks");
-
- // No of milliseconds to wait for threads to finish execution, before stopping them.
- int threadWaitMs = Integer.getInteger("ClassLoaderLeakPreventor.threadWaitMs", ClassLoaderLeakPreventor.THREAD_WAIT_MS_DEFAULT);
-
- /*
- * No of milliseconds to wait for shutdown hooks to finish execution, before stopping them.
- * If set to -1 there will be no waiting at all, but Thread is allowed to run until finished.
- */
- int shutdownHookWaitMs = Integer.getInteger("ClassLoaderLeakPreventor.shutdownHookWaitMs", SHUTDOWN_HOOK_WAIT_MS_DEFAULT);
-
- LOG.info("Settings for {} (CL: 0x{}):", ClassLoaderLifeCycleWithLeakPrevention.class.getName(), Integer.toHexString(System.identityHashCode(webappClassLoader)) );
- LOG.info(" stopThreads = {}", stopThreads);
- LOG.info(" stopTimerThreads = {}", stopTimerThreads);
- LOG.info(" executeShutdownHooks = {}", executeShutdownHooks);
- LOG.info(" threadWaitMs = {} ms", threadWaitMs);
- LOG.info(" shutdownHookWaitMs = {} ms", shutdownHookWaitMs);
-
- // use webapp classloader as safe base? or system?
- ClassLoaderLeakPreventorFactory classLoaderLeakPreventorFactory = new ClassLoaderLeakPreventorFactory(webappClassLoader);
- classLoaderLeakPreventorFactory.setLogger(new LoggingAdapter());
-
- final ShutdownHookCleanUp shutdownHookCleanUp = classLoaderLeakPreventorFactory.getCleanUp(ShutdownHookCleanUp.class);
- shutdownHookCleanUp.setExecuteShutdownHooks(executeShutdownHooks);
- shutdownHookCleanUp.setShutdownHookWaitMs(shutdownHookWaitMs);
-
- final StopThreadsCleanUp stopThreadsCleanUp = classLoaderLeakPreventorFactory.getCleanUp(StopThreadsCleanUp.class);
- stopThreadsCleanUp.setStopThreads(stopThreads);
- stopThreadsCleanUp.setStopTimerThreads(stopTimerThreads);
- stopThreadsCleanUp.setThreadWaitMs(threadWaitMs);
-
- // remove awt and imageio cleanup
- classLoaderLeakPreventorFactory.removePreInitiator(AwtToolkitInitiator.class);
- classLoaderLeakPreventorFactory.removePreInitiator(SunAwtAppContextInitiator.class);
- classLoaderLeakPreventorFactory.removeCleanUp(IIOServiceProviderCleanUp.class);
- classLoaderLeakPreventorFactory.removePreInitiator(Java2dRenderQueueInitiator.class);
- classLoaderLeakPreventorFactory.removePreInitiator(Java2dDisposerInitiator.class);
-
- // the MBeanCleanUp causes a Exception and we use no mbeans
- classLoaderLeakPreventorFactory.removeCleanUp(MBeanCleanUp.class);
-
- return classLoaderLeakPreventorFactory;
- }
-
- @VisibleForTesting
- void setClassLoaderAppendListener(ClassLoaderAppendListener classLoaderAppendListener) {
- this.classLoaderAppendListener = classLoaderAppendListener;
- }
-
- @Override
- protected void shutdownClassLoaders() {
- ClassLoaderAndPreventor clap = classLoaders.poll();
- while (clap != null) {
- clap.shutdown();
- clap = classLoaders.poll();
- }
- // be sure it is realy empty
- classLoaders.clear();
- classLoaders = new ArrayDeque<>();
- }
-
- @Override
- protected T initAndAppend(T originalClassLoader) {
- LOG.debug("init classloader {}", originalClassLoader);
- T classLoader = classLoaderAppendListener.apply(originalClassLoader);
-
- ClassLoaderLeakPreventor preventor = classLoaderLeakPreventorFactory.newLeakPreventor(classLoader);
- preventor.runPreClassLoaderInitiators();
- classLoaders.push(new ClassLoaderAndPreventor(classLoader, preventor));
-
- return classLoader;
- }
-
- interface ClassLoaderAppendListener {
- C apply(C classLoader);
- }
-
- private static class ClassLoaderAndPreventor {
-
- private final ClassLoader classLoader;
- private final ClassLoaderLeakPreventor preventor;
-
- private ClassLoaderAndPreventor(ClassLoader classLoader, ClassLoaderLeakPreventor preventor) {
- this.classLoader = classLoader;
- this.preventor = preventor;
- }
-
- void shutdown() {
- LOG.debug("shutdown classloader {}", classLoader);
- preventor.runCleanUps();
- close();
- }
-
- private void close() {
- if (classLoader instanceof Closeable) {
- LOG.trace("close classloader {}", classLoader);
- try {
- ((Closeable) classLoader).close();
- } catch (IOException e) {
- LOG.warn("failed to close classloader", e);
- }
- }
- }
- }
-}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/LoggingAdapter.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/LoggingAdapter.java
deleted file mode 100644
index ec52672904..0000000000
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/classloading/LoggingAdapter.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2020-present Cloudogu GmbH and Contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package sonia.scm.lifecycle.classloading;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
-
-/**
- * Logging adapter for {@link ClassLoaderLeakPreventor}.
- */
-public class LoggingAdapter implements se.jiderhamn.classloader.leak.prevention.Logger {
-
- @SuppressWarnings("squid:S3416") // suppress "loggers should be named for their enclosing classes" rule
- private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderLeakPreventor.class);
-
- @Override
- public void debug(String msg) {
- LOG.debug(msg);
- }
-
- @Override
- public void info(String msg) {
- LOG.info(msg);
- }
-
- @Override
- public void warn(String msg) {
- LOG.warn(msg);
- }
-
- @Override
- public void warn(Throwable t) {
- LOG.warn(t.getMessage(), t);
- }
-
- @Override
- public void error(String msg) {
- LOG.error(msg);
- }
-
- @Override
- public void error(Throwable t) {
- LOG.error(t.getMessage(), t);
- }
-}
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/InjectionContextRestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/InjectionContextRestartStrategyTest.java
deleted file mode 100644
index 100e0e9031..0000000000
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/InjectionContextRestartStrategyTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2020-present Cloudogu GmbH and Contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package sonia.scm.lifecycle;
-
-import com.github.legman.Subscribe;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import sonia.scm.event.RecreateEventBusEvent;
-import sonia.scm.event.ScmEventBus;
-
-import java.util.concurrent.TimeUnit;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.awaitility.Awaitility.await;
-import static org.mockito.Mockito.verify;
-
-@ExtendWith(MockitoExtension.class)
-class InjectionContextRestartStrategyTest {
-
- @Mock
- private RestartStrategy.InjectionContext context;
-
- private InjectionContextRestartStrategy strategy = new InjectionContextRestartStrategy(Thread.currentThread().getContextClassLoader());
-
- @BeforeEach
- void setWaitToZero() {
- strategy.setWaitInMs(0L);
- // disable gc during tests
- strategy.setGcEnabled(false);
- }
-
- @Test
- void shouldCallDestroyAndInitialize() {
- TestingInjectionContext ctx = new TestingInjectionContext();
- strategy.restart(ctx);
- await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(ctx.destroyed).isTrue());
- await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(ctx.initialized).isTrue());
- }
-
- @Test
- void shouldFireRecreateEventBusEvent() {
- Listener listener = new Listener();
- ScmEventBus.getInstance().register(listener);
-
- strategy.restart(context);
-
- assertThat(listener.event).isNotNull();
- }
-
- @Test
- void shouldRegisterContextAfterRestart() throws InterruptedException {
- TestingInjectionContext ctx = new TestingInjectionContext();
- strategy.restart(ctx);
-
- await().atMost(1, TimeUnit.SECONDS).until(() -> ctx.initialized);
- Thread.sleep(50L);
- ScmEventBus.getInstance().post("hello event");
-
- assertThat(ctx.event).isEqualTo("hello event");
- }
-
- public static class Listener {
-
- private RecreateEventBusEvent event;
-
- @Subscribe(async = false)
- public void setEvent(RecreateEventBusEvent event) {
- this.event = event;
- }
- }
-
- public static class TestingInjectionContext implements RestartStrategy.InjectionContext {
-
- private volatile String event;
- private boolean initialized = false;
- private boolean destroyed = false;
-
- @Subscribe(async = false)
- public void setEvent(String event) {
- this.event = event;
- }
-
- @Override
- public void initialize() {
- this.initialized = true;
- }
-
- @Override
- public void destroy() {
- this.destroyed = true;
- }
- }
-
-}
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
index 3301e46151..f590281175 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
@@ -45,6 +45,16 @@ class RestartStrategyTest {
});
}
+ @Test
+ void shouldReturnRestartStrategyFromSystemPropertyWithClassLoaderConstructor() {
+ withStrategy(ComplexRestartStrategy.class.getName(), (rs) -> {
+ assertThat(rs).containsInstanceOf(ComplexRestartStrategy.class)
+ .get()
+ .extracting("classLoader")
+ .isSameAs(classLoader);
+ });
+ }
+
@Test
void shouldThrowExceptionForNonStrategyClass() {
withStrategy(RestartStrategyTest.class.getName(), () -> {
@@ -74,13 +84,6 @@ class RestartStrategyTest {
});
}
- @Test
- void shouldReturnInjectionContextRestartStrategy() {
- withStrategy(InjectionContextRestartStrategy.NAME, (rs) -> {
- assertThat(rs).containsInstanceOf(InjectionContextRestartStrategy.class);
- });
- }
-
@ParameterizedTest
@ValueSource(strings = { "linux", "darwin", "solaris", "freebsd", "openbsd" })
void shouldReturnPosixRestartStrategyForPosixBased(String os) {
@@ -121,4 +124,18 @@ class RestartStrategyTest {
}
}
+ public static class ComplexRestartStrategy implements RestartStrategy {
+
+ private final ClassLoader classLoader;
+
+ public ComplexRestartStrategy(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public void restart(InjectionContext context) {
+
+ }
+ }
+
}
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java
index 2855a6dde4..133bbcacbd 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleTest.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.lifecycle.classloading;
import org.junit.jupiter.api.Test;
@@ -30,28 +30,6 @@ import static org.assertj.core.api.Assertions.assertThat;
class ClassLoaderLifeCycleTest {
- @Test
- void shouldCreateSimpleClassLoader() {
- System.setProperty(ClassLoaderLifeCycle.PROPERTY, SimpleClassLoaderLifeCycle.NAME);
- try {
- ClassLoaderLifeCycle classLoaderLifeCycle = ClassLoaderLifeCycle.create();
- assertThat(classLoaderLifeCycle).isInstanceOf(SimpleClassLoaderLifeCycle.class);
- } finally {
- System.clearProperty(ClassLoaderLifeCycle.PROPERTY);
- }
- }
-
- @Test
- void shouldCreateWithLeakPreventionClassLoader() {
- System.setProperty(ClassLoaderLifeCycle.PROPERTY, ClassLoaderLifeCycleWithLeakPrevention.NAME);
- try {
- ClassLoaderLifeCycle classLoaderLifeCycle = ClassLoaderLifeCycle.create();
- assertThat(classLoaderLifeCycle).isInstanceOf(ClassLoaderLifeCycleWithLeakPrevention.class);
- } finally {
- System.clearProperty(ClassLoaderLifeCycle.PROPERTY);
- }
- }
-
@Test
void shouldCreateDefaultClassLoader() {
ClassLoaderLifeCycle classLoaderLifeCycle = ClassLoaderLifeCycle.create();
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPreventionTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPreventionTest.java
deleted file mode 100644
index f237ab8a30..0000000000
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/classloading/ClassLoaderLifeCycleWithLeakPreventionTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2020-present Cloudogu GmbH and Contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package sonia.scm.lifecycle.classloading;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
-import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventorFactory;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class ClassLoaderLifeCycleWithLeakPreventionTest {
-
- @Mock
- private ClassLoaderLeakPreventorFactory classLoaderLeakPreventorFactory;
-
- @Mock
- private ClassLoaderLeakPreventor classLoaderLeakPreventor;
-
- @Test
- void shouldThrowIllegalStateExceptionWithoutInit() {
- ClassLoaderLifeCycleWithLeakPrevention lifeCycle = new ClassLoaderLifeCycleWithLeakPrevention(Thread.currentThread().getContextClassLoader());
- assertThrows(IllegalStateException.class, lifeCycle::getBootstrapClassLoader);
- }
-
- @Test
- void shouldThrowIllegalStateExceptionAfterShutdown() {
- ClassLoaderLifeCycleWithLeakPrevention lifeCycle = createMockedLifeCycle();
- lifeCycle.initialize();
-
- lifeCycle.shutdown();
- assertThrows(IllegalStateException.class, lifeCycle::getBootstrapClassLoader);
- }
-
- @Test
- void shouldCreateBootstrapClassLoaderOnInit() {
- ClassLoaderLifeCycleWithLeakPrevention lifeCycle = new ClassLoaderLifeCycleWithLeakPrevention(Thread.currentThread().getContextClassLoader());
- lifeCycle.initialize();
-
- assertThat(lifeCycle.getBootstrapClassLoader()).isNotNull();
- }
-
- @Test
- void shouldCallTheLeakPreventor() {
- ClassLoaderLifeCycleWithLeakPrevention lifeCycle = createMockedLifeCycle();
-
- lifeCycle.initialize();
- verify(classLoaderLeakPreventor, times(1)).runPreClassLoaderInitiators();
-
- lifeCycle.createChildFirstPluginClassLoader(new URL[0], null, "a");
- lifeCycle.createPluginClassLoader(new URL[0], null, "b");
- verify(classLoaderLeakPreventor, times(3)).runPreClassLoaderInitiators();
-
- lifeCycle.shutdown();
- verify(classLoaderLeakPreventor, times(3)).runCleanUps();
- }
-
- @Test
- void shouldCloseCloseableClassLoaders() throws IOException {
- // we use URLClassLoader, because we must be sure that the classloader is closable
- URLClassLoader webappClassLoader = spy(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()));
-
- ClassLoaderLifeCycleWithLeakPrevention lifeCycle = createMockedLifeCycle(webappClassLoader);
- lifeCycle.setClassLoaderAppendListener(new ClassLoaderLifeCycleWithLeakPrevention.ClassLoaderAppendListener() {
- @Override
- public C apply(C classLoader) {
- return spy(classLoader);
- }
- });
- lifeCycle.initialize();
-
- ClassLoader pluginA = lifeCycle.createChildFirstPluginClassLoader(new URL[0], null, "a");
- ClassLoader pluginB = lifeCycle.createPluginClassLoader(new URL[0], null, "b");
-
- lifeCycle.shutdown();
-
- closed(pluginB);
- closed(pluginA);
-
- neverClosed(webappClassLoader);
- }
-
- private void neverClosed(Object object) throws IOException {
- Closeable closeable = closeable(object);
- verify(closeable, never()).close();
- }
-
- private void closed(Object object) throws IOException {
- Closeable closeable = closeable(object);
- verify(closeable).close();
- }
-
- private Closeable closeable(Object object) {
- assertThat(object).isInstanceOf(Closeable.class);
- return (Closeable) object;
- }
-
- private ClassLoaderLifeCycleWithLeakPrevention createMockedLifeCycle() {
- return createMockedLifeCycle(Thread.currentThread().getContextClassLoader());
- }
-
- private ClassLoaderLifeCycleWithLeakPrevention createMockedLifeCycle(ClassLoader classLoader) {
- when(classLoaderLeakPreventorFactory.newLeakPreventor(any(ClassLoader.class))).thenReturn(classLoaderLeakPreventor);
- return new ClassLoaderLifeCycleWithLeakPrevention(classLoader, classLoaderLeakPreventorFactory);
- }
-
-}
From 02c0fc80251603061a27cdd1fa5b1120e83e1868 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Wed, 25 Mar 2020 08:54:24 +0100
Subject: [PATCH 10/18] added restart changes to changelog
---
CHANGELOG.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c85a7d964e..465214482d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Extension point to add links to the repository cards from plug ins ([#1041](https://github.com/scm-manager/scm-manager/pull/1041))
+- Libc based restart strategy for posix operating systems
+- Notification if restart is not supported on the underlying platform
### Changed
- Update resteasy to version 4.5.2.Final
@@ -15,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- EventSource Polyfill
+- ClassLoader based restart logic
### Fixed
- Build on windows ([#1048](https://github.com/scm-manager/scm-manager/issues/1048), [#1049](https://github.com/scm-manager/scm-manager/issues/1049), [#1056](https://github.com/scm-manager/scm-manager/pull/1056))
From 92ab80cb7d5b6b2d7366b78e784086c5106322a0 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Wed, 25 Mar 2020 08:57:10 +0100
Subject: [PATCH 11/18] added missing pr references to changelog
---
CHANGELOG.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 465214482d..948b311b7b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,8 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Extension point to add links to the repository cards from plug ins ([#1041](https://github.com/scm-manager/scm-manager/pull/1041))
-- Libc based restart strategy for posix operating systems
-- Notification if restart is not supported on the underlying platform
+- Libc based restart strategy for posix operating systems ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
+- Notification if restart is not supported on the underlying platform ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
### Changed
- Update resteasy to version 4.5.2.Final
@@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- EventSource Polyfill
-- ClassLoader based restart logic
+- ClassLoader based restart logic ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
### Fixed
- Build on windows ([#1048](https://github.com/scm-manager/scm-manager/issues/1048), [#1049](https://github.com/scm-manager/scm-manager/issues/1049), [#1056](https://github.com/scm-manager/scm-manager/pull/1056))
From 88a851d817671cb9fb64e478ba251354120dd4e6 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra
Date: Wed, 25 Mar 2020 09:45:23 +0100
Subject: [PATCH 12/18] do not create config.xml during unit test
---
.../sonia/scm/api/v2/resources/ConfigResource.java | 13 +++++++++++--
.../scm/api/v2/resources/ConfigResourceTest.java | 9 +++++----
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.java
index 61b3864d7c..5575af06fb 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ConfigResource.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.api.v2.resources;
+import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -45,6 +46,7 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
+import java.util.function.Consumer;
/**
* RESTful Web Service Resource to manage the configuration.
@@ -61,6 +63,8 @@ public class ConfigResource {
private final ScmConfiguration configuration;
private final NamespaceStrategyValidator namespaceStrategyValidator;
+ private Consumer store = (config) -> ScmConfigurationUtil.getInstance().store(config);
+
@Inject
public ConfigResource(ConfigDtoToScmConfigurationMapper dtoToConfigMapper,
ScmConfigurationToConfigDtoMapper configToDtoMapper,
@@ -71,6 +75,11 @@ public class ConfigResource {
this.namespaceStrategyValidator = namespaceStrategyValidator;
}
+ @VisibleForTesting
+ void setStore(Consumer store) {
+ this.store = store;
+ }
+
/**
* Returns the global scm config.
*/
@@ -137,7 +146,7 @@ public class ConfigResource {
ScmConfiguration config = dtoToConfigMapper.map(configDto);
synchronized (ScmConfiguration.class) {
configuration.load(config);
- ScmConfigurationUtil.getInstance().store(configuration);
+ store.accept(configuration);
}
return Response.noContent().build();
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java
index cbf4f61570..6f3803bbfd 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.ShiroRule;
@@ -89,6 +89,7 @@ public class ConfigResourceTest {
initMocks(this);
ConfigResource configResource = new ConfigResource(dtoToConfigMapper, configToDtoMapper, createConfiguration(), namespaceStrategyValidator);
+ configResource.setStore(config -> {});
dispatcher.addSingletonResource(configResource);
}
@@ -128,7 +129,9 @@ public class ConfigResourceTest {
request = MockHttpRequest.get("/" + ConfigResource.CONFIG_PATH_V2);
response = new MockHttpResponse();
- dispatcher.invoke(request, response); assertEquals(HttpServletResponse.SC_OK, response.getStatus());
+ dispatcher.invoke(request, response);
+
+ assertEquals(HttpServletResponse.SC_OK, response.getStatus());
assertTrue(response.getContentAsString().contains("\"proxyPassword\":\"newPassword\""));
assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/config"));
assertTrue("link not found", response.getContentAsString().contains("\"update\":{\"href\":\"/v2/config"));
@@ -146,8 +149,6 @@ public class ConfigResourceTest {
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus());
}
-
-
@Test
@SubjectAware(username = "readWrite")
public void shouldValidateNamespaceStrategy() throws URISyntaxException {
From 5d05ab966ac54eb344baaccb8cf8b0293ed6dcc4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?=
Date: Wed, 25 Mar 2020 13:07:43 +0100
Subject: [PATCH 13/18] Fix translations
---
scm-ui/ui-webapp/public/locales/de/admin.json | 4 ++--
scm-ui/ui-webapp/public/locales/en/admin.json | 10 +++++-----
.../repository-migration-manual-restart.mustache | 4 ++--
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/scm-ui/ui-webapp/public/locales/de/admin.json b/scm-ui/ui-webapp/public/locales/de/admin.json
index a60b01046c..62ba36bd69 100644
--- a/scm-ui/ui-webapp/public/locales/de/admin.json
+++ b/scm-ui/ui-webapp/public/locales/de/admin.json
@@ -72,8 +72,8 @@
"executePending": "Die folgenden Plugin-Änderungen werden ausgeführt. Anschließend wird SCM-Manager neu gestartet.",
"cancelPending": "Die folgenden Plugin-Änderungen werden abgebrochen und zurückgesetzt.",
"updateAllInfo": "Die folgenden Plugins werden aktualisiert. Die Änderungen werden nach dem nächsten Neustart wirksam.",
- "manualRestartRequired": "Nach dem die Plugin-Änderung durchgeführt wurde, muss SCM-Manager neu gestartet werden.",
- "showPending": "Um die folgenden Plugin-Änderungen auszuführen muss SCM-Manager neu gestartet werden."
+ "manualRestartRequired": "Nachdem die Plugin-Änderung durchgeführt wurde, muss SCM-Manager neu gestartet werden.",
+ "showPending": "Um die folgenden Plugin-Änderungen auszuführen, muss SCM-Manager neu gestartet werden."
}
},
"repositoryRole": {
diff --git a/scm-ui/ui-webapp/public/locales/en/admin.json b/scm-ui/ui-webapp/public/locales/en/admin.json
index 953cf335b4..7afad7829e 100644
--- a/scm-ui/ui-webapp/public/locales/en/admin.json
+++ b/scm-ui/ui-webapp/public/locales/en/admin.json
@@ -68,12 +68,12 @@
"uninstalledNotification": "Successfully uninstalled plugin. You have to reload the page, to see ui changes:",
"executedChangesNotification": "Successfully executed plugin changes. You have to reload the page, to see ui changes:",
"reload": "reload now",
- "restartNotification": "You should only restart scm-manager if no one else is currently working with it.",
- "executePending": "The following plugin changes will be executed and after that the scm-manager will be restarted.",
+ "restartNotification": "You should only restart SCM-Manager if no one else is currently working with it.",
+ "executePending": "The following plugin changes will be executed and after that the SCM-Manager will be restarted.",
"cancelPending": "The following plugin changes will be canceled.",
- "updateAllInfo": "The following plugin changes will be executed. You need to restart the scm-manager to make these changes effective.",
- "manualRestartRequired": "After the plugin change has been made, scm-manager must be restarted.",
- "showPending": "To execute the following plugin changes, scm-manager must be restarted."
+ "updateAllInfo": "The following plugin changes will be executed. You need to restart the SCM-Manager to make these changes effective.",
+ "manualRestartRequired": "After the plugin change has been made, SCM-Manager must be restarted.",
+ "showPending": "To execute the following plugin changes, SCM-Manager must be restarted."
}
},
"repositoryRole": {
diff --git a/scm-webapp/src/main/resources/templates/repository-migration-manual-restart.mustache b/scm-webapp/src/main/resources/templates/repository-migration-manual-restart.mustache
index a0d2fd1a96..46d65fa86b 100644
--- a/scm-webapp/src/main/resources/templates/repository-migration-manual-restart.mustache
+++ b/scm-webapp/src/main/resources/templates/repository-migration-manual-restart.mustache
@@ -4,8 +4,8 @@
{{$content}}
- The migration is prepared and gets executed after scm-manager is restarted.
- Please restart your scm-manager instance.
+ The migration is prepared and gets executed after SCM-Manager is restarted.
+ Please restart your SCM-Manager instance.
{{/content}}
From af45388c27951ff8477d2ff528d364d7dea8ff6d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?=
Date: Wed, 25 Mar 2020 14:03:33 +0100
Subject: [PATCH 14/18] Add documentation
---
.../src/main/java/sonia/scm/lifecycle/RestartStrategy.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java
index a33e16a3fb..81fcffff56 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java
@@ -21,13 +21,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.lifecycle;
import java.util.Optional;
/**
- * Strategy for restarting SCM-Manager.
+ * Strategy for restarting SCM-Manager. Implementations must either have a default constructor or one taking the
+ * class loader for the current context as a single argument.
*/
public interface RestartStrategy {
From 7247a81c9d986d4e1159a8baf78a43b2d4839497 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?=
Date: Wed, 25 Mar 2020 15:31:20 +0100
Subject: [PATCH 15/18] Use exceptions with explicit messages
---
.../PluginChecksumMismatchException.java | 16 ++++++--
.../scm/plugin/PluginCleanupException.java | 40 +++++++++++++++++++
.../scm/plugin/PluginDownloadException.java | 13 ++++--
.../scm/plugin/PluginInstallException.java | 17 +++++---
.../sonia/scm/plugin/PluginInstaller.java | 10 ++---
.../main/resources/locales/de/plugins.json | 12 ++++++
.../main/resources/locales/en/plugins.json | 12 ++++++
.../scm/plugin/DefaultPluginManagerTest.java | 4 +-
8 files changed, 104 insertions(+), 20 deletions(-)
create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginCleanupException.java
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java
index 0cdb3c6b2c..af419fa297 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginChecksumMismatchException.java
@@ -21,11 +21,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.plugin;
+import static sonia.scm.ContextEntry.ContextBuilder.entity;
+
public class PluginChecksumMismatchException extends PluginInstallException {
- public PluginChecksumMismatchException(String message) {
- super(message);
+ public PluginChecksumMismatchException(AvailablePlugin plugin, String calculatedChecksum, String expectedChecksum) {
+ super(
+ entity("Plugin", plugin.getDescriptor().getInformation().getName()).build(),
+ String.format("downloaded plugin checksum %s does not match expected %s", calculatedChecksum, expectedChecksum)
+ );
+ }
+
+ @Override
+ public String getCode() {
+ return "6mRuFxaWM1";
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCleanupException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCleanupException.java
new file mode 100644
index 0000000000..82c4d94b66
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCleanupException.java
@@ -0,0 +1,40 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020-present Cloudogu GmbH and Contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package sonia.scm.plugin;
+
+import java.nio.file.Path;
+
+import static sonia.scm.ContextEntry.ContextBuilder.entity;
+
+public class PluginCleanupException extends PluginInstallException {
+ public PluginCleanupException(Path file) {
+ super(entity("File", file.toString()).build(), "failed to cleanup, after broken installation");
+ }
+
+ @Override
+ public String getCode() {
+ return "8nRuFzjss1";
+ }
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java
index 75dfc78b40..b67a766ed2 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginDownloadException.java
@@ -21,11 +21,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.plugin;
+import static sonia.scm.ContextEntry.ContextBuilder.entity;
+
public class PluginDownloadException extends PluginInstallException {
- public PluginDownloadException(String message, Throwable cause) {
- super(message, cause);
+ public PluginDownloadException(AvailablePlugin plugin, Exception cause) {
+ super(entity("Plugin", plugin.getDescriptor().getInformation().getName()).build(), "failed to download plugin", cause);
+ }
+
+ @Override
+ public String getCode() {
+ return "9iRuFz1UB1";
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java
index e5d1d0d2a2..b70f668b37 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstallException.java
@@ -21,16 +21,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.plugin;
-public class PluginInstallException extends RuntimeException {
+import sonia.scm.ContextEntry;
+import sonia.scm.ExceptionWithContext;
- public PluginInstallException(String message) {
- super(message);
+import java.util.List;
+
+abstract class PluginInstallException extends ExceptionWithContext {
+
+ public PluginInstallException(List context, String message) {
+ super(context, message);
}
- public PluginInstallException(String message, Throwable cause) {
- super(message, cause);
+ public PluginInstallException(List context, String message, Exception cause) {
+ super(context, message, cause);
}
}
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java
index 0649d161e1..1ca34c5166 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginInstaller.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.plugin;
import com.google.common.hash.HashCode;
@@ -64,7 +64,7 @@ class PluginInstaller {
return new PendingPluginInstallation(plugin.install(), file);
} catch (IOException ex) {
cleanup(file);
- throw new PluginDownloadException("failed to download plugin", ex);
+ throw new PluginDownloadException(plugin, ex);
}
}
@@ -74,7 +74,7 @@ class PluginInstaller {
Files.deleteIfExists(file);
}
} catch (IOException e) {
- throw new PluginInstallException("failed to cleanup, after broken installation");
+ throw new PluginCleanupException(file);
}
}
@@ -84,9 +84,7 @@ class PluginInstaller {
String calculatedChecksum = hash.toString();
if (!checksum.get().equalsIgnoreCase(calculatedChecksum)) {
cleanup(file);
- throw new PluginChecksumMismatchException(
- String.format("downloaded plugin checksum %s does not match expected %s", calculatedChecksum, checksum.get())
- );
+ throw new PluginChecksumMismatchException(plugin, calculatedChecksum, checksum.get());
}
}
}
diff --git a/scm-webapp/src/main/resources/locales/de/plugins.json b/scm-webapp/src/main/resources/locales/de/plugins.json
index 809da9d0df..8c5c88e0f8 100644
--- a/scm-webapp/src/main/resources/locales/de/plugins.json
+++ b/scm-webapp/src/main/resources/locales/de/plugins.json
@@ -203,6 +203,18 @@
"4GRrgkSC01": {
"displayName": "Unerwartetes Merge-Ergebnis",
"description": "Der Merge hatte ein unerwartetes Ergebis, das nicht automatisiert behandelt werden konnte. Nähere Details sind im Log zu finden. Führen Sie den Merge ggf. manuell durch."
+ },
+ "6mRuFxaWM1": {
+ "displayName": "Falsche Checksumme",
+ "description": "Die Checksumme des heruntergeladenen Plugins stimmt nicht mit der erwarteten Checksumme überein. Bitte versuchen Sie es erneut und prüfen Sie die Interneteinstellungen wie z. B. die Proxy-Einstellungen."
+ },
+ "9iRuFz1UB1": {
+ "displayName": "Fehler beim Herunterladen",
+ "description": "Das Plugin konnte nicht vom Server heruntergeladen werden. BitteThe plugin could not be loaded from the server. Bitte versuchen Sie es erneut und prüfen Sie die Interneteinstellungen wie z. B. die Proxy-Einstellungen. Weitere Details finden sich im Server Log."
+ },
+ "8nRuFzjss1": {
+ "displayName": "Fehler beim Löschen falscher Downloads",
+ "description": "Ein fehlerhaft heruntergeladenes Plugin konnte nicht gelöscht werden. Bitte prüfen Sie die Server Logs und löschen die Datei manuell."
}
},
"namespaceStrategies": {
diff --git a/scm-webapp/src/main/resources/locales/en/plugins.json b/scm-webapp/src/main/resources/locales/en/plugins.json
index 69e8d057ee..998e796bac 100644
--- a/scm-webapp/src/main/resources/locales/en/plugins.json
+++ b/scm-webapp/src/main/resources/locales/en/plugins.json
@@ -203,6 +203,18 @@
"4GRrgkSC01": {
"displayName": "Unexpected merge result",
"description": "The merge led to an unexpected result, that could not be handled automatically. More details could be found in the log. Please merge the branches manually."
+ },
+ "6mRuFxaWM1": {
+ "displayName": "Wrong checksum",
+ "description": "The checksum of the downloaded plugin did not match the expected checksum. Please try again or check the internet settings like proxies."
+ },
+ "9iRuFz1UB1": {
+ "displayName": "Could not load plugin",
+ "description": "The plugin could not be loaded from the server. Please try again or check the internet settings like proxies. More information can be found in the server log."
+ },
+ "8nRuFzjss1": {
+ "displayName": "Error while cleaning up failed plugin",
+ "description": "A failed plugin download could not be removed correctly. Please check the server log and remove the plugin manually."
}
},
"namespaceStrategies": {
diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
index 47c351ff30..8f3f7bbe57 100644
--- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.plugin;
import com.google.common.collect.ImmutableList;
@@ -252,7 +252,7 @@ class DefaultPluginManagerTest {
PendingPluginInstallation pendingMail = mock(PendingPluginInstallation.class);
doReturn(pendingMail).when(installer).install(mail);
- doThrow(new PluginChecksumMismatchException("checksum does not match")).when(installer).install(review);
+ doThrow(new PluginChecksumMismatchException(mail, "1", "2")).when(installer).install(review);
assertThrows(PluginInstallException.class, () -> manager.install("scm-review-plugin", false));
From 15aab3059db76e1d8140d5844d8f0b94fdb4fc93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?=
Date: Wed, 25 Mar 2020 15:55:03 +0100
Subject: [PATCH 16/18] Add documentation
---
CHANGELOG.md | 1 +
scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java | 1 +
2 files changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 948b311b7b..bc6d2713ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Extension point to add links to the repository cards from plug ins ([#1041](https://github.com/scm-manager/scm-manager/pull/1041))
- Libc based restart strategy for posix operating systems ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
+- Simple restart strategy with System.exit ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
- Notification if restart is not supported on the underlying platform ([#1079](https://github.com/scm-manager/scm-manager/pull/1079))
### Changed
diff --git a/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java b/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
index 0b6c18f0a9..12f9279c80 100644
--- a/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
+++ b/scm-core/src/main/java/sonia/scm/lifecycle/Restarter.java
@@ -43,6 +43,7 @@ public interface Restarter {
*
* @param cause cause of the restart. This should be the class which calls this method.
* @param reason reason for the required restart.
+ * @throws RestartNotSupportedException if restarting is not supported by the underlying platform.
*/
void restart(Class> cause, String reason);
}
From 24c7cb0b67b1865cfca7469edfecb641550bba0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?=
Date: Wed, 25 Mar 2020 16:52:53 +0100
Subject: [PATCH 17/18] Ensure that context is destroyed on restart
---
.../scm/lifecycle/BootstrapContextFilter.java | 4 +-
.../scm/lifecycle/ExitRestartStrategy.java | 14 ++---
.../scm/lifecycle/PosixRestartStrategy.java | 7 +--
.../sonia/scm/lifecycle/RestartStrategy.java | 51 +++++++++++++++----
.../lifecycle/ExitRestartStrategyTest.java | 2 +-
.../scm/lifecycle/RestartStrategyTest.java | 10 ++--
6 files changed, 57 insertions(+), 31 deletions(-)
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java
index 5c81089616..b5094e2aba 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/BootstrapContextFilter.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.lifecycle;
//~--- non-JDK imports --------------------------------------------------------
@@ -103,7 +103,7 @@ public class BootstrapContextFilter extends GuiceFilter {
}
}
- private class GuiceInjectionContext implements RestartStrategy.InjectionContext {
+ private class GuiceInjectionContext implements RestartStrategy.InternalInjectionContext {
@Override
public void initialize() {
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
index d8eb2fd22a..04d58df731 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/ExitRestartStrategy.java
@@ -35,7 +35,7 @@ import java.util.function.IntConsumer;
*
* This is useful if an external mechanism is able to restart the process after it has exited.
*/
-class ExitRestartStrategy implements RestartStrategy {
+class ExitRestartStrategy extends RestartStrategy {
private static final Logger LOG = LoggerFactory.getLogger(ExitRestartStrategy.class);
@@ -45,6 +45,8 @@ class ExitRestartStrategy implements RestartStrategy {
private IntConsumer exiter = System::exit;
+ private int exitCode;
+
ExitRestartStrategy() {
}
@@ -54,12 +56,12 @@ class ExitRestartStrategy implements RestartStrategy {
}
@Override
- public void restart(InjectionContext context) {
- int exitCode = determineExitCode();
-
- LOG.warn("destroy injection context");
- context.destroy();
+ public void prepareRestart(InjectionContext context) {
+ exitCode = determineExitCode();
+ }
+ @Override
+ protected void executeRestart(InjectionContext context) {
LOG.warn("exit scm-manager with exit code {}", exitCode);
exiter.accept(exitCode);
}
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
index 6d5477dd78..852d4b5f60 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/PosixRestartStrategy.java
@@ -33,7 +33,7 @@ import static sonia.scm.lifecycle.CLibrary.*;
/**
* Restart strategy which uses execvp from libc. This strategy is only supported on posix base operating systems.
*/
-class PosixRestartStrategy implements RestartStrategy {
+class PosixRestartStrategy extends RestartStrategy {
private static final Logger LOG = LoggerFactory.getLogger(PosixRestartStrategy.class);
@@ -41,10 +41,7 @@ class PosixRestartStrategy implements RestartStrategy {
}
@Override
- public void restart(InjectionContext context) {
- LOG.warn("destroy injection context");
- context.destroy();
-
+ protected void executeRestart(InjectionContext context) {
LOG.warn("restart scm-manager jvm process");
try {
restart();
diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java
index 81fcffff56..c21c66f011 100644
--- a/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java
+++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/RestartStrategy.java
@@ -24,23 +24,20 @@
package sonia.scm.lifecycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.Optional;
/**
* Strategy for restarting SCM-Manager. Implementations must either have a default constructor or one taking the
* class loader for the current context as a single argument.
*/
-public interface RestartStrategy {
+public abstract class RestartStrategy {
- /**
- * Context for Injection in SCM-Manager.
- */
- interface InjectionContext {
- /**
- * Initialize the injection context.
- */
- void initialize();
+ private static final Logger LOG = LoggerFactory.getLogger(RestartStrategy.class);
+ interface InternalInjectionContext extends InjectionContext {
/**
* Destroys the injection context.
*/
@@ -48,11 +45,43 @@ public interface RestartStrategy {
}
/**
- * Restart SCM-Manager.
+ * Context for Injection in SCM-Manager.
+ */
+ public interface InjectionContext {
+ /**
+ * Initialize the injection context.
+ */
+ void initialize();
+ }
+
+ /**
+ * Restart SCM-Manager by first calling {@link #prepareRestart(InjectionContext)}, destroying the
+ * current context, and finally calling {@link #executeRestart(InjectionContext)}.
*
* @param context injection context
*/
- void restart(InjectionContext context);
+ public final void restart(InternalInjectionContext context) {
+ prepareRestart(context);
+ LOG.warn("destroy injection context");
+ context.destroy();
+ executeRestart(context);
+ }
+
+ /**
+ * Prepare the restart of SCM-Manager. Here you can check whether restart is possible and,
+ * if necessary, throw a {@link RestartNotSupportedException} to abort the restart.
+ *
+ * @param context injection context
+ */
+ protected void prepareRestart(InjectionContext context) {
+ }
+
+ /**
+ * Actually restart SCM-Manager.
+ *
+ * @param context injection context
+ */
+ protected abstract void executeRestart(InjectionContext context);
/**
* Returns the configured strategy or empty if restart is not supported by the underlying platform.
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
index 8fba403120..766fa3bb64 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/ExitRestartStrategyTest.java
@@ -39,7 +39,7 @@ import static org.mockito.Mockito.verify;
class ExitRestartStrategyTest {
@Mock
- private RestartStrategy.InjectionContext context;
+ private RestartStrategy.InternalInjectionContext context;
private ExitRestartStrategy strategy;
private CapturingExiter exiter;
diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
index f590281175..2205bfc0d0 100644
--- a/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/RestartStrategyTest.java
@@ -117,14 +117,13 @@ class RestartStrategyTest {
}
}
- public static class TestingRestartStrategy implements RestartStrategy {
+ public static class TestingRestartStrategy extends RestartStrategy {
@Override
- public void restart(InjectionContext context) {
-
+ protected void executeRestart(InjectionContext context) {
}
}
- public static class ComplexRestartStrategy implements RestartStrategy {
+ public static class ComplexRestartStrategy extends RestartStrategy {
private final ClassLoader classLoader;
@@ -133,8 +132,7 @@ class RestartStrategyTest {
}
@Override
- public void restart(InjectionContext context) {
-
+ protected void executeRestart(InjectionContext context) {
}
}
From a65d955cde6f9ff73747fee61669fe1176fa6127 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?=
Date: Thu, 26 Mar 2020 07:45:43 +0100
Subject: [PATCH 18/18] Update shiro
---
CHANGELOG.md | 1 +
pom.xml | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc6d2713ec..42f2061564 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Update resteasy to version 4.5.2.Final
+- Update shiro to version 1.5.2
- Use browser built-in EventSource for apiClient subscriptions
- Changeover to MIT license ([#1066](https://github.com/scm-manager/scm-manager/pull/1066))
diff --git a/pom.xml b/pom.xml
index c38bb5ef84..2054116a65 100644
--- a/pom.xml
+++ b/pom.xml
@@ -926,7 +926,7 @@
1.2.0
- 1.5.1
+ 1.5.25.6.1.202002131546-r-scm1