simplify scm-manager bootstrap

We have simplified the scm-manager bootstrap process, by replacing the
ServletContextListener call hierarchy with much simpler ModuleProviders.
We have also removed the parent injector. Now we create always a new
injector. If something goes wrong in the process of injector creation,
we will show a nicely styled error page instead of stacktrace on a white
page.
This commit is contained in:
Sebastian Sdorra
2019-06-24 16:59:28 +02:00
parent f0bb55e77b
commit 9662b8a00b
17 changed files with 670 additions and 312 deletions

View File

@@ -0,0 +1,70 @@
package sonia.scm;
import com.google.inject.Injector;
import org.jboss.resteasy.plugins.guice.ModuleProcessor;
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
import org.jboss.resteasy.plugins.server.servlet.ListenerBootstrap;
import org.jboss.resteasy.spi.Registry;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* Resteasy initialization and dispatching. This servlet combines the initialization of
* {@link org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener} and the dispatching of
* {@link HttpServletDispatcher}. The combination is required to fix the initialization order.
*/
@Singleton
public class ResteasyAllInOneServletDispatcher extends HttpServletDispatcher {
private static final Logger LOG = LoggerFactory.getLogger(ResteasyAllInOneServletDispatcher.class);
private final Injector injector;
private ResteasyDeployment deployment;
@Inject
public ResteasyAllInOneServletDispatcher(Injector injector) {
this.injector = injector;
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
LOG.info("init resteasy");
ServletContext servletContext = servletConfig.getServletContext();
createDeployment(servletContext);
ModuleProcessor processor = createModuleProcessor();
processor.processInjector(injector);
super.init(servletConfig);
}
private void createDeployment(ServletContext servletContext) {
ListenerBootstrap config = new ListenerBootstrap(servletContext);
deployment = config.createDeployment();
deployment.start();
servletContext.setAttribute(ResteasyDeployment.class.getName(), deployment);
}
private ModuleProcessor createModuleProcessor() {
Registry registry = deployment.getRegistry();
ResteasyProviderFactory providerFactory = deployment.getProviderFactory();
return new ModuleProcessor(registry, providerFactory);
}
@Override
public void destroy() {
LOG.info("destroy resteasy");
super.destroy();
deployment.stop();
}
}

View File

@@ -2,19 +2,17 @@ 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;
/**
* Module to configure resteasy with guice.
*/
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);
serve("/api/*").with(ResteasyAllInOneServletDispatcher.class, initParams);
}
}

View File

