mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 17:05:43 +01:00
added RestartStrategies to be able to swap the context recreation strategy
This commit is contained in:
@@ -37,7 +37,6 @@ import com.github.legman.Subscribe;
|
|||||||
import com.google.inject.servlet.GuiceFilter;
|
import com.google.inject.servlet.GuiceFilter;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.event.RecreateEventBusEvent;
|
|
||||||
import sonia.scm.event.ScmEventBus;
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
@@ -50,63 +49,29 @@ import javax.servlet.ServletException;
|
|||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
public class BootstrapContextFilter extends GuiceFilter
|
public class BootstrapContextFilter extends GuiceFilter {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the logger for BootstrapContextFilter
|
* the logger for BootstrapContextFilter
|
||||||
*/
|
*/
|
||||||
private static final Logger logger =
|
private static final Logger LOG = LoggerFactory.getLogger(BootstrapContextFilter.class);
|
||||||
LoggerFactory.getLogger(BootstrapContextFilter.class);
|
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
|
||||||
|
|
||||||
private final BootstrapContextListener listener = new BootstrapContextListener();
|
private final BootstrapContextListener listener = new BootstrapContextListener();
|
||||||
|
|
||||||
/**
|
/** Field description */
|
||||||
* Restart the whole webapp context.
|
private FilterConfig filterConfig;
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param event restart event
|
|
||||||
*
|
|
||||||
* @throws ServletException
|
|
||||||
*/
|
|
||||||
@Subscribe
|
|
||||||
public void handleRestartEvent(RestartEvent event) throws ServletException
|
|
||||||
{
|
|
||||||
logger.warn("received restart event from {} with reason: {}",
|
|
||||||
event.getCause(), event.getReason());
|
|
||||||
|
|
||||||
if (filterConfig == null)
|
|
||||||
{
|
|
||||||
logger.error("filter config is null, scm-manager is not initialized");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.warn("destroy filter pipeline, because of a received restart event");
|
|
||||||
destroy();
|
|
||||||
|
|
||||||
logger.warn("send recreate eventbus event");
|
|
||||||
ScmEventBus.getInstance().post(new RecreateEventBusEvent());
|
|
||||||
ScmEventBus.getInstance().register(this);
|
|
||||||
|
|
||||||
logger.warn("reinitialize filter pipeline, because of a received restart event");
|
|
||||||
initGuice();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig filterConfig) throws ServletException
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
{
|
|
||||||
this.filterConfig = filterConfig;
|
this.filterConfig = filterConfig;
|
||||||
|
|
||||||
initGuice();
|
initGuice();
|
||||||
|
|
||||||
logger.info("register for restart events");
|
LOG.info("register for restart events");
|
||||||
ScmEventBus.getInstance().register(this);
|
ScmEventBus.getInstance().register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initGuice() throws ServletException {
|
private void initGuice() throws ServletException {
|
||||||
super.init(filterConfig);
|
super.init(filterConfig);
|
||||||
|
|
||||||
listener.contextInitialized(new ServletContextEvent(filterConfig.getServletContext()));
|
listener.contextInitialized(new ServletContextEvent(filterConfig.getServletContext()));
|
||||||
@@ -119,8 +84,39 @@ public class BootstrapContextFilter extends GuiceFilter
|
|||||||
ServletContextCleaner.cleanup(filterConfig.getServletContext());
|
ServletContextCleaner.cleanup(filterConfig.getServletContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
/**
|
||||||
|
* Restart SCM-Manager.
|
||||||
|
*
|
||||||
|
* @param event restart event
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
public void handleRestartEvent(RestartEvent event) {
|
||||||
|
LOG.warn("received restart event from {} with reason: {}",
|
||||||
|
event.getCause(), event.getReason());
|
||||||
|
|
||||||
|
if (filterConfig == null) {
|
||||||
|
LOG.error("filter config is null, scm-manager is not initialized");
|
||||||
|
} else {
|
||||||
|
RestartStrategy restartStrategy = RestartStrategy.get();
|
||||||
|
restartStrategy.restart(new GuiceInjectionContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GuiceInjectionContext implements RestartStrategy.InjectionContext {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
try {
|
||||||
|
BootstrapContextFilter.this.initGuice();
|
||||||
|
} catch (ServletException e) {
|
||||||
|
throw new IllegalStateException("failed to initialize guice", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
BootstrapContextFilter.this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Field description */
|
|
||||||
private FilterConfig filterConfig;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
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 java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart strategy implementation which destroy the injection context and re initialize it.
|
||||||
|
*/
|
||||||
|
public class InjectionContextRestartStrategy implements RestartStrategy {
|
||||||
|
|
||||||
|
private static final AtomicLong INSTANCE_COUNTER = new AtomicLong();
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(InjectionContextRestartStrategy.class);
|
||||||
|
|
||||||
|
private long waitInMs = 250L;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setWaitInMs(long waitInMs) {
|
||||||
|
this.waitInMs = waitInMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void restart(InjectionContext context) {
|
||||||
|
LOG.warn("destroy injection context");
|
||||||
|
context.destroy();
|
||||||
|
|
||||||
|
LOG.warn("send recreate eventbus event");
|
||||||
|
ScmEventBus.getInstance().post(new RecreateEventBusEvent());
|
||||||
|
|
||||||
|
// restart context delayed, to avoid timing problems
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(waitInMs);
|
||||||
|
|
||||||
|
LOG.warn("reinitialize injection context");
|
||||||
|
context.initialize();
|
||||||
|
|
||||||
|
LOG.debug("re register injection context for events");
|
||||||
|
ScmEventBus.getInstance().register(context);
|
||||||
|
} catch ( Exception ex) {
|
||||||
|
LOG.error("failed to restart", ex);
|
||||||
|
}
|
||||||
|
}, "Delayed-Restart-" + INSTANCE_COUNTER.incrementAndGet()).start();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
38
scm-webapp/src/main/java/sonia/scm/boot/RestartStrategy.java
Normal file
38
scm-webapp/src/main/java/sonia/scm/boot/RestartStrategy.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy for restarting SCM-Manager.
|
||||||
|
*/
|
||||||
|
public interface RestartStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for Injection in SCM-Manager.
|
||||||
|
*/
|
||||||
|
interface InjectionContext {
|
||||||
|
/**
|
||||||
|
* Initialize the injection context.
|
||||||
|
*/
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the injection context.
|
||||||
|
*/
|
||||||
|
void destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart SCM-Manager.
|
||||||
|
* @param context injection context
|
||||||
|
*/
|
||||||
|
void restart(InjectionContext context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configured strategy.
|
||||||
|
*
|
||||||
|
* @return configured strategy
|
||||||
|
*/
|
||||||
|
static RestartStrategy get() {
|
||||||
|
return new InjectionContextRestartStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
import com.github.legman.Subscribe;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
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 static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class InjectionContextRestartStrategyTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RestartStrategy.InjectionContext context;
|
||||||
|
|
||||||
|
private InjectionContextRestartStrategy strategy = new InjectionContextRestartStrategy();
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setWaitToZero() {
|
||||||
|
strategy.setWaitInMs(0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCallDestroyAndInitialize() throws InterruptedException {
|
||||||
|
strategy.restart(context);
|
||||||
|
|
||||||
|
verify(context).destroy();
|
||||||
|
Thread.sleep(50L);
|
||||||
|
verify(context).initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Subscribe(async = false)
|
||||||
|
public void setEvent(String event) {
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user