mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
@@ -37,6 +37,7 @@ 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;
|
||||
@@ -55,6 +56,7 @@ import sonia.scm.upgrade.UpgradeManager;
|
||||
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.Collections;
|
||||
@@ -77,9 +79,12 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
||||
private final Set<PluginWrapper> plugins;
|
||||
private Injector injector;
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
public ScmContextListener(ClassLoader parent, Set<PluginWrapper> plugins)
|
||||
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;
|
||||
@@ -127,9 +132,6 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
||||
List<Module> moduleList = Lists.newArrayList();
|
||||
|
||||
moduleList.add(new ResteasyModule());
|
||||
moduleList.add(new ScmInitializerModule());
|
||||
moduleList.add(new ScmEventBusModule());
|
||||
moduleList.add(new EagerSingletonModule());
|
||||
moduleList.add(ShiroWebModule.guiceFilterModule());
|
||||
moduleList.add(new WebElementModule(pluginLoader));
|
||||
moduleList.add(new ScmServletModule(context, pluginLoader, overrides));
|
||||
|
||||
@@ -212,12 +212,7 @@ public class ScmServletModule extends ServletModule
|
||||
{
|
||||
install(ThrowingProviderBinder.forModule(this));
|
||||
|
||||
SCMContextProvider context = SCMContext.getContext();
|
||||
|
||||
bind(SCMContextProvider.class).toInstance(context);
|
||||
|
||||
ScmConfiguration config = getScmConfiguration();
|
||||
CipherUtil cu = CipherUtil.getInstance();
|
||||
|
||||
bind(NamespaceStrategy.class).toProvider(NamespaceStrategyProvider.class);
|
||||
|
||||
@@ -234,21 +229,11 @@ public class ScmServletModule extends ServletModule
|
||||
bind(ScmEventBus.class).toInstance(ScmEventBus.getInstance());
|
||||
|
||||
// bind core
|
||||
bind(ConfigurationStoreFactory.class, JAXBConfigurationStoreFactory.class);
|
||||
bind(ConfigurationEntryStoreFactory.class, JAXBConfigurationEntryStoreFactory.class);
|
||||
bind(DataStoreFactory.class, JAXBDataStoreFactory.class);
|
||||
bind(BlobStoreFactory.class, FileBlobStoreFactory.class);
|
||||
bind(ScmConfiguration.class).toInstance(config);
|
||||
bind(PluginLoader.class).toInstance(pluginLoader);
|
||||
bind(PluginManager.class, DefaultPluginManager.class);
|
||||
|
||||
// bind scheduler
|
||||
bind(Scheduler.class).to(QuartzScheduler.class);
|
||||
|
||||
// note CipherUtil uses an other generator
|
||||
bind(KeyGenerator.class).to(DefaultKeyGenerator.class);
|
||||
bind(CipherHandler.class).toInstance(cu.getCipherHandler());
|
||||
bind(FileSystem.class, DefaultFileSystem.class);
|
||||
|
||||
// bind health check stuff
|
||||
bind(HealthCheckContextListener.class);
|
||||
@@ -327,7 +312,6 @@ public class ScmServletModule extends ServletModule
|
||||
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
|
||||
|
||||
// bind events
|
||||
// bind(LastModifiedUpdateListener.class);
|
||||
|
||||
bind(AccessTokenCookieIssuer.class).to(DefaultAccessTokenCookieIssuer.class);
|
||||
bind(PushStateDispatcher.class).toProvider(PushStateDispatcherProvider.class);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
|
||||
*
|
||||
* <p>
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
@@ -11,7 +11,7 @@
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
@@ -22,67 +22,65 @@
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.boot;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.EagerSingletonModule;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.ScmContextListener;
|
||||
import sonia.scm.ScmEventBusModule;
|
||||
import sonia.scm.ScmInitializerModule;
|
||||
import sonia.scm.Stage;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.plugin.DefaultPluginLoader;
|
||||
import sonia.scm.plugin.Plugin;
|
||||
import sonia.scm.plugin.PluginException;
|
||||
import sonia.scm.plugin.PluginLoadException;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.plugin.PluginWrapper;
|
||||
import sonia.scm.plugin.PluginsInternal;
|
||||
import sonia.scm.plugin.SmpArchive;
|
||||
import sonia.scm.update.UpdateEngine;
|
||||
import sonia.scm.util.ClassLoaders;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import javax.xml.bind.DataBindingException;
|
||||
import javax.xml.bind.JAXB;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class BootstrapContextListener implements ServletContextListener
|
||||
{
|
||||
public class BootstrapContextListener implements ServletContextListener {
|
||||
|
||||
/** Field description */
|
||||
private static final String DIRECTORY_PLUGINS = "plugins";
|
||||
@@ -109,22 +107,16 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
* @param sce
|
||||
*/
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
contextListener.contextDestroyed(sce);
|
||||
|
||||
for (PluginWrapper plugin : contextListener.getPlugins())
|
||||
{
|
||||
for (PluginWrapper plugin : contextListener.getPlugins()) {
|
||||
ClassLoader pcl = plugin.getClassLoader();
|
||||
|
||||
if (pcl instanceof Closeable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (pcl instanceof Closeable) {
|
||||
try {
|
||||
((Closeable) pcl).close();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
} catch (IOException ex) {
|
||||
logger.warn("could not close plugin classloader", ex);
|
||||
}
|
||||
}
|
||||
@@ -141,43 +133,68 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
* @param sce
|
||||
*/
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
context = sce.getServletContext();
|
||||
|
||||
File pluginDirectory = getPluginDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
createContextListener(pluginDirectory);
|
||||
|
||||
contextListener.contextInitialized(sce);
|
||||
|
||||
// register for restart events
|
||||
if (!registered && (SCMContext.getContext().getStage() == Stage.DEVELOPMENT)) {
|
||||
logger.info("register for restart events");
|
||||
ScmEventBus.getInstance().register(this);
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void createContextListener(File pluginDirectory) {
|
||||
try {
|
||||
if (!isCorePluginExtractionDisabled()) {
|
||||
extractCorePlugins(context, pluginDirectory);
|
||||
} else {
|
||||
logger.info("core plugin extraction is disabled");
|
||||
}
|
||||
|
||||
ClassLoader cl =
|
||||
ClassLoaders.getContextClassLoader(BootstrapContextListener.class);
|
||||
ClassLoader cl = ClassLoaders.getContextClassLoader(BootstrapContextListener.class);
|
||||
|
||||
Set<PluginWrapper> plugins = PluginsInternal.collectPlugins(cl,
|
||||
pluginDirectory.toPath());
|
||||
Set<PluginWrapper> plugins = PluginsInternal.collectPlugins(cl, pluginDirectory.toPath());
|
||||
|
||||
contextListener = new ScmContextListener(cl, plugins);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
PluginLoader pluginLoader = new DefaultPluginLoader(context, cl, plugins);
|
||||
|
||||
Injector bootstrapInjector = createBootstrapInjector(pluginLoader);
|
||||
|
||||
processUpdates(pluginLoader, bootstrapInjector);
|
||||
|
||||
contextListener = bootstrapInjector.getInstance(ScmContextListener.Factory.class).create(cl, plugins);
|
||||
} catch (IOException ex) {
|
||||
throw new PluginLoadException("could not load plugins", ex);
|
||||
}
|
||||
}
|
||||
|
||||
contextListener.contextInitialized(sce);
|
||||
|
||||
// register for restart events
|
||||
if (!registered
|
||||
&& (SCMContext.getContext().getStage() == Stage.DEVELOPMENT))
|
||||
{
|
||||
logger.info("register for restart events");
|
||||
ScmEventBus.getInstance().register(this);
|
||||
registered = true;
|
||||
}
|
||||
private Injector createBootstrapInjector(PluginLoader pluginLoader) {
|
||||
Module scmContextListenerModule = new ScmContextListenerModule();
|
||||
BootstrapModule bootstrapModule = new BootstrapModule(pluginLoader);
|
||||
ScmInitializerModule scmInitializerModule = new ScmInitializerModule();
|
||||
EagerSingletonModule eagerSingletonModule = new EagerSingletonModule();
|
||||
ScmEventBusModule scmEventBusModule = new ScmEventBusModule();
|
||||
|
||||
return Guice.createInjector(
|
||||
bootstrapModule,
|
||||
scmContextListenerModule,
|
||||
scmEventBusModule,
|
||||
scmInitializerModule,
|
||||
eagerSingletonModule
|
||||
);
|
||||
}
|
||||
|
||||
private void processUpdates(PluginLoader pluginLoader, Injector bootstrapInjector) {
|
||||
Injector updateInjector = bootstrapInjector.createChildInjector(new UpdateStepModule(pluginLoader));
|
||||
|
||||
UpdateEngine updateEngine = updateInjector.getInstance(UpdateEngine.class);
|
||||
updateEngine.update();
|
||||
}
|
||||
|
||||
private boolean isCorePluginExtractionDisabled() {
|
||||
@@ -195,41 +212,32 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
* @throws IOException
|
||||
*/
|
||||
private void extractCorePlugin(ServletContext context, File pluginDirectory,
|
||||
PluginIndexEntry entry)
|
||||
throws IOException
|
||||
{
|
||||
PluginIndexEntry entry)
|
||||
throws IOException {
|
||||
URL url = context.getResource(PLUGIN_DIRECTORY.concat(entry.getName()));
|
||||
SmpArchive archive = SmpArchive.create(url);
|
||||
Plugin plugin = archive.getPlugin();
|
||||
|
||||
File directory = PluginsInternal.createPluginDirectory(pluginDirectory,
|
||||
plugin);
|
||||
plugin);
|
||||
File checksumFile = PluginsInternal.getChecksumFile(directory);
|
||||
|
||||
if (!directory.exists())
|
||||
{
|
||||
if (!directory.exists()) {
|
||||
logger.warn("install plugin {}", plugin.getInformation().getId());
|
||||
PluginsInternal.extract(archive, entry.getChecksum(), directory,
|
||||
checksumFile, true);
|
||||
}
|
||||
else if (!checksumFile.exists())
|
||||
{
|
||||
} else if (!checksumFile.exists()) {
|
||||
logger.warn("plugin directory {} exists without checksum file.",
|
||||
directory);
|
||||
PluginsInternal.extract(archive, entry.getChecksum(), directory,
|
||||
checksumFile, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
String checksum = Files.toString(checksumFile, Charsets.UTF_8).trim();
|
||||
|
||||
if (checksum.equals(entry.getChecksum()))
|
||||
{
|
||||
if (checksum.equals(entry.getChecksum())) {
|
||||
logger.debug("plugin {} is up to date",
|
||||
plugin.getInformation().getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
logger.warn("checksum mismatch of pluing {}, start update",
|
||||
plugin.getInformation().getId());
|
||||
PluginsInternal.extract(archive, entry.getChecksum(), directory,
|
||||
@@ -247,14 +255,12 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void extractCorePlugins(ServletContext context, File pluginDirectory) throws IOException
|
||||
{
|
||||
private void extractCorePlugins(ServletContext context, File pluginDirectory) throws IOException {
|
||||
IOUtil.mkdirs(pluginDirectory);
|
||||
|
||||
PluginIndex index = readCorePluginIndex(context);
|
||||
|
||||
for (PluginIndexEntry entry : index)
|
||||
{
|
||||
for (PluginIndexEntry entry : index) {
|
||||
extractCorePlugin(context, pluginDirectory, entry);
|
||||
}
|
||||
}
|
||||
@@ -267,27 +273,20 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private PluginIndex readCorePluginIndex(ServletContext context)
|
||||
{
|
||||
private PluginIndex readCorePluginIndex(ServletContext context) {
|
||||
PluginIndex index = null;
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
URL indexUrl = context.getResource(PLUGIN_COREINDEX);
|
||||
|
||||
if (indexUrl == null)
|
||||
{
|
||||
if (indexUrl == null) {
|
||||
throw new PluginException("no core plugin index found");
|
||||
}
|
||||
|
||||
index = JAXB.unmarshal(indexUrl, PluginIndex.class);
|
||||
}
|
||||
catch (MalformedURLException ex)
|
||||
{
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new PluginException("could not load core plugin index", ex);
|
||||
}
|
||||
catch (DataBindingException ex)
|
||||
{
|
||||
} catch (DataBindingException ex) {
|
||||
throw new PluginException("could not unmarshall core plugin index", ex);
|
||||
}
|
||||
|
||||
@@ -302,8 +301,7 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File getPluginDirectory()
|
||||
{
|
||||
private File getPluginDirectory() {
|
||||
File baseDirectory = SCMContext.getContext().getBaseDirectory();
|
||||
|
||||
return new File(baseDirectory, DIRECTORY_PLUGINS);
|
||||
@@ -315,13 +313,12 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 14/07/09
|
||||
* @author Enter your name here...
|
||||
* @version Enter version here..., 14/07/09
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "plugin-index")
|
||||
private static class PluginIndex implements Iterable<PluginIndexEntry>
|
||||
{
|
||||
private static class PluginIndex implements Iterable<PluginIndexEntry> {
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -330,8 +327,7 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Iterator<PluginIndexEntry> iterator()
|
||||
{
|
||||
public Iterator<PluginIndexEntry> iterator() {
|
||||
return getPlugins().iterator();
|
||||
}
|
||||
|
||||
@@ -343,10 +339,8 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<PluginIndexEntry> getPlugins()
|
||||
{
|
||||
if (plugins == null)
|
||||
{
|
||||
public List<PluginIndexEntry> getPlugins() {
|
||||
if (plugins == null) {
|
||||
plugins = ImmutableList.of();
|
||||
}
|
||||
|
||||
@@ -365,13 +359,12 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 14/07/09
|
||||
* @author Enter your name here...
|
||||
* @version Enter version here..., 14/07/09
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
@XmlRootElement(name = "plugins")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
private static class PluginIndexEntry
|
||||
{
|
||||
private static class PluginIndexEntry {
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -379,8 +372,7 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getChecksum()
|
||||
{
|
||||
public String getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
@@ -390,8 +382,7 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -415,4 +406,11 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
|
||||
/** Field description */
|
||||
private boolean registered = false;
|
||||
|
||||
private static class ScmContextListenerModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(new FactoryModuleBuilder().build(ScmContextListener.Factory.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
86
scm-webapp/src/main/java/sonia/scm/boot/BootstrapModule.java
Normal file
86
scm-webapp/src/main/java/sonia/scm/boot/BootstrapModule.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.throwingproviders.ThrowingProviderBinder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.ClassOverrides;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.io.DefaultFileSystem;
|
||||
import sonia.scm.io.FileSystem;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.repository.RepositoryLocationResolver;
|
||||
import sonia.scm.repository.xml.PathBasedRepositoryLocationResolver;
|
||||
import sonia.scm.security.CipherHandler;
|
||||
import sonia.scm.security.CipherUtil;
|
||||
import sonia.scm.security.DefaultKeyGenerator;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.store.BlobStoreFactory;
|
||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import sonia.scm.store.DataStoreFactory;
|
||||
import sonia.scm.store.FileBlobStoreFactory;
|
||||
import sonia.scm.store.JAXBConfigurationEntryStoreFactory;
|
||||
import sonia.scm.store.JAXBConfigurationStoreFactory;
|
||||
import sonia.scm.store.JAXBDataStoreFactory;
|
||||
|
||||
public class BootstrapModule extends AbstractModule {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BootstrapModule.class);
|
||||
|
||||
private final ClassOverrides overrides;
|
||||
private final PluginLoader pluginLoader;
|
||||
|
||||
BootstrapModule(PluginLoader pluginLoader) {
|
||||
this.overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader());
|
||||
this.pluginLoader = pluginLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(ThrowingProviderBinder.forModule(this));
|
||||
|
||||
SCMContextProvider context = SCMContext.getContext();
|
||||
|
||||
bind(SCMContextProvider.class).toInstance(context);
|
||||
|
||||
bind(KeyGenerator.class).to(DefaultKeyGenerator.class);
|
||||
|
||||
bind(RepositoryLocationResolver.class).to(PathBasedRepositoryLocationResolver.class);
|
||||
|
||||
bind(FileSystem.class, DefaultFileSystem.class);
|
||||
|
||||
// note CipherUtil uses an other generator
|
||||
bind(CipherHandler.class).toInstance(CipherUtil.getInstance().getCipherHandler());
|
||||
|
||||
// bind core
|
||||
bind(ConfigurationStoreFactory.class, JAXBConfigurationStoreFactory.class);
|
||||
bind(ConfigurationEntryStoreFactory.class, JAXBConfigurationEntryStoreFactory.class);
|
||||
bind(DataStoreFactory.class, JAXBDataStoreFactory.class);
|
||||
bind(BlobStoreFactory.class, FileBlobStoreFactory.class);
|
||||
bind(PluginLoader.class).toInstance(pluginLoader);
|
||||
}
|
||||
|
||||
private <T> void bind(Class<T> clazz, Class<? extends T> defaultImplementation) {
|
||||
Class<? extends T> implementation = find(clazz, defaultImplementation);
|
||||
LOG.debug("bind {} to {}", clazz, implementation);
|
||||
bind(clazz).to(implementation);
|
||||
}
|
||||
|
||||
private <T> Class<? extends T> find(Class<T> clazz, Class<? extends T> defaultImplementation) {
|
||||
Class<? extends T> implementation = overrides.getOverride(clazz);
|
||||
|
||||
if (implementation != null) {
|
||||
LOG.info("found override {} for {}", implementation, clazz);
|
||||
} else {
|
||||
implementation = defaultImplementation;
|
||||
|
||||
LOG.trace(
|
||||
"no override available for {}, using default implementation {}",
|
||||
clazz, implementation);
|
||||
}
|
||||
|
||||
return implementation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import sonia.scm.migration.UpdateStep;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
|
||||
class UpdateStepModule extends AbstractModule {
|
||||
|
||||
private final PluginLoader pluginLoader;
|
||||
|
||||
UpdateStepModule(PluginLoader pluginLoader) {
|
||||
this.pluginLoader = pluginLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
Multibinder<UpdateStep> updateStepBinder = Multibinder.newSetBinder(binder(), UpdateStep.class);
|
||||
pluginLoader
|
||||
.getExtensionProcessor()
|
||||
.byExtensionPoint(UpdateStep.class)
|
||||
.forEach(stepClass -> updateStepBinder.addBinding().to(stepClass));
|
||||
}
|
||||
}
|
||||
86
scm-webapp/src/main/java/sonia/scm/update/UpdateEngine.java
Normal file
86
scm-webapp/src/main/java/sonia/scm/update/UpdateEngine.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package sonia.scm.update;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.migration.UpdateException;
|
||||
import sonia.scm.migration.UpdateStep;
|
||||
import sonia.scm.store.ConfigurationEntryStore;
|
||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class UpdateEngine {
|
||||
|
||||
public static final Logger LOG = LoggerFactory.getLogger(UpdateEngine.class);
|
||||
|
||||
private static final String STORE_NAME = "executedUpdates";
|
||||
|
||||
private final List<UpdateStep> steps;
|
||||
private final ConfigurationEntryStore<UpdateVersionInfo> store;
|
||||
|
||||
@Inject
|
||||
public UpdateEngine(Set<UpdateStep> steps, ConfigurationEntryStoreFactory storeFactory) {
|
||||
this.steps = sortSteps(steps);
|
||||
this.store = storeFactory.withType(UpdateVersionInfo.class).withName(STORE_NAME).build();
|
||||
}
|
||||
|
||||
private List<UpdateStep> sortSteps(Set<UpdateStep> steps) {
|
||||
LOG.trace("sorting available update steps:");
|
||||
List<UpdateStep> sortedSteps = steps.stream()
|
||||
.sorted(Comparator.comparing(UpdateStep::getTargetVersion).reversed())
|
||||
.collect(toList());
|
||||
sortedSteps.forEach(step -> LOG.trace("{} for version {}", step.getAffectedDataType(), step.getTargetVersion()));
|
||||
return sortedSteps;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
steps
|
||||
.stream()
|
||||
.filter(this::notRunYet)
|
||||
.forEach(this::execute);
|
||||
}
|
||||
|
||||
private void execute(UpdateStep updateStep) {
|
||||
try {
|
||||
LOG.info("running update step for type {} and version {}",
|
||||
updateStep.getAffectedDataType(),
|
||||
updateStep.getTargetVersion()
|
||||
);
|
||||
updateStep.doUpdate();
|
||||
} catch (Exception e) {
|
||||
throw new UpdateException(
|
||||
String.format(
|
||||
"could not execute update for type %s to version %s in class %s",
|
||||
updateStep.getAffectedDataType(),
|
||||
updateStep.getTargetVersion(),
|
||||
updateStep.getClass()),
|
||||
e);
|
||||
}
|
||||
UpdateVersionInfo newVersionInfo = new UpdateVersionInfo(updateStep.getTargetVersion().getParsedVersion());
|
||||
store.put(updateStep.getAffectedDataType(), newVersionInfo);
|
||||
}
|
||||
|
||||
private boolean notRunYet(UpdateStep updateStep) {
|
||||
LOG.trace("checking whether to run update step for type {} and version {}",
|
||||
updateStep.getAffectedDataType(),
|
||||
updateStep.getTargetVersion()
|
||||
);
|
||||
UpdateVersionInfo updateVersionInfo = store.get(updateStep.getAffectedDataType());
|
||||
if (updateVersionInfo == null) {
|
||||
LOG.trace("no updates for type {} run yet; step will be executed", updateStep.getAffectedDataType());
|
||||
return true;
|
||||
}
|
||||
boolean result = updateStep.getTargetVersion().isNewer(updateVersionInfo.getLatestVersion());
|
||||
LOG.trace("latest version for type {}: {}; step will be executed: {}",
|
||||
updateStep.getAffectedDataType(),
|
||||
updateVersionInfo.getLatestVersion(),
|
||||
result
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package sonia.scm.update;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement(name = "latest-version")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class UpdateVersionInfo {
|
||||
private String latestVersion;
|
||||
|
||||
public UpdateVersionInfo() {
|
||||
}
|
||||
|
||||
public UpdateVersionInfo(String latestVersion) {
|
||||
this.latestVersion = latestVersion;
|
||||
}
|
||||
|
||||
public String getLatestVersion() {
|
||||
return latestVersion;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user