@@ -1,183 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.assistedinject.Assisted;
import org.apache.shiro.guice.web.ShiroWebModule;
import org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.api.v2.resources.MapperModule;
import sonia.scm.cache.CacheManager;
import sonia.scm.debug.DebugModule;
import sonia.scm.filter.WebElementModule;
import sonia.scm.group.GroupManager;
import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.plugin.ExtensionProcessor;
import sonia.scm.plugin.PluginWrapper;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.schedule.Scheduler;
import sonia.scm.user.UserManager;
import sonia.scm.util.IOUtil;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import java.util.List;
import java.util.Set;
/**
*
* @author Sebastian Sdorra
*/
public class ScmContextListener extends GuiceResteasyBootstrapServletContextListener
{
/**
* the logger for ScmContextListener
*/
private static final Logger LOG = LoggerFactory.getLogger(ScmContextListener.class);
private final ClassLoader parent;
private final Set<PluginWrapper> plugins;
private Injector injector;
public interface Factory {
ScmContextListener create(ClassLoader parent, Set<PluginWrapper> plugins);
}
@Inject
public ScmContextListener(@Assisted ClassLoader parent, @Assisted Set<PluginWrapper> plugins)
{
this.parent = parent;
this.plugins = plugins;
}
public Set<PluginWrapper> getPlugins() {
return plugins;
}
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
beforeInjectorCreation();
super.contextInitialized(servletContextEvent);
afterInjectorCreation(servletContextEvent);
}
private void beforeInjectorCreation() {
}
private boolean hasStartupErrors() {
return SCMContext.getContext().getStartupError() != null;
}
@Override
protected List<? extends Module> getModules(ServletContext context) {
DefaultPluginLoader pluginLoader = new DefaultPluginLoader(context, parent, plugins);
ClassOverrides overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader());
List<Module> moduleList = Lists.newArrayList();
moduleList.add(new ResteasyModule());
moduleList.add(ShiroWebModule.guiceFilterModule());
moduleList.add(new WebElementModule(pluginLoader));
moduleList.add(new ScmServletModule(context, pluginLoader, overrides));
moduleList.add(
new ScmSecurityModule(context, pluginLoader.getExtensionProcessor())
);
appendModules(pluginLoader.getExtensionProcessor(), moduleList);
moduleList.addAll(overrides.getModules());
if (SCMContext.getContext().getStage() == Stage.DEVELOPMENT){
moduleList.add(new DebugModule());
}
moduleList.add(new MapperModule());
return moduleList;
}
private void appendModules(ExtensionProcessor ep, List<Module> moduleList) {
for (Class<? extends Module> module : ep.byExtensionPoint(Module.class)) {
try {
LOG.info("add module {}", module);
moduleList.add(module.newInstance());
} catch (IllegalAccessException | InstantiationException ex) {
throw Throwables.propagate(ex);
}
}
}
@Override
protected void withInjector(Injector injector) {
this.injector = injector;
}
private void afterInjectorCreation(ServletContextEvent event) {
if (injector != null && !hasStartupErrors()) {
bindEagerSingletons();
initializeServletContextListeners(event);
}
}
private void bindEagerSingletons() {
injector.getInstance(EagerSingletonModule.class).initialize(injector);
}
private void initializeServletContextListeners(ServletContextEvent event) {
injector.getInstance(ServletContextListenerHolder.class).contextInitialized(event);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent)
{
if (injector != null &&!hasStartupErrors()) {
closeCloseables();
destroyServletContextListeners(servletContextEvent);
}
super.contextDestroyed(servletContextEvent);
}
private void closeCloseables() {
injector.getInstance(CloseableModule.class).closeAll();
}
private void destroyServletContextListeners(ServletContextEvent event) {
injector.getInstance(ServletContextListenerHolder.class).contextDestroyed(event);
}
}

View File

@@ -81,8 +81,7 @@ public class ScmSecurityModule extends ShiroWebModule
* @param servletContext
* @param extensionProcessor
*/
ScmSecurityModule(ServletContext servletContext,
ExtensionProcessor extensionProcessor)
public ScmSecurityModule(ServletContext servletContext, ExtensionProcessor extensionProcessor)
{
super(servletContext);
this.extensionProcessor = extensionProcessor;

View File

@@ -59,8 +59,8 @@ import sonia.scm.net.ahc.ContentTransformer;
import sonia.scm.net.ahc.DefaultAdvancedHttpClient;
import sonia.scm.net.ahc.JsonContentTransformer;
import sonia.scm.net.ahc.XmlContentTransformer;
import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.plugin.DefaultPluginManager;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginManager;
import sonia.scm.repository.DefaultRepositoryManager;
import sonia.scm.repository.DefaultRepositoryProvider;
@@ -110,7 +110,6 @@ import sonia.scm.web.security.AdministrationContext;
import sonia.scm.web.security.DefaultAdministrationContext;
import javax.net.ssl.SSLContext;
import javax.servlet.ServletContext;
import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
@@ -179,9 +178,8 @@ public class ScmServletModule extends ServletModule
//~--- constructors ---------------------------------------------------------
ScmServletModule(ServletContext servletContext, DefaultPluginLoader pluginLoader, ClassOverrides overrides)
public ScmServletModule(PluginLoader pluginLoader, ClassOverrides overrides)
{
this.servletContext = servletContext;
this.pluginLoader = pluginLoader;
this.overrides = overrides;
}
@@ -206,10 +204,6 @@ public class ScmServletModule extends ServletModule
RepositoryProvider.class, Repository.class).to(
DefaultRepositoryProvider.class).in(RequestScoped.class);
// bind servlet context
bind(ServletContext.class).annotatedWith(Default.class).toInstance(
servletContext);
// bind event api
bind(ScmEventBus.class).toInstance(ScmEventBus.getInstance());
@@ -408,8 +402,5 @@ public class ScmServletModule extends ServletModule
private final ClassOverrides overrides;
/** Field description */
private final DefaultPluginLoader pluginLoader;
/** Field description */
private final ServletContext servletContext;
private final PluginLoader pluginLoader;
}

