diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java index f48ecb658a..d6823b78c1 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java @@ -35,11 +35,15 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- +import com.google.inject.Binder; +import com.google.inject.Module; + import sonia.scm.plugin.ext.ExtensionProcessor; //~--- JDK imports ------------------------------------------------------------ import java.util.Collection; +import java.util.Set; /** * @@ -52,12 +56,28 @@ public interface PluginLoader * Method description * * - * @param processor + * @param binder */ - public void processExtensions(ExtensionProcessor processor); + public void processExtensions(Binder binder); //~--- get methods ---------------------------------------------------------- + /** + * Method description + * + * + * @return + */ + public Collection getInjectionModules(); + + /** + * Method description + * + * + * @return + */ + public Collection getInstalledModules(); + /** * Method description * diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 29a836a0eb..8ed865810d 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -17,6 +17,15 @@ + + + + sonia.scm.maven + scm-annotation-processor + 2.0.0-SNAPSHOT + provided + + javax.servlet servlet-api diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java index 3845c985f7..4729980992 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java @@ -181,7 +181,7 @@ public class ScmContextListener extends GuiceServletContextListener */ private Injector getDefaultInjector(ServletContext servletContext) { - DefaultPluginLoader pluginLoader = new DefaultPluginLoader(servletContext); + DefaultPluginLoader pluginLoader = new DefaultPluginLoader(); ClassOverrides overrides = ClassOverrides.findOverrides(); ScmServletModule main = new ScmServletModule(pluginLoader, overrides); @@ -193,7 +193,7 @@ public class ScmContextListener extends GuiceServletContextListener moduleList.add(ShiroWebModule.guiceFilterModule()); moduleList.add(main); moduleList.add(new ScmSecurityModule(servletContext)); - moduleList.addAll(pluginLoader.getModuleSet()); + moduleList.addAll(pluginLoader.getInjectionModules()); moduleList.addAll(overrides.getModules()); return Guice.createInjector(moduleList); diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index a1ef18a2ff..fc692f0c7c 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -39,10 +39,10 @@ import com.google.inject.Provider; import com.google.inject.multibindings.Multibinder; import com.google.inject.name.Names; import com.google.inject.servlet.RequestScoped; -import com.google.inject.servlet.ServletModule; import com.google.inject.throwingproviders.ThrowingProviderBinder; import org.apache.shiro.authz.permission.PermissionResolver; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,11 +66,11 @@ import sonia.scm.net.HttpClient; import sonia.scm.net.URLHttpClient; import sonia.scm.plugin.DefaultPluginLoader; import sonia.scm.plugin.DefaultPluginManager; -import sonia.scm.plugin.Plugin; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.repository.DefaultRepositoryManager; import sonia.scm.repository.DefaultRepositoryProvider; +import sonia.scm.repository.HealthCheckContextListener; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryDAO; import sonia.scm.repository.RepositoryManager; @@ -132,27 +132,21 @@ import sonia.scm.web.security.DefaultAdministrationContext; //~--- JDK imports ------------------------------------------------------------ - import com.sun.jersey.api.core.PackagesResourceConfig; import com.sun.jersey.api.core.ResourceConfig; import com.sun.jersey.api.json.JSONConfiguration; +import com.sun.jersey.guice.JerseyServletModule; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; import com.sun.jersey.spi.container.servlet.ServletContainer; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.Map; -import java.util.Set; -import sonia.scm.repository.HealthCheckContextListener; -import sonia.scm.repository.HealthChecker; /** * * @author Sebastian Sdorra */ -public class ScmServletModule extends ServletModule +public class ScmServletModule extends JerseyServletModule { /** Field description */ @@ -267,7 +261,7 @@ public class ScmServletModule extends ServletModule bind(CipherHandler.class).toInstance(cu.getCipherHandler()); bind(EncryptionHandler.class, MessageDigestEncryptionHandler.class); bind(FileSystem.class, DefaultFileSystem.class); - + // bind health check stuff bind(HealthCheckContextListener.class); @@ -376,67 +370,18 @@ public class ScmServletModule extends ServletModule */ params.put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE.toString()); params.put(ResourceConfig.FEATURE_REDIRECT, Boolean.TRUE.toString()); + + /* + * TODO remove UriExtensionsConfig and PackagesResourceConfig + * to stop jersey classpath scanning + */ + params.put(ServletContainer.RESOURCE_CONFIG_CLASS, UriExtensionsConfig.class.getName()); - - String restPath = getRestPackages(); - - if (logger.isInfoEnabled()) - { - logger.info("configure jersey with package path: {}", restPath); - } - - params.put(PackagesResourceConfig.PROPERTY_PACKAGES, restPath); + params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "unbound"); serve(PATTERN_RESTAPI).with(GuiceContainer.class, params); } - /** - * Method description - * - * - * @param packageSet - * @param plugin - */ - private void appendPluginPackages(Set packageSet, Plugin plugin) - { - Set pluginPackageSet = plugin.getPackageSet(); - - if (pluginPackageSet != null) - { - for (String pluginPkg : pluginPackageSet) - { - boolean append = true; - - for (String pkg : packageSet) - { - if (pluginPkg.startsWith(pkg)) - { - append = false; - - break; - } - } - - if (append) - { - if (logger.isDebugEnabled()) - { - String name = "unknown"; - - if (plugin.getInformation() != null) - { - name = plugin.getInformation().getName(); - } - - logger.debug("plugin {} added rest path {}", name, pluginPkg); - } - - packageSet.add(pluginPkg); - } - } - } - } - /** * Method description * @@ -524,44 +469,6 @@ public class ScmServletModule extends ServletModule //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @return - */ - private String getRestPackages() - { - Set packageSet = new HashSet(); - - packageSet.add(SCMContext.DEFAULT_PACKAGE); - - Collection plugins = pluginLoader.getInstalledPlugins(); - - if (plugins != null) - { - for (Plugin plugin : plugins) - { - appendPluginPackages(packageSet, plugin); - } - } - - StringBuilder buffer = new StringBuilder(); - Iterator pkgIterator = packageSet.iterator(); - - while (pkgIterator.hasNext()) - { - buffer.append(pkgIterator.next()); - - if (pkgIterator.hasNext()) - { - buffer.append(";"); - } - } - - return buffer.toString(); - } - /** * Load ScmConfiguration with JAXB * diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/DisabledAccountExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/DisabledAccountExceptionMapper.java index 4df332acfe..b0b73a5353 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/DisabledAccountExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/DisabledAccountExceptionMapper.java @@ -38,11 +38,13 @@ import org.apache.shiro.authc.DisabledAccountException; //~--- JDK imports ------------------------------------------------------------ import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; /** * * @author Sebastian Sdorra */ +@Provider public class DisabledAccountExceptionMapper extends StatusExceptionMapper { diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/ExcessiveAttemptsExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/ExcessiveAttemptsExceptionMapper.java index 42e3e2a766..83a19ace08 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/ExcessiveAttemptsExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/ExcessiveAttemptsExceptionMapper.java @@ -38,11 +38,13 @@ import org.apache.shiro.authc.ExcessiveAttemptsException; //~--- JDK imports ------------------------------------------------------------ import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; /** * * @author Sebastian Sdorra */ +@Provider public class ExcessiveAttemptsExceptionMapper extends StatusExceptionMapper { diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java index 03586d9664..1a36bf0387 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java @@ -35,7 +35,10 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.inject.Binder; import com.google.inject.Module; @@ -43,43 +46,21 @@ import com.google.inject.Module; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.SCMContext; -import sonia.scm.plugin.ext.AnnotatedClass; -import sonia.scm.plugin.ext.AnnotationCollector; -import sonia.scm.plugin.ext.AnnotationProcessor; -import sonia.scm.plugin.ext.AnnotationScanner; -import sonia.scm.plugin.ext.AnnotationScannerFactory; -import sonia.scm.plugin.ext.DefaultAnnotationScannerFactory; -import sonia.scm.plugin.ext.Extension; import sonia.scm.plugin.ext.ExtensionBinder; -import sonia.scm.plugin.ext.ExtensionProcessor; -import sonia.scm.plugin.ext.Extensions; import sonia.scm.util.ClassLoaders; -import sonia.scm.util.IOUtil; -import sonia.scm.web.security.DefaultAuthenticationHandler; //~--- JDK imports ------------------------------------------------------------ -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; - -import java.net.MalformedURLException; import java.net.URL; -import java.net.URLDecoder; import java.util.Collection; import java.util.Enumeration; -import java.util.HashSet; import java.util.Set; -import javax.servlet.ServletContext; -import javax.servlet.ServletContextListener; - -import javax.xml.bind.JAXB; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; /** * @@ -89,24 +70,11 @@ public class DefaultPluginLoader implements PluginLoader { /** Field description */ - public static final String ENCODING = "UTF-8"; - - /** Field description */ - public static final String EXTENSION_JAR = ".jar"; + public static final String PATH_MODULECONFIG = "META-INF/scm/module.xml"; /** Field description */ public static final String PATH_PLUGINCONFIG = "META-INF/scm/plugin.xml"; - /** Field description */ - public static final String PATH_WEBINFLIB = "/WEB-INF/lib"; - - /** Field description */ - public static final String PATH_SCMCORE = PATH_WEBINFLIB.concat("/scm-core"); - - /** Field description */ - public static final String REGE_COREPLUGIN = - "^.*(?:/|\\\\)WEB-INF(?:/|\\\\)lib(?:/|\\\\).*\\.jar$"; - /** the logger for DefaultPluginLoader */ private static final Logger logger = LoggerFactory.getLogger(DefaultPluginLoader.class); @@ -116,90 +84,54 @@ public class DefaultPluginLoader implements PluginLoader /** * Constructs ... * - * - * @param servletContext */ - public DefaultPluginLoader(ServletContext servletContext) + public DefaultPluginLoader() { - this.servletContext = servletContext; - this.annotationScannerFactory = new DefaultAnnotationScannerFactory(); - ClassLoader classLoader = ClassLoaders.getContextClassLoader(DefaultPluginLoader.class); try { - locateCoreFile(); - loadPlugins(classLoader); - scanForAnnotations(); - findModules(); + JAXBContext context = JAXBContext.newInstance(ScmModule.class, + Plugin.class); + + modules = getInstalled(classLoader, context, PATH_MODULECONFIG); + plugins = getInstalled(classLoader, context, PATH_PLUGINCONFIG); + + appendExtensions(multiple, single, extensions, modules); + appendExtensions(multiple, single, extensions, plugins); } - catch (IOException ex) + catch (Exception ex) { - throw new RuntimeException(ex); + throw Throwables.propagate(ex); } } //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @param classLoader - * @param packages - * @param extensionPointProcessor - * @param extensionProcessor - * @param - * - * @return - */ - public AnnotationScanner createAnnotationScanner( - ClassLoader classLoader, Collection packages, - AnnotationProcessor extensionPointProcessor, - AnnotationProcessor extensionProcessor) - { - AnnotationScanner scanner = annotationScannerFactory.create(classLoader, - packages); - - if (extensionPointProcessor != null) - { - scanner.addProcessor(ExtensionPoint.class, extensionPointProcessor); - } - - if (extensionProcessor != null) - { - scanner.addProcessor(Extension.class, extensionProcessor); - } - - return scanner; - } - /** * Method description * * * @param binder */ + @Override public void processExtensions(Binder binder) { - new ExtensionBinder(binder).bind(bounds, extensionPoints, extensions); - } + logger.info("start processing extensions"); - /** - * Method description - * - * - * @param processor - */ - @Override - public void processExtensions(ExtensionProcessor processor) - { - for (AnnotatedClass extension : extensions) + appendExtensions(multiple, single, extensions, modules); + appendExtensions(multiple, single, extensions, plugins); + + if (logger.isInfoEnabled()) { - processor.processExtension(extension.getAnnotation(), - extension.getAnnotatedClass()); + logger.info( + "found {} extensions for {} multiple and {} single extension points", + extensions.size(), multiple.size(), single.size()); } + + // TODO bind jax-rs providers always as singleton + new ExtensionBinder(binder).bind(multiple, single, extensions); } //~--- get methods ---------------------------------------------------------- @@ -211,9 +143,9 @@ public class DefaultPluginLoader implements PluginLoader * @return */ @Override - public Collection getInstalledPlugins() + public Set getInjectionModules() { - return installedPlugins; + return ImmutableSet.copyOf(injectionModules); } /** @@ -222,9 +154,22 @@ public class DefaultPluginLoader implements PluginLoader * * @return */ - public Set getModuleSet() + @Override + public Collection getInstalledModules() { - return moduleSet; + return modules; + } + + /** + * Method description + * + * + * @return + */ + @Override + public Collection getInstalledPlugins() + { + return plugins; } //~--- methods -------------------------------------------------------------- @@ -233,409 +178,103 @@ public class DefaultPluginLoader implements PluginLoader * Method description * * - * @param moduleClass + * @param multiple + * @param single + * @param extensions + * @param mods */ - private void addModule(Class moduleClass) + private void appendExtensions(Set multiple, Set single, + Set extensions, Iterable mods) { - try + for (ScmModule mod : mods) { - logger.info("add module {}", moduleClass); - moduleSet.add((Module) moduleClass.newInstance()); - } - catch (Exception ex) - { - logger.error( - "could not create module instance of ".concat(moduleClass.getName()), - ex); - } - - } - - /** - * Method description - * - * - * @param path - * - * @return - */ - private String decodePath(String path) - { - File file = new File(path); - - if (!file.exists()) - { - try + for (ExtensionPointElement epe : mod.getExtensionPoints()) { - path = URLDecoder.decode(path, ENCODING); - } - catch (IOException ex) - { - logger.error("could not decode path ".concat(path), ex); - } - } - - return path; - } - - /** - * Method description - * - * - * @param url - * - * @return - */ - private String extractResourcePath(URL url) - { - String path = url.toExternalForm(); - - if (path.startsWith("file:")) - { - path = path.substring("file:".length(), - path.length() - "/META-INF/scm/plugin.xml".length()); - } - else - { - - // jar:file:/some/path/file.jar!/META-INF/scm/plugin.xml - path = path.substring("jar:file:".length(), path.lastIndexOf('!')); - path = decodePath(path); - } - - logger.trace("extrace resource path {} from url {}", path, url); - - return path; - } - - /** - * Method description - * - */ - private void findModules() - { - for (AnnotatedClass extension : extensions) - { - Class extensionClass = extension.getAnnotatedClass(); - - if (Module.class.isAssignableFrom(extensionClass)) - { - bounds.add(extension); - addModule(extensionClass); - } - } - } - - /** - * Method description - * - * - * @param url - */ - private void loadPlugin(URL url) - { - String path = extractResourcePath(url); - - if (logger.isTraceEnabled()) - { - logger.trace("try to load plugin from {}", path); - } - - try - { - boolean corePlugin = path.matches(REGE_COREPLUGIN); - - if (logger.isInfoEnabled()) - { - logger.info("load {}plugin {}", corePlugin - ? "core " - : " ", path); - } - - Plugin plugin = JAXB.unmarshal(url, Plugin.class); - PluginInformation info = plugin.getInformation(); - PluginCondition condition = plugin.getCondition(); - - if (condition != null) - { - info.setCondition(condition); - } - - if (info != null) - { - info.setState(corePlugin - ? PluginState.CORE - : PluginState.INSTALLED); - } - - plugin.setPath(path); - - if (logger.isDebugEnabled()) - { - logger.debug("add plugin {} to installed plugins", info.getId()); - } - - installedPlugins.add(plugin); - } - catch (Exception ex) - { - logger.error("could not load plugin ".concat(path), ex); - } - } - - /** - * Method description - * - * - * @param classLoader - * - * @throws IOException - */ - private void loadPlugins(ClassLoader classLoader) throws IOException - { - Enumeration urlEnum = classLoader.getResources(PATH_PLUGINCONFIG); - - if (urlEnum != null) - { - while (urlEnum.hasMoreElements()) - { - URL url = urlEnum.nextElement(); - - loadPlugin(url); - } - - if (logger.isDebugEnabled()) - { - logger.debug("loaded {} plugins", installedPlugins.size()); - } - } - else if (logger.isWarnEnabled()) - { - logger.warn("no plugin descriptor found"); - } - } - - /** - * Method description - * - * - * @param classLoader - * - * @throws MalformedURLException - */ - @SuppressWarnings("unchecked") - private void locateCoreFile() throws MalformedURLException - { - Set paths = servletContext.getResourcePaths(PATH_WEBINFLIB); - - for (String path : paths) - { - if (path.startsWith(PATH_SCMCORE) && path.endsWith(EXTENSION_JAR)) - { - coreFile = servletContext.getResource(path); - - break; - } - } - - if (coreFile == null) - { - throw new IllegalStateException("could not find scm-core file"); - } - } - - /** - * Method description - * - * - * @param classLoader - * @param packageSet - * @param extensionPointCollector - * @param extensionCollector - * @param file - * - * @throws IOException - */ - private void scanFile(ClassLoader classLoader, Collection packageSet, - AnnotationCollector extensionPointCollector, - AnnotationCollector extensionCollector, File file) - throws IOException - { - if (logger.isTraceEnabled()) - { - String type = file.isDirectory() - ? "directory" - : "jar"; - - logger.trace("search extensions in packages {} of {} file {}", - new Object[] { packageSet, - type, file }); - } - - if (file.isDirectory()) - { - createAnnotationScanner(classLoader, packageSet, extensionPointCollector, - extensionCollector).scanDirectory(file); - } - else - { - InputStream input = null; - - try - { - input = new FileInputStream(file); - createAnnotationScanner(classLoader, packageSet, - extensionPointCollector, extensionCollector).scanArchive(input); - } - finally - { - IOUtil.close(input); - } - } - } - - /** - * Method description - * - * - * @param processor - * - * @param binder - */ - private void scanForAnnotations() - { - ClassLoader classLoader = - ClassLoaders.getContextClassLoader(DefaultPluginLoader.class); - - AnnotationCollector extensionPointCollector = - new AnnotationCollector(); - AnnotationCollector extensionCollector = - new AnnotationCollector(); - - logger.debug("search extension points in {}", coreFile); - - Set corePackages = ImmutableSet.of("sonia.scm"); - - try - { - scanURL(classLoader, corePackages, extensionPointCollector, null, - coreFile); - } - catch (Exception ex) - { - throw new IllegalStateException("could not process scm-core", ex); - } - - for (Plugin plugin : installedPlugins) - { - if (logger.isDebugEnabled()) - { - logger.debug("search extensions from plugin {}", - plugin.getInformation().getId()); - } - - try - { - Set packageSet = plugin.getPackageSet(); - - if (packageSet == null) + if (epe.isMultiple()) { - packageSet = new HashSet(); - } - - packageSet.add(SCMContext.DEFAULT_PACKAGE); - - File pluginFile = new File(plugin.getPath()); - - if (pluginFile.exists()) - { - scanFile(classLoader, packageSet, extensionPointCollector, - extensionCollector, pluginFile); + multiple.add(epe.getClazz()); } else { - logger.error("could not find plugin file {}", plugin.getPath()); + single.add(epe.getClazz()); } } - catch (IOException ex) + + for (Class extensionClass : mod.getExtensions()) { - logger.error("error during extension processing", ex); + if (Module.class.isAssignableFrom(extensionClass)) + { + try + { + injectionModules.add((Module) extensionClass.newInstance()); + } + catch (Exception ex) + { + logger.error("could not create instance of module", ex); + } + } + else + { + extensions.add(extensionClass); + } } + + Iterables.addAll(extensions, mod.getJaxrsProviders()); + Iterables.addAll(extensions, mod.getJaxrsResources()); } - - //J- - extensionPoints = extensionPointCollector.getAnnotatedClasses(); - extensionPoints.add( - new AnnotatedClass( - Extensions.createExtensionPoint(true), - ServletContextListener.class - ) - ); - - extensions = extensionCollector.getAnnotatedClasses(); - extensions.add( - new AnnotatedClass( - Extensions.createExtension(), - DefaultAuthenticationHandler.class - ) - ); - //J+ } + //~--- get methods ---------------------------------------------------------- + /** * Method description * * * @param classLoader - * @param packageSet - * @param extensionPointCollector - * @param extensionCollector - * @param file + * @param context + * @param path + * @param + * + * @return * * @throws IOException + * @throws JAXBException */ - private void scanURL(ClassLoader classLoader, Collection packageSet, - AnnotationCollector extensionPointCollector, - AnnotationCollector extensionCollector, URL file) - throws IOException + private Set getInstalled(ClassLoader classLoader, JAXBContext context, + String path) + throws IOException, JAXBException { - InputStream content = null; + Builder builder = ImmutableSet.builder(); + Enumeration urls = classLoader.getResources(path); - try + while (urls.hasMoreElements()) { - content = file.openStream(); - createAnnotationScanner(classLoader, packageSet, extensionPointCollector, - extensionCollector).scanArchive(content); - } - finally - { - IOUtil.close(content); + URL url = urls.nextElement(); + T module = (T) context.createUnmarshaller().unmarshal(url); + + builder.add(module); } + + return builder.build(); } //~--- fields --------------------------------------------------------------- /** Field description */ - private final AnnotationScannerFactory annotationScannerFactory; + private Set modules; /** Field description */ - private final Set> bounds = Sets.newHashSet(); + private Set multiple = Sets.newHashSet(); /** Field description */ - private final Set moduleSet = Sets.newHashSet(); + private Set plugins; /** Field description */ - private final Set installedPlugins = new HashSet(); + private Set single = Sets.newHashSet(); /** Field description */ - private final ServletContext servletContext; + private Set injectionModules = Sets.newHashSet(); /** Field description */ - private URL coreFile; - - /** Field description */ - private Set> extensionPoints; - - /** Field description */ - private Set> extensions; + private Set extensions = Sets.newHashSet(); } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index d317155a2a..9191abe167 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -211,7 +211,8 @@ public class DefaultPluginManager implements PluginManager // ugly workaround Plugin newPlugin = new Plugin(); - newPlugin.setInformation(plugin); + // TODO check + // newPlugin.setInformation(plugin); installedPlugins.put(id, newPlugin); } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/ext/ExtensionBinder.java b/scm-webapp/src/main/java/sonia/scm/plugin/ext/ExtensionBinder.java index 57b3a69457..29777832c3 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/ext/ExtensionBinder.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/ext/ExtensionBinder.java @@ -38,6 +38,8 @@ package sonia.scm.plugin.ext; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.inject.Binder; +import com.google.inject.Scopes; +import com.google.inject.binder.AnnotatedBindingBuilder; import com.google.inject.binder.ScopedBindingBuilder; import com.google.inject.multibindings.Multibinder; @@ -45,18 +47,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.EagerSingleton; -import sonia.scm.plugin.ExtensionPoint; import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ import java.util.Set; +import javax.ws.rs.ext.Provider; + /** * * @author Sebastian Sdorra */ -public class ExtensionBinder +public final class ExtensionBinder { /** @@ -84,54 +87,58 @@ public class ExtensionBinder * Method description * * - * - * @param bounds - * @param extensionPoints + * @param mutipleExtensionPoints + * @param singleExtensionPoints * @param extensions */ - public void bind(Set> bounds, - Set> extensionPoints, - Set> extensions) + public void bind(Set mutipleExtensionPoints, + Set singleExtensionPoints, Set extensions) { if (logger.isInfoEnabled()) { logger.info("bind {} extensions to {} extension points", - extensions.size(), extensionPoints.size()); + extensions.size(), + mutipleExtensionPoints.size() + singleExtensionPoints.size()); } - for (AnnotatedClass extensionPoint : extensionPoints) - { - ExtensionPoint extensionPointAnnotation = extensionPoint.getAnnotation(); - Class extensionPointClass = extensionPoint.getAnnotatedClass(); + Set boundClasses = Sets.newHashSet(); - if (extensionPointAnnotation.multi()) + for (Class extensionPointClass : mutipleExtensionPoints) + { + bindMultiExtensionPoint(boundClasses, extensionPointClass, extensions); + } + + for (Class extensionPointClass : singleExtensionPoints) + { + bindExtensionPoint(boundClasses, extensionPointClass, extensions); + } + + Set extensionsCopy = Sets.newHashSet(extensions); + + Iterables.removeAll(extensionsCopy, boundClasses); + + for (Class extension : extensionsCopy) + { + AnnotatedBindingBuilder abb = binder.bind(extension); + + if (isProvider(extension)) { - bindMultiExtensionPoint(bounds, extensionPointClass, extensions); + logger.info("bind provider {} as singleton", extension); + abb.in(Scopes.SINGLETON); + } + else if (isEagerSingleton(extension)) + { + + logger.info("bind {} as eager singleton, without extensionpoint", + extension); + abb.asEagerSingleton(); } else { - bindExtensionPoint(bounds, extensionPointClass, extensions); + logger.info("bind {}, without extensionpoint", extension); + binder.bind(extension); } } - - Set> extensionsCopy = Sets.newHashSet(extensions); - - Iterables.removeAll(extensionsCopy, bounds); - - for (AnnotatedClass extension : extensionsCopy) - { - boolean eagerSingleton = isEagerSingleton(extension.getAnnotatedClass()); - String as = Util.EMPTY_STRING; - - if (eagerSingleton) - { - as = " as eager singleton"; - } - - logger.info("bind {}{}, without extensionpoint", - extension.getAnnotatedClass(), as); - binder.bind(extension.getAnnotatedClass()); - } } /** @@ -140,23 +147,31 @@ public class ExtensionBinder * * * @param found + * + * @param boundClasses * @param extensionPointClass * @param extensions */ @SuppressWarnings("unchecked") - private void bindExtensionPoint(Set> found, - Class extensionPointClass, Set> extensions) + private void bindExtensionPoint(Set boundClasses, + Class extensionPointClass, Set extensions) { - for (AnnotatedClass extension : extensions) - { - Class extensionClass = extension.getAnnotatedClass(); + boolean bound = false; + for (Class extensionClass : extensions) + { if (extensionPointClass.isAssignableFrom(extensionClass)) { - found.add(extension); - bindSingleInstance(extensionPointClass, extensionClass); + if (bound) + { + throw new IllegalStateException( + "extension point ".concat(extensionPointClass.getName()).concat( + " is not multiple and is already bound to another class")); + } - break; + bindSingleInstance(extensionPointClass, extensionClass); + boundClasses.add(extensionClass); + bound = true; } } } @@ -167,12 +182,14 @@ public class ExtensionBinder * * * @param found + * + * @param boundClasses * @param extensionPointClass * @param extensions */ @SuppressWarnings("unchecked") - private void bindMultiExtensionPoint(Set> found, - Class extensionPointClass, Set> extensions) + private void bindMultiExtensionPoint(Set boundClasses, + Class extensionPointClass, Iterable extensions) { if (logger.isInfoEnabled()) { @@ -182,10 +199,8 @@ public class ExtensionBinder Multibinder multibinder = Multibinder.newSetBinder(binder, extensionPointClass); - for (AnnotatedClass extension : extensions) + for (Class extensionClass : extensions) { - Class extensionClass = extension.getAnnotatedClass(); - if (extensionPointClass.isAssignableFrom(extensionClass)) { boolean eagerSingleton = isEagerSingleton(extensionClass); @@ -203,8 +218,6 @@ public class ExtensionBinder extensionClass.getName(), extensionPointClass.getName(), as); } - found.add(extension); - ScopedBindingBuilder sbb = multibinder.addBinding().to(extensionClass); if (eagerSingleton) @@ -212,6 +225,8 @@ public class ExtensionBinder sbb.asEagerSingleton(); logger.info("bind {} as eager singleton"); } + + boundClasses.add(extensionClass); } } } @@ -261,13 +276,26 @@ public class ExtensionBinder * * @return */ - private boolean isEagerSingleton(Class extensionClass) + private boolean isEagerSingleton(Class extensionClass) { return extensionClass.isAnnotationPresent(EagerSingleton.class); } + /** + * Method description + * + * + * @param extensionClass + * + * @return + */ + private boolean isProvider(Class extensionClass) + { + return extensionClass.isAnnotationPresent(Provider.class); + } + //~--- fields --------------------------------------------------------------- /** Field description */ - private Binder binder; + private final Binder binder; } diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAuthenticationHandler.java b/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAuthenticationHandler.java index 3e96c4a77e..66293ebcde 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAuthenticationHandler.java +++ b/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAuthenticationHandler.java @@ -52,11 +52,13 @@ import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import sonia.scm.plugin.ext.Extension; /** * * @author Sebastian Sdorra */ +@Extension @Singleton public class DefaultAuthenticationHandler implements AuthenticationHandler {