mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 17:05:43 +01:00
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:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,7 +82,6 @@ public class BootstrapContextFilter extends GuiceFilter {
|
||||
super.destroy();
|
||||
|
||||
listener.contextDestroyed(new ServletContextEvent(filterConfig.getServletContext()));
|
||||
ServletContextCleaner.cleanup(filterConfig.getServletContext());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
11
scm-webapp/src/main/java/sonia/scm/boot/ModuleProvider.java
Normal file
11
scm-webapp/src/main/java/sonia/scm/boot/ModuleProvider.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.google.inject.Module;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ModuleProvider {
|
||||
|
||||
Collection<Module> createModules();
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user