View File

@@ -0,0 +1,78 @@
package sonia.scm.boot;
import com.google.common.base.Throwables;
import com.google.inject.Module;
import org.apache.shiro.guice.web.ShiroWebModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.ClassOverrides;
import sonia.scm.ResteasyModule;
import sonia.scm.SCMContext;
import sonia.scm.ScmSecurityModule;
import sonia.scm.ScmServletModule;
import sonia.scm.Stage;
import sonia.scm.api.v2.resources.MapperModule;
import sonia.scm.debug.DebugModule;
import sonia.scm.filter.WebElementModule;
import sonia.scm.plugin.ExtensionProcessor;
import sonia.scm.plugin.PluginLoader;
import javax.servlet.ServletContext;
import java.util.ArrayList;
import java.util.List;
class ApplicationModuleProvider implements ModuleProvider {
private static final Logger LOG = LoggerFactory.getLogger(ApplicationModuleProvider.class);
private final ServletContext servletContext;
private final PluginLoader pluginLoader;
ApplicationModuleProvider(ServletContext servletContext, PluginLoader pluginLoader) {
this.servletContext = servletContext;
this.pluginLoader = pluginLoader;
}
@Override
public List<Module> createModules() {
ClassOverrides overrides = createClassOverrides();
return createModules(overrides);
}
private List<Module> createModules(ClassOverrides overrides) {
List<Module> moduleList = new ArrayList<>();
moduleList.add(new ResteasyModule());
moduleList.add(ShiroWebModule.guiceFilterModule());
moduleList.add(new WebElementModule(pluginLoader));
moduleList.add(new ScmServletModule(pluginLoader, overrides));
moduleList.add(
new ScmSecurityModule(servletContext, pluginLoader.getExtensionProcessor())
);
appendModules(pluginLoader.getExtensionProcessor(), moduleList);
moduleList.addAll(overrides.getModules());
if (SCMContext.getContext().getStage() == Stage.DEVELOPMENT){
moduleList.add(new DebugModule());
}
moduleList.add(new MapperModule());
return moduleList;
}
private ClassOverrides createClassOverrides() {
ClassLoader uberClassLoader = pluginLoader.getUberClassLoader();
return ClassOverrides.findOverrides(uberClassLoader);
}
private void appendModules(ExtensionProcessor ep, List<Module> moduleList) {
for (Class<? extends Module> module : ep.byExtensionPoint(Module.class)) {
try {
LOG.info("add module {}", module);
moduleList.add(module.newInstance());
} catch (IllegalAccessException | InstantiationException ex) {
throw Throwables.propagate(ex);
}
}
}
}

View File

