mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 16:35:45 +01:00
fix wrong ClassLoader for Delayed-Restart Thread, which has caused an ClassLoader leak.
Also added system properties to configure shutdown only, wait between stop and start and possibility to disable gc.
This commit is contained in:
@@ -90,8 +90,12 @@ public class LegmanScmEventBus extends ScmEventBus
|
||||
@Override
|
||||
public void post(Object event)
|
||||
{
|
||||
logger.debug("post {} to event bus {}", event, name);
|
||||
eventBus.post(event);
|
||||
if (eventBus != null) {
|
||||
logger.debug("post {} to event bus {}", event, name);
|
||||
eventBus.post(event);
|
||||
} else {
|
||||
logger.error("failed to post event {}, because event bus is shutdown", event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,9 +108,12 @@ public class LegmanScmEventBus extends ScmEventBus
|
||||
@Override
|
||||
public void register(Object object)
|
||||
{
|
||||
logger.trace("register {} to event bus {}", object, name);
|
||||
eventBus.register(object);
|
||||
|
||||
if (eventBus != null) {
|
||||
logger.trace("register {} to event bus {}", object, name);
|
||||
eventBus.register(object);
|
||||
} else {
|
||||
logger.error("failed to register {}, because eventbus is shutdown", object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,22 +125,37 @@ public class LegmanScmEventBus extends ScmEventBus
|
||||
@Override
|
||||
public void unregister(Object object)
|
||||
{
|
||||
logger.trace("unregister {} from event bus {}", object, name);
|
||||
|
||||
try
|
||||
{
|
||||
eventBus.unregister(object);
|
||||
if (eventBus != null) {
|
||||
logger.trace("unregister {} from event bus {}", object, name);
|
||||
|
||||
try {
|
||||
eventBus.unregister(object);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logger.trace("object {} was not registered", object);
|
||||
}
|
||||
} else {
|
||||
logger.error("failed to unregister object {}, because event bus is shutdown", object);
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
logger.trace("object {} was not registered", object);
|
||||
}
|
||||
|
||||
@Subscribe(async = false)
|
||||
public void shutdownEventBus(ShutdownEventBusEvent shutdownEventBusEvent) {
|
||||
if (eventBus != null) {
|
||||
logger.info("shutdown event bus executor for {}, because of received ShutdownEventBusEvent", name);
|
||||
eventBus.shutdown();
|
||||
eventBus = null;
|
||||
} else {
|
||||
logger.warn("event bus was already shutdown");
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(async = false)
|
||||
public void recreateEventBus(RecreateEventBusEvent recreateEventBusEvent) {
|
||||
logger.info("shutdown event bus executor for {}", name);
|
||||
eventBus.shutdown();
|
||||
if (eventBus != null) {
|
||||
logger.info("shutdown event bus executor for {}, because of received RecreateEventBusEvent", name);
|
||||
eventBus.shutdown();
|
||||
}
|
||||
logger.info("recreate event bus because of received RecreateEventBusEvent");
|
||||
eventBus = create();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package sonia.scm.event;
|
||||
|
||||
public class ShutdownEventBusEvent {
|
||||
}
|
||||
@@ -58,13 +58,16 @@ public class BootstrapContextFilter extends GuiceFilter {
|
||||
|
||||
private final BootstrapContextListener listener = new BootstrapContextListener();
|
||||
|
||||
private ClassLoader webAppClassLoader;
|
||||
|
||||
/** Field description */
|
||||
private FilterConfig filterConfig;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
this.filterConfig = filterConfig;
|
||||
|
||||
// store webapp classloader for delayed restarts
|
||||
webAppClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
initializeContext();
|
||||
}
|
||||
|
||||
@@ -97,7 +100,7 @@ public class BootstrapContextFilter extends GuiceFilter {
|
||||
if (filterConfig == null) {
|
||||
LOG.error("filter config is null, scm-manager is not initialized");
|
||||
} else {
|
||||
RestartStrategy restartStrategy = RestartStrategy.get();
|
||||
RestartStrategy restartStrategy = RestartStrategy.get(webAppClassLoader);
|
||||
restartStrategy.restart(new GuiceInjectionContext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
|
||||
@@ -13,20 +14,47 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
*/
|
||||
public class InjectionContextRestartStrategy implements RestartStrategy {
|
||||
|
||||
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 long waitInMs = 250L;
|
||||
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) {
|
||||
LOG.warn("destroy injection context");
|
||||
context.destroy();
|
||||
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());
|
||||
@@ -34,6 +62,12 @@ public class InjectionContextRestartStrategy implements RestartStrategy {
|
||||
// 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");
|
||||
@@ -45,6 +79,15 @@ public class InjectionContextRestartStrategy implements RestartStrategy {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ public interface RestartStrategy {
|
||||
* Initialize the injection context.
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Destroys the injection context.
|
||||
*/
|
||||
@@ -31,8 +30,8 @@ public interface RestartStrategy {
|
||||
*
|
||||
* @return configured strategy
|
||||
*/
|
||||
static RestartStrategy get() {
|
||||
return new InjectionContextRestartStrategy();
|
||||
static RestartStrategy get(ClassLoader webAppClassLoader) {
|
||||
return new InjectionContextRestartStrategy(webAppClassLoader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user