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);
- }
-
-}