@@ -82,7 +82,6 @@ public class BootstrapContextFilter extends GuiceFilter {
super.destroy();
listener.contextDestroyed(new ServletContextEvent(filterConfig.getServletContext()));
ServletContextCleaner.cleanup(filterConfig.getServletContext());
}
/**

View File

@@ -29,111 +29,134 @@
package sonia.scm.boot;
import com.google.inject.AbstractModule;
import com.google.common.collect.ImmutableList;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.servlet.GuiceServletContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.CloseableModule;
import sonia.scm.EagerSingletonModule;
import sonia.scm.SCMContext;
import sonia.scm.ScmContextListener;
import sonia.scm.ScmEventBusModule;
import sonia.scm.ScmInitializerModule;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginWrapper;
import sonia.scm.update.MigrationWizardContextListener;
import sonia.scm.update.MigrationWizardModuleProvider;
import sonia.scm.update.UpdateEngine;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Sebastian Sdorra
*/
public class BootstrapContextListener implements ServletContextListener {
public class BootstrapContextListener extends GuiceServletContextListener {
private static final Logger LOG = LoggerFactory.getLogger(BootstrapContextListener.class);
private final ClassLoaderLifeCycle classLoaderLifeCycle = ClassLoaderLifeCycle.create();
private ServletContext context;
private ServletContextListener contextListener;
private InjectionLifeCycle injectionLifeCycle;
@Override
public void contextInitialized(ServletContextEvent sce) {
classLoaderLifeCycle.init();
LOG.info("start scm-manager initialization");
context = sce.getServletContext();
classLoaderLifeCycle.init();
super.contextInitialized(sce);
createContextListener();
Injector injector = (Injector) context.getAttribute(Injector.class.getName());
injectionLifeCycle = new InjectionLifeCycle(injector);
injectionLifeCycle.initialize();
}
contextListener.contextInitialized(sce);
@Override
protected Injector getInjector() {
Throwable startupError = SCMContext.getContext().getStartupError();
if (startupError != null) {
return createStageOneInjector(SingleView.error(startupError));
} else if (Versions.isTooOld()) {
return createStageOneInjector(SingleView.view("/templates/too-old.mustache", HttpServletResponse.SC_CONFLICT));
} else {
try {
return createStageTwoInjector();
} catch (Exception ex) {
return createStageOneInjector(SingleView.error(ex));
}
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
contextListener.contextDestroyed(sce);
LOG.info("shutdown scm-manager context");
ServletContextCleaner.cleanup(context);
injectionLifeCycle.shutdown();
injectionLifeCycle = null;
classLoaderLifeCycle.shutdown();
context = null;
contextListener = null;
}
private void createContextListener() {
Throwable startupError = SCMContext.getContext().getStartupError();
if (startupError != null) {
contextListener = SingleView.error(startupError);
} else if (Versions.isTooOld()) {
contextListener = SingleView.view("/templates/too-old.mustache", HttpServletResponse.SC_CONFLICT);
} else {
createMigrationOrNormalContextListener();
Versions.writeNew();
}
}
private void createMigrationOrNormalContextListener() {
private Injector createStageTwoInjector() {
PluginBootstrap pluginBootstrap = new PluginBootstrap(context, classLoaderLifeCycle);
Injector bootstrapInjector = createBootstrapInjector(pluginBootstrap.getPluginLoader());
startEitherMigrationOrNormalServlet(classLoaderLifeCycle.getBootstrapClassLoader(), pluginBootstrap.getPlugins(), pluginBootstrap.getPluginLoader(), bootstrapInjector);
ModuleProvider provider = createMigrationOrNormalModuleProvider(pluginBootstrap);
return createStageTwoInjector(provider, pluginBootstrap.getPluginLoader());
}
private void startEitherMigrationOrNormalServlet(ClassLoader cl, Set<PluginWrapper> plugins, PluginLoader pluginLoader, Injector bootstrapInjector) {
MigrationWizardContextListener wizardContextListener = prepareWizardIfNeeded(bootstrapInjector);
private ModuleProvider createMigrationOrNormalModuleProvider(PluginBootstrap pluginBootstrap) {
Injector bootstrapInjector = createBootstrapInjector(pluginBootstrap.getPluginLoader());
if (wizardContextListener.wizardNecessary()) {
contextListener = wizardContextListener;
return startEitherMigrationOrApplication(pluginBootstrap.getPluginLoader(), bootstrapInjector);
}
private ModuleProvider startEitherMigrationOrApplication(PluginLoader pluginLoader, Injector bootstrapInjector) {
MigrationWizardModuleProvider wizardModuleProvider = new MigrationWizardModuleProvider(bootstrapInjector);
if (wizardModuleProvider.wizardNecessary()) {
return wizardModuleProvider;
} else {
processUpdates(pluginLoader, bootstrapInjector);
contextListener = bootstrapInjector.getInstance(ScmContextListener.Factory.class).create(cl, plugins);
Versions.writeNew();
return new ApplicationModuleProvider(context, pluginLoader);
}
}
private Injector createStageOneInjector(ModuleProvider provider) {
return Guice.createInjector(provider.createModules());
}
private MigrationWizardContextListener prepareWizardIfNeeded(Injector bootstrapInjector) {
return new MigrationWizardContextListener(bootstrapInjector);
private Injector createStageTwoInjector(ModuleProvider provider, PluginLoader pluginLoader) {
List<Module> modules = new ArrayList<>(createBootstrapModules(pluginLoader));
modules.addAll(provider.createModules());
return Guice.createInjector(modules);
}
private Injector createBootstrapInjector(PluginLoader pluginLoader) {
Module scmContextListenerModule = new ScmContextListenerModule();
BootstrapModule bootstrapModule = new BootstrapModule(pluginLoader);
ScmInitializerModule scmInitializerModule = new ScmInitializerModule();
EagerSingletonModule eagerSingletonModule = new EagerSingletonModule();
CloseableModule closeableModule = new CloseableModule();
ScmEventBusModule scmEventBusModule = new ScmEventBusModule();
return Guice.createInjector(createBootstrapModules(pluginLoader));
}
return Guice.createInjector(
bootstrapModule,
scmContextListenerModule,
scmEventBusModule,
scmInitializerModule,
eagerSingletonModule,
closeableModule
private List<Module> createBootstrapModules(PluginLoader pluginLoader) {
List<Module> modules = new ArrayList<>(createBaseModules());
modules.add(new BootstrapModule(pluginLoader));
return modules;
}
private List<Module> createBaseModules() {
return ImmutableList.of(
new EagerSingletonModule(),
new ScmInitializerModule(),
new ScmEventBusModule(),
new ServletContextModule(),
new CloseableModule()
);
}
@@ -144,10 +167,4 @@ public class BootstrapContextListener implements ServletContextListener {
updateEngine.update();
}
private static class ScmContextListenerModule extends AbstractModule {
@Override
protected void configure() {
install(new FactoryModuleBuilder().build(ScmContextListener.Factory.class));
}
}
}

View File

@@ -0,0 +1,60 @@
package sonia.scm.boot;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import sonia.scm.CloseableModule;
import sonia.scm.Default;
import sonia.scm.EagerSingletonModule;
import sonia.scm.ServletContextListenerHolder;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import java.util.Optional;
class InjectionLifeCycle {
private final Injector injector;
InjectionLifeCycle(Injector injector) {
this.injector = injector;
}
void initialize() {
initializeEagerSingletons();
initializeServletContextListeners();
}
void shutdown() {
destroyServletContextListeners();
closeRegisteredCloseables();
}
private void initializeServletContextListeners() {
ServletContextListenerHolder instance = injector.getInstance(ServletContextListenerHolder.class);
ServletContext context = injector.getInstance(Key.get(ServletContext.class, Default.class));
instance.contextInitialized(new ServletContextEvent(context));
}
private void initializeEagerSingletons() {
findInstance(EagerSingletonModule.class).ifPresent(m -> m.initialize(injector));
}
private void closeRegisteredCloseables() {
findInstance(CloseableModule.class).ifPresent(CloseableModule::closeAll);
}
private void destroyServletContextListeners() {
ServletContextListenerHolder instance = injector.getInstance(ServletContextListenerHolder.class);
ServletContext context = injector.getInstance(Key.get(ServletContext.class, Default.class));
instance.contextDestroyed(new ServletContextEvent(context));
}
private <T> Optional<T> findInstance(Class<T> clazz) {
Binding<T> binding = injector.getExistingBinding(Key.get(clazz));
if (binding != null) {
return Optional.of(binding.getProvider().get());
}
return Optional.empty();
}
}

View File

@@ -0,0 +1,11 @@
package sonia.scm.boot;
import com.google.inject.Module;
import java.util.Collection;
public interface ModuleProvider {
Collection<Module> createModules();
}

View File

@@ -0,0 +1,14 @@
package sonia.scm.boot;
import com.google.inject.servlet.ServletModule;
import sonia.scm.Default;
import javax.servlet.ServletContext;
class ServletContextModule extends ServletModule {
@Override
protected void configureServlets() {
bind(ServletContext.class).annotatedWith(Default.class).toInstance(getServletContext());
}
}

View File

@@ -1,11 +1,10 @@
package sonia.scm.boot;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule;
import sonia.scm.Default;
import sonia.scm.SCMContext;
@@ -14,17 +13,16 @@ import sonia.scm.template.MustacheTemplateEngine;
import sonia.scm.template.TemplateEngine;
import sonia.scm.template.TemplateEngineFactory;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
final class SingleView {
private SingleView() {
}
static ServletContextListener error(Throwable throwable) {
static SingleViewModuleProvider error(Throwable throwable) {
String error = Throwables.getStackTraceAsString(throwable);
ViewController controller = new SimpleViewController("/templates/error.mustache", request -> {
@@ -34,30 +32,30 @@ final class SingleView {
);
return new View(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, model);
});
return new SingleViewContextListener(controller);
return new SingleViewModuleProvider(controller);
}
static ServletContextListener view(String template, int sc) {
static SingleViewModuleProvider view(String template, int sc) {
ViewController controller = new SimpleViewController(template, request -> {
Object model = ImmutableMap.of(
"contextPath", request.getContextPath()
);
return new View(sc, model);
});
return new SingleViewContextListener(controller);
return new SingleViewModuleProvider(controller);
}
private static class SingleViewContextListener extends GuiceServletContextListener {
private static class SingleViewModuleProvider implements ModuleProvider {
private final ViewController controller;
private SingleViewContextListener(ViewController controller) {
private SingleViewModuleProvider(ViewController controller) {
this.controller = controller;
}
@Override
protected Injector getInjector() {
return Guice.createInjector(new SingleViewModule(controller));
public Collection<Module> createModules() {
return ImmutableList.of(new ServletContextModule(), new SingleViewModule(controller));
}
}
@@ -84,8 +82,6 @@ final class SingleView {
MustacheTemplateEngine.class);
bind(TemplateEngineFactory.class);
bind(ServletContext.class).annotatedWith(Default.class).toInstance(getServletContext());
serve("/images/*", "/styles/*", "/favicon.ico").with(StaticResourceServlet.class);
serve("/*").with(SingleViewServlet.class);
}

View File

@@ -1,14 +1,18 @@
package sonia.scm.update;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.Module;
import sonia.scm.boot.ModuleProvider;
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
public class MigrationWizardContextListener extends GuiceServletContextListener {
import java.util.Collection;
import java.util.Collections;
public class MigrationWizardModuleProvider implements ModuleProvider {
private final Injector bootstrapInjector;
public MigrationWizardContextListener(Injector bootstrapInjector) {
public MigrationWizardModuleProvider(Injector bootstrapInjector) {
this.bootstrapInjector = bootstrapInjector;
}
@@ -17,7 +21,7 @@ public class MigrationWizardContextListener extends GuiceServletContextListener
}
@Override
protected Injector getInjector() {
return bootstrapInjector.createChildInjector(new MigrationWizardModule());
public Collection<Module> createModules() {
return Collections.singleton(new MigrationWizardModule());
}
}