mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-15 09:46:16 +01:00
added RestartServlet, which is able to trigger a complete restart of the context
This commit is contained in:
20
scm-webapp/src/main/java/sonia/scm/ResteasyModule.java
Normal file
20
scm-webapp/src/main/java/sonia/scm/ResteasyModule.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package sonia.scm;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.inject.servlet.ServletModule;
|
||||||
|
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
|
||||||
|
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ResteasyModule extends ServletModule {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureServlets() {
|
||||||
|
bind(HttpServletDispatcher.class).in(Singleton.class);
|
||||||
|
|
||||||
|
Map<String, String> initParams = ImmutableMap.of(ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX, "/api");
|
||||||
|
serve("/api/*").with(HttpServletDispatcher.class, initParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -126,6 +126,7 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
|||||||
ClassOverrides overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader());
|
ClassOverrides overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader());
|
||||||
List<Module> moduleList = Lists.newArrayList();
|
List<Module> moduleList = Lists.newArrayList();
|
||||||
|
|
||||||
|
moduleList.add(new ResteasyModule());
|
||||||
moduleList.add(new ScmInitializerModule());
|
moduleList.add(new ScmInitializerModule());
|
||||||
moduleList.add(new ScmEventBusModule());
|
moduleList.add(new ScmEventBusModule());
|
||||||
moduleList.add(new EagerSingletonModule());
|
moduleList.add(new EagerSingletonModule());
|
||||||
|
|||||||
@@ -34,22 +34,19 @@ package sonia.scm.boot;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.github.legman.Subscribe;
|
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.SCMContext;
|
import sonia.scm.SCMContext;
|
||||||
import sonia.scm.Stage;
|
import sonia.scm.Stage;
|
||||||
import sonia.scm.event.ScmEventBus;
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
|
||||||
|
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
import javax.servlet.ServletContextEvent;
|
import javax.servlet.ServletContextEvent;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
@@ -65,6 +62,8 @@ public class BootstrapContextFilter extends GuiceFilter
|
|||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|
||||||
|
private final BootstrapContextListener listener = new BootstrapContextListener();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restart the whole webapp context.
|
* Restart the whole webapp context.
|
||||||
*
|
*
|
||||||
@@ -85,29 +84,20 @@ public class BootstrapContextFilter extends GuiceFilter
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
logger.warn("destroy filter pipeline, because of a received restart event");
|
||||||
logger.warn(
|
|
||||||
"destroy filter pipeline, because of a received restart event");
|
|
||||||
destroy();
|
destroy();
|
||||||
logger.warn(
|
|
||||||
"reinitialize filter pipeline, because of a received restart event");
|
logger.warn("reinitialize filter pipeline, because of a received restart event");
|
||||||
super.init(filterConfig);
|
initGuice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method description
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param filterConfig
|
|
||||||
*
|
|
||||||
* @throws ServletException
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig filterConfig) throws ServletException
|
public void init(FilterConfig filterConfig) throws ServletException
|
||||||
{
|
{
|
||||||
this.filterConfig = filterConfig;
|
this.filterConfig = filterConfig;
|
||||||
super.init(filterConfig);
|
|
||||||
|
initGuice();
|
||||||
|
|
||||||
if (SCMContext.getContext().getStage() == Stage.DEVELOPMENT)
|
if (SCMContext.getContext().getStage() == Stage.DEVELOPMENT)
|
||||||
{
|
{
|
||||||
@@ -116,6 +106,19 @@ public class BootstrapContextFilter extends GuiceFilter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initGuice() throws ServletException {
|
||||||
|
super.init(filterConfig);
|
||||||
|
|
||||||
|
listener.contextInitialized(new ServletContextEvent(filterConfig.getServletContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
super.destroy();
|
||||||
|
listener.contextDestroyed(new ServletContextEvent(filterConfig.getServletContext()));
|
||||||
|
ServletContextCleaner.cleanup(filterConfig.getServletContext());
|
||||||
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
|
|||||||
@@ -187,40 +187,12 @@ public class BootstrapContextListener implements ServletContextListener
|
|||||||
return Boolean.getBoolean("sonia.scm.boot.disable-core-plugin-extraction");
|
return Boolean.getBoolean("sonia.scm.boot.disable-core-plugin-extraction");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Restart the whole webapp context.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param event restart event
|
|
||||||
*/
|
|
||||||
@Subscribe
|
|
||||||
public void handleRestartEvent(RestartEvent event)
|
|
||||||
{
|
|
||||||
logger.warn("received restart event from {} with reason: {}",
|
|
||||||
event.getCause(), event.getReason());
|
|
||||||
|
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
logger.error("context is null, scm-manager is not initialized");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ServletContextEvent sce = new ServletContextEvent(context);
|
|
||||||
|
|
||||||
logger.warn("destroy context, because of a received restart event");
|
|
||||||
contextDestroyed(sce);
|
|
||||||
logger.warn("reinitialize context, because of a received restart event");
|
|
||||||
contextInitialized(sce);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method description
|
* Method description
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param pluginDirectory
|
* @param pluginDirectory
|
||||||
* @param name
|
|
||||||
* @param entry
|
* @param entry
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
|||||||
97
scm-webapp/src/main/java/sonia/scm/boot/RestartServlet.java
Normal file
97
scm-webapp/src/main/java/sonia/scm/boot/RestartServlet.java
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.Priority;
|
||||||
|
import sonia.scm.SCMContext;
|
||||||
|
import sonia.scm.Stage;
|
||||||
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
import sonia.scm.filter.WebElement;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This servlet sends a {@link RestartEvent} to the {@link ScmEventBus} which causes scm-manager to restart the context.
|
||||||
|
* The {@link RestartServlet} can be used for reloading java code or for installing plugins without a complete restart.
|
||||||
|
* At the moment the Servlet accepts only request, if scm-manager was started in the {@link Stage#DEVELOPMENT} stage.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@Priority(0)
|
||||||
|
@WebElement("/restart")
|
||||||
|
public class RestartServlet extends HttpServlet {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RestartServlet.class);
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private final AtomicBoolean restarting = new AtomicBoolean();
|
||||||
|
|
||||||
|
private final ScmEventBus eventBus;
|
||||||
|
private final Stage stage;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RestartServlet() {
|
||||||
|
this(ScmEventBus.getInstance(), SCMContext.getContext().getStage());
|
||||||
|
}
|
||||||
|
|
||||||
|
RestartServlet(ScmEventBus eventBus, Stage stage) {
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.stage = stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
||||||
|
LOG.info("received sendRestartEvent request");
|
||||||
|
|
||||||
|
if (isRestartAllowed()) {
|
||||||
|
|
||||||
|
try (InputStream requestInput = req.getInputStream()) {
|
||||||
|
Reason reason = objectMapper.readValue(requestInput, Reason.class);
|
||||||
|
sendRestartEvent(resp, reason);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOG.warn("failed to trigger sendRestartEvent event", ex);
|
||||||
|
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LOG.debug("received restart event in non development stage");
|
||||||
|
resp.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRestartAllowed() {
|
||||||
|
return stage == Stage.DEVELOPMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendRestartEvent(HttpServletResponse response, Reason reason) {
|
||||||
|
if ( restarting.compareAndSet(false, true) ) {
|
||||||
|
LOG.info("trigger sendRestartEvent, because of {}", reason.getMessage());
|
||||||
|
eventBus.post(new RestartEvent(RestartServlet.class, reason.getMessage()));
|
||||||
|
|
||||||
|
response.setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||||
|
} else {
|
||||||
|
LOG.warn("scm-manager restarts already");
|
||||||
|
response.setStatus(HttpServletResponse.SC_CONFLICT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Reason {
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove cached resources from {@link ServletContext} to allow a clean restart of scm-manager without stale or
|
||||||
|
* duplicated data.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
final class ServletContextCleaner {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ServletContextCleaner.class);
|
||||||
|
|
||||||
|
private static final Set<String> REMOVE_PREFIX = ImmutableSet.of(
|
||||||
|
"org.jboss.resteasy",
|
||||||
|
"resteasy",
|
||||||
|
"org.apache.shiro",
|
||||||
|
"sonia.scm"
|
||||||
|
);
|
||||||
|
|
||||||
|
private ServletContextCleaner() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove cached attributes from {@link ServletContext}.
|
||||||
|
*
|
||||||
|
* @param servletContext servlet context
|
||||||
|
*/
|
||||||
|
static void cleanup(ServletContext servletContext) {
|
||||||
|
LOG.info("remove cached attributes from context");
|
||||||
|
|
||||||
|
Enumeration<String> attributeNames = servletContext.getAttributeNames();
|
||||||
|
while( attributeNames.hasMoreElements()) {
|
||||||
|
String name = attributeNames.nextElement();
|
||||||
|
if (shouldRemove(name)) {
|
||||||
|
LOG.info("remove attribute {} from servlet context", name);
|
||||||
|
servletContext.removeAttribute(name);
|
||||||
|
} else {
|
||||||
|
LOG.info("keep attribute {} in servlet context", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean shouldRemove(String name) {
|
||||||
|
for (String prefix : REMOVE_PREFIX) {
|
||||||
|
if (name.startsWith(prefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,9 +66,6 @@
|
|||||||
<logger name="sonia.scm.plugin.ext.DefaultAnnotationScanner" level="INFO" />
|
<logger name="sonia.scm.plugin.ext.DefaultAnnotationScanner" level="INFO" />
|
||||||
<logger name="sonia.scm.security.ConfigurableLoginAttemptHandler" level="DEBUG" />
|
<logger name="sonia.scm.security.ConfigurableLoginAttemptHandler" level="DEBUG" />
|
||||||
|
|
||||||
<!-- event bus -->
|
|
||||||
<logger name="sonia.scm.event.LegmanScmEventBus" level="INFO" />
|
|
||||||
|
|
||||||
<!-- cgi -->
|
<!-- cgi -->
|
||||||
<logger name="sonia.scm.web.cgi.DefaultCGIExecutor" level="DEBUG" />
|
<logger name="sonia.scm.web.cgi.DefaultCGIExecutor" level="DEBUG" />
|
||||||
|
|
||||||
@@ -93,7 +90,9 @@
|
|||||||
<logger name="net.sf.ehcache" level="DEBUG" />
|
<logger name="net.sf.ehcache" level="DEBUG" />
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<logger name="org.jboss.resteasy" level="DEBUG" />
|
<logger name="org.jboss.resteasy" level="INFO" />
|
||||||
|
|
||||||
|
<logger name="sonia.scm.boot.RestartServlet" level="TRACE" />
|
||||||
|
|
||||||
<root level="WARN">
|
<root level="WARN">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
|
|||||||
@@ -41,10 +41,6 @@
|
|||||||
|
|
||||||
<!-- bootstraping -->
|
<!-- bootstraping -->
|
||||||
|
|
||||||
<listener>
|
|
||||||
<listener-class>sonia.scm.boot.BootstrapContextListener</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
<filter>
|
<filter>
|
||||||
<filter-name>BootstrapFilter</filter-name>
|
<filter-name>BootstrapFilter</filter-name>
|
||||||
<filter-class>sonia.scm.boot.BootstrapContextFilter</filter-class>
|
<filter-class>sonia.scm.boot.BootstrapContextFilter</filter-class>
|
||||||
@@ -55,25 +51,6 @@
|
|||||||
<url-pattern>/*</url-pattern>
|
<url-pattern>/*</url-pattern>
|
||||||
</filter-mapping>
|
</filter-mapping>
|
||||||
|
|
||||||
<!-- rest -->
|
|
||||||
|
|
||||||
<context-param>
|
|
||||||
<param-name>resteasy.servlet.mapping.prefix</param-name>
|
|
||||||
<param-value>/api</param-value>
|
|
||||||
</context-param>
|
|
||||||
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>Resteasy</servlet-name>
|
|
||||||
<servlet-class>
|
|
||||||
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
|
|
||||||
</servlet-class>
|
|
||||||
</servlet>
|
|
||||||
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>Resteasy</servlet-name>
|
|
||||||
<url-pattern>/api/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!-- capture sessions -->
|
<!-- capture sessions -->
|
||||||
<!--
|
<!--
|
||||||
TODO remove, we need no longer a session
|
TODO remove, we need no longer a session
|
||||||
|
|||||||
133
scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java
Normal file
133
scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
import com.github.legman.Subscribe;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import sonia.scm.Stage;
|
||||||
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
import sonia.scm.event.ScmTestEventBus;
|
||||||
|
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class RestartServletTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HttpServletRequest request;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HttpServletResponse response;
|
||||||
|
|
||||||
|
private RestartServlet restartServlet;
|
||||||
|
|
||||||
|
private EventListener listener;
|
||||||
|
|
||||||
|
private void setUpObjectUnderTest(Stage stage) {
|
||||||
|
listener = new EventListener();
|
||||||
|
ScmEventBus eventBus = ScmTestEventBus.getInstance();
|
||||||
|
eventBus.register(listener);
|
||||||
|
|
||||||
|
restartServlet = new RestartServlet(eventBus, stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestart() throws IOException {
|
||||||
|
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||||
|
setRequestInputReason("something changed");
|
||||||
|
|
||||||
|
restartServlet.doPost(request, response);
|
||||||
|
|
||||||
|
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||||
|
|
||||||
|
RestartEvent restartEvent = listener.restartEvent;
|
||||||
|
assertThat(restartEvent).isNotNull();
|
||||||
|
assertThat(restartEvent.getCause()).isEqualTo(RestartServlet.class);
|
||||||
|
assertThat(restartEvent.getReason()).isEqualTo("something changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestartCalledTwice() throws IOException {
|
||||||
|
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||||
|
|
||||||
|
setRequestInputReason("initial change");
|
||||||
|
restartServlet.doPost(request, response);
|
||||||
|
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||||
|
|
||||||
|
setRequestInputReason("changed again");
|
||||||
|
restartServlet.doPost(request, response);
|
||||||
|
verify(response).setStatus(HttpServletResponse.SC_CONFLICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestartWithInvalidContent() throws IOException {
|
||||||
|
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||||
|
|
||||||
|
setRequestInputContent("invalid json");
|
||||||
|
|
||||||
|
restartServlet.doPost(request, response);
|
||||||
|
|
||||||
|
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestartInProductionStage() throws IOException {
|
||||||
|
setUpObjectUnderTest(Stage.PRODUCTION);
|
||||||
|
|
||||||
|
setRequestInputReason("initial change");
|
||||||
|
|
||||||
|
restartServlet.doPost(request, response);
|
||||||
|
|
||||||
|
verify(response).setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRequestInputReason(String message) throws IOException {
|
||||||
|
String content = createReason(message);
|
||||||
|
setRequestInputContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRequestInputContent(String content) throws IOException {
|
||||||
|
InputStream input = createReasonAsInputStream(content);
|
||||||
|
when(request.getInputStream()).thenReturn(createServletInputStream(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServletInputStream createServletInputStream(final InputStream inputStream) {
|
||||||
|
return new ServletInputStream() {
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return inputStream.read();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream createReasonAsInputStream(String content) {
|
||||||
|
return new ByteArrayInputStream(content.getBytes(Charsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createReason(String message) {
|
||||||
|
return String.format("{\"message\": \"%s\"}", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EventListener {
|
||||||
|
|
||||||
|
private RestartEvent restartEvent;
|
||||||
|
|
||||||
|
@Subscribe(async = false)
|
||||||
|
public void store(RestartEvent restartEvent) {
|
||||||
|
this.restartEvent = restartEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package sonia.scm.boot;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class ServletContextCleanerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ServletContext servletContext;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanup() {
|
||||||
|
Set<String> names = ImmutableSet.of(
|
||||||
|
"org.jboss.resteasy.Dispatcher",
|
||||||
|
"resteasy.Deployment",
|
||||||
|
"sonia.scm.Context",
|
||||||
|
"org.eclipse.jetty.HttpServer",
|
||||||
|
"javax.servlet.Context",
|
||||||
|
"org.apache.shiro.SecurityManager"
|
||||||
|
);
|
||||||
|
|
||||||
|
when(servletContext.getAttributeNames()).thenReturn(toEnumeration(names));
|
||||||
|
|
||||||
|
ServletContextCleaner.cleanup(servletContext);
|
||||||
|
|
||||||
|
verify(servletContext).removeAttribute("org.jboss.resteasy.Dispatcher");
|
||||||
|
verify(servletContext).removeAttribute("resteasy.Deployment");
|
||||||
|
verify(servletContext).removeAttribute("sonia.scm.Context");
|
||||||
|
verify(servletContext, never()).removeAttribute("org.eclipse.jetty.HttpServer");
|
||||||
|
verify(servletContext, never()).removeAttribute("javax.servlet.Context");
|
||||||
|
verify(servletContext).removeAttribute("org.apache.shiro.SecurityManager");
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Enumeration<T> toEnumeration(Collection<T> collection) {
|
||||||
|
return new Vector<>(collection).elements();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user