Sebastian Sdorra avatar Sebastian Sdorra committed 22571c9

find and bind extension points automatically

Comments (0)

Files changed (11)

scm-core/src/main/java/sonia/scm/io/FileSystem.java

  *
  * @author Sebastian Sdorra
  */
-@ExtensionPoint
+@ExtensionPoint(multi=false)
 public interface FileSystem
 {
 

scm-core/src/main/java/sonia/scm/plugin/ExtensionPoint.java

  */
 @Documented
 @Target({ ElementType.TYPE })
-@Retention(RetentionPolicy.CLASS)
-public @interface ExtensionPoint {}
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExtensionPoint
+{
+  boolean multi() default true;
+}

scm-core/src/main/java/sonia/scm/plugin/ext/AnnotatedClass.java

  */
 
 
+
 package sonia.scm.plugin.ext;
 
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Objects;
+
 //~--- JDK imports ------------------------------------------------------------
 
 import java.lang.annotation.Annotation;
     this.annotatedClass = annotatedClass;
   }
 
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * {@inheritDoc}
+   *
+   *
+   * @param obj
+   *
+   * @return
+   */
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (obj == null)
+    {
+      return false;
+    }
+
+    if (getClass() != obj.getClass())
+    {
+      return false;
+    }
+
+    final AnnotatedClass<T> other = (AnnotatedClass<T>) obj;
+
+    return Objects.equal(annotation, other.annotation)
+      && Objects.equal(annotatedClass, other.annotatedClass);
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   *
+   * @return
+   */
+  @Override
+  public int hashCode()
+  {
+    return Objects.hashCode(annotation, annotatedClass);
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   *
+   * @return
+   */
+  @Override
+  public String toString()
+  {
+    //J-
+    return Objects.toStringHelper(this)
+                  .add("annotation", annotation)
+                  .add("annotatedClass", annotatedClass)
+                  .toString();
+    //J+
+  }
+
   //~--- get methods ----------------------------------------------------------
 
   /**

scm-webapp/src/main/java/sonia/scm/BindingExtensionProcessor.java

-/**
- * 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;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import com.google.common.collect.Sets;
-import com.google.inject.Binder;
-import com.google.inject.Module;
-import com.google.inject.multibindings.Multibinder;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import sonia.scm.group.GroupListener;
-import sonia.scm.io.FileSystem;
-import sonia.scm.plugin.ext.Extension;
-import sonia.scm.plugin.ext.ExtensionProcessor;
-import sonia.scm.repository.BlameLinePreProcessor;
-import sonia.scm.repository.BlameLinePreProcessorFactory;
-import sonia.scm.repository.ChangesetPreProcessor;
-import sonia.scm.repository.ChangesetPreProcessorFactory;
-import sonia.scm.repository.FileObjectPreProcessor;
-import sonia.scm.repository.FileObjectPreProcessorFactory;
-import sonia.scm.repository.RepositoryHandler;
-import sonia.scm.repository.RepositoryHook;
-import sonia.scm.repository.RepositoryListener;
-import sonia.scm.repository.RepositoryRequestListener;
-import sonia.scm.repository.spi.RepositoryServiceResolver;
-import sonia.scm.resources.ResourceHandler;
-import sonia.scm.security.EncryptionHandler;
-import sonia.scm.user.UserListener;
-import sonia.scm.web.security.AuthenticationHandler;
-import sonia.scm.web.security.AuthenticationListener;
-import sonia.scm.web.security.DefaultAuthenticationHandler;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.servlet.ServletContextListener;
-
-/**
- *
- * @author Sebastian Sdorra
- */
-public class BindingExtensionProcessor implements ExtensionProcessor
-{
-
-  /** the logger for BindingExtensionProcessor */
-  private static final Logger logger =
-    LoggerFactory.getLogger(BindingExtensionProcessor.class);
-
-  //~--- constructors ---------------------------------------------------------
-
-  /**
-   * Constructs ...
-   *
-   *
-   */
-  public BindingExtensionProcessor()
-  {
-    this.moduleSet = Sets.newHashSet();
-    this.extensions = Sets.newHashSet();
-    this.decoratorFactories = Sets.newHashSet();
-  }
-
-  //~--- methods --------------------------------------------------------------
-
-  /**
-   * Method description
-   *
-   *
-   * @param binder
-   */
-  public void bindDecorators(Binder binder)
-  {
-    DecoratorBinder decoratorBinder = new DecoratorBinder(binder);
-
-    for (Class<? extends DecoratorFactory<?>> c : decoratorFactories)
-    {
-      decoratorBinder.bindFactory(c);
-    }
-  }
-
-  /**
-   * Method description
-   *
-   *
-   * @param binder
-   */
-  @SuppressWarnings("unchecked")
-  public void bindExtensions(Binder binder)
-  {
-    Multibinder<RepositoryHandler> repositoryHandlers =
-      Multibinder.newSetBinder(binder, RepositoryHandler.class);
-    Multibinder<AuthenticationHandler> authenticators =
-      Multibinder.newSetBinder(binder, AuthenticationHandler.class);
-    Multibinder<ResourceHandler> resourceHandler =
-      Multibinder.newSetBinder(binder, ResourceHandler.class);
-    Multibinder<RepositoryHook> repositoryHookBinder =
-      Multibinder.newSetBinder(binder, RepositoryHook.class);
-
-    // changeset pre processor
-    Multibinder<ChangesetPreProcessor> changesetPreProcessorBinder =
-      Multibinder.newSetBinder(binder, ChangesetPreProcessor.class);
-    Multibinder<ChangesetPreProcessorFactory> changesetPreProcessorFactoryBinder =
-      Multibinder.newSetBinder(binder, ChangesetPreProcessorFactory.class);
-
-    // fileobject pre processor
-    Multibinder<FileObjectPreProcessor> fileObjectPreProcessorBinder =
-      Multibinder.newSetBinder(binder, FileObjectPreProcessor.class);
-    Multibinder<FileObjectPreProcessorFactory> fileObjectPreProcessorFactoryBinder =
-      Multibinder.newSetBinder(binder, FileObjectPreProcessorFactory.class);
-
-    // blameline pre processor
-    Multibinder<BlameLinePreProcessor> blameLinePreProcessorBinder =
-      Multibinder.newSetBinder(binder, BlameLinePreProcessor.class);
-    Multibinder<BlameLinePreProcessorFactory> blameLinePreProcessorFactoryBinder =
-      Multibinder.newSetBinder(binder, BlameLinePreProcessorFactory.class);
-
-    // repository service resolver
-    Multibinder<RepositoryServiceResolver> repositoryServiceResolverBinder =
-      Multibinder.newSetBinder(binder, RepositoryServiceResolver.class);
-
-    // listeners
-    Multibinder<RepositoryListener> repositoryListenerBinder =
-      Multibinder.newSetBinder(binder, RepositoryListener.class);
-    Multibinder<UserListener> userListenerBinder =
-      Multibinder.newSetBinder(binder, UserListener.class);
-    Multibinder<GroupListener> groupListenerBinder =
-      Multibinder.newSetBinder(binder, GroupListener.class);
-    Multibinder<AuthenticationListener> authenticationListenerBinder =
-      Multibinder.newSetBinder(binder, AuthenticationListener.class);
-    Multibinder<RepositoryRequestListener> repositoryRequestListenerBinder =
-      Multibinder.newSetBinder(binder, RepositoryRequestListener.class);
-    Multibinder<ServletContextListener> servletContextListenerBinder =
-      Multibinder.newSetBinder(binder, ServletContextListener.class);
-
-    authenticators.addBinding().to(DefaultAuthenticationHandler.class);
-
-    for (Class extensionClass : extensions)
-    {
-      if (RepositoryHandler.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind RepositoryHandler {}", extensionClass.getName());
-        }
-
-        binder.bind(extensionClass);
-        repositoryHandlers.addBinding().to(extensionClass);
-      }
-      else if (EncryptionHandler.class.isAssignableFrom(extensionClass))
-      {
-        bind(binder, EncryptionHandler.class, extensionClass);
-      }
-      else if (AuthenticationHandler.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind AuthenticationHandler {}",
-            extensionClass.getName());
-        }
-
-        binder.bind(extensionClass);
-        authenticators.addBinding().to(extensionClass);
-      }
-      else if (GroupListener.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind GroupListener {}", extensionClass.getName());
-        }
-
-        binder.bind(extensionClass);
-        groupListenerBinder.addBinding().to(extensionClass);
-      }
-      else if (UserListener.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind UserListener {}", extensionClass.getName());
-        }
-
-        binder.bind(extensionClass);
-        userListenerBinder.addBinding().to(extensionClass);
-      }
-      else if (RepositoryListener.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind RepositoryListener {}", extensionClass.getName());
-        }
-
-        binder.bind(extensionClass);
-        repositoryListenerBinder.addBinding().to(extensionClass);
-      }
-      else if (AuthenticationListener.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind AuthenticaitonListener {}",
-            extensionClass.getName());
-        }
-
-        binder.bind(extensionClass);
-        authenticationListenerBinder.addBinding().to(extensionClass);
-      }
-      else if (ResourceHandler.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind ResourceHandler {}", extensionClass.getName());
-        }
-
-        resourceHandler.addBinding().to(extensionClass);
-      }
-      else if (FileSystem.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind FileSystem {}", extensionClass.getName());
-        }
-
-        fileSystemClass = extensionClass;
-      }
-      else if (ChangesetPreProcessor.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind ChangesetPreProcessor {}",
-            extensionClass.getName());
-        }
-
-        changesetPreProcessorBinder.addBinding().to(extensionClass);
-      }
-      else if (
-        ChangesetPreProcessorFactory.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind ChangesetPreProcessorFactory {}",
-            extensionClass.getName());
-        }
-
-        changesetPreProcessorFactoryBinder.addBinding().to(extensionClass);
-      }
-      else if (FileObjectPreProcessor.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind FileObjectPreProcessor {}",
-            extensionClass.getName());
-        }
-
-        fileObjectPreProcessorBinder.addBinding().to(extensionClass);
-      }
-      else if (
-        FileObjectPreProcessorFactory.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind FileObjectPreProcessorFactory {}",
-            extensionClass.getName());
-        }
-
-        fileObjectPreProcessorFactoryBinder.addBinding().to(extensionClass);
-      }
-      else if (BlameLinePreProcessor.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind BlameLinePreProcessor {}",
-            extensionClass.getName());
-        }
-
-        blameLinePreProcessorBinder.addBinding().to(extensionClass);
-      }
-      else if (
-        BlameLinePreProcessorFactory.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind BlameLinePreProcessorFactory {}",
-            extensionClass.getName());
-        }
-
-        blameLinePreProcessorFactoryBinder.addBinding().to(extensionClass);
-      }
-      else if (RepositoryHook.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind RepositoryHook {}", extensionClass.getName());
-        }
-
-        repositoryHookBinder.addBinding().to(extensionClass);
-      }
-      else if (RepositoryRequestListener.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind RepositoryRequestListener {}",
-            extensionClass.getName());
-        }
-
-        repositoryRequestListenerBinder.addBinding().to(extensionClass);
-      }
-      else if (ServletContextListener.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind ServletContextListener {}",
-            extensionClass.getName());
-        }
-
-        servletContextListenerBinder.addBinding().to(extensionClass);
-      }
-      else if (RepositoryServiceResolver.class.isAssignableFrom(extensionClass))
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind RepositoryServiceResolver {}",
-            extensionClass.getName());
-        }
-
-        repositoryServiceResolverBinder.addBinding().to(extensionClass);
-      }
-      else
-      {
-        if (logger.isInfoEnabled())
-        {
-          logger.info("bind {}", extensionClass.getName());
-        }
-
-        binder.bind(extensionClass);
-      }
-    }
-  }
-
-  /**
-   * Method description
-   *
-   *
-   * @param extension
-   * @param extensionClass
-   */
-  @Override
-  public void processExtension(Extension extension, Class extensionClass)
-  {
-    if (Module.class.isAssignableFrom(extensionClass))
-    {
-      if (logger.isInfoEnabled())
-      {
-        logger.info("add GuiceModule {}", extensionClass.getName());
-      }
-
-      addModuleClass(extensionClass);
-    }
-    else if (DecoratorFactory.class.isAssignableFrom(extensionClass))
-    {
-      decoratorFactories.add(extensionClass);
-    }
-    else
-    {
-      extensions.add(extensionClass);
-    }
-  }
-
-  //~--- get methods ----------------------------------------------------------
-
-  /**
-   * Method description
-   *
-   *
-   * @return
-   */
-  public Class<? extends FileSystem> getFileSystemClass()
-  {
-    return fileSystemClass;
-  }
-
-  /**
-   * Method description
-   *
-   *
-   * @return
-   */
-  public Set<RepositoryHook> getHooks()
-  {
-    return hooks;
-  }
-
-  /**
-   * Method description
-   *
-   *
-   * @return
-   */
-  public Set<Module> getModuleSet()
-  {
-    return moduleSet;
-  }
-
-  //~--- methods --------------------------------------------------------------
-
-  /**
-   * Method description
-   *
-   *
-   * @param extensionClass
-   */
-  private void addModuleClass(Class<? extends Module> extensionClass)
-  {
-    try
-    {
-      Module module = extensionClass.newInstance();
-
-      moduleSet.add(module);
-    }
-    catch (Exception ex)
-    {
-      logger.error(ex.getMessage(), ex);
-    }
-  }
-
-  /**
-   * Method description
-   *
-   *
-   *
-   * @param binder
-   * @param type
-   * @param bindingType
-   * @param <T>
-   *
-   */
-  private <T> void bind(Binder binder, Class<T> type,
-    Class<? extends T> bindingType)
-  {
-    if (logger.isDebugEnabled())
-    {
-      logger.debug("bind {} of type {}", type.getName(), bindingType.getName());
-    }
-
-    binder.bind(type).to(bindingType);
-  }
-
-  //~--- fields ---------------------------------------------------------------
-
-  /** Field description */
-  private Set<Class<? extends DecoratorFactory<?>>> decoratorFactories;
-
-  /** Field description */
-  private Set<Class<?>> extensions;
-
-  /** Field description */
-  private Class<? extends FileSystem> fileSystemClass;
-
-  /** Field description */
-  private Set<RepositoryHook> hooks = new HashSet<RepositoryHook>();
-
-  /** Field description */
-  private Set<Module> moduleSet;
-}

scm-webapp/src/main/java/sonia/scm/DecoratorBinder.java

-/**
- * 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;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import com.google.inject.Binder;
-import com.google.inject.multibindings.Multibinder;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import sonia.scm.group.GroupManagerDecoratorFactory;
-import sonia.scm.repository.RepositoryManagerDecoratorFactory;
-import sonia.scm.user.UserManagerDecoratorFactory;
-
-/**
- *
- * @author Sebastian Sdorra
- */
-public class DecoratorBinder
-{
-
-  /**
-   * the logger for DecoratorBinder
-   */
-  private static final Logger logger =
-    LoggerFactory.getLogger(DecoratorBinder.class);
-
-  //~--- constructors ---------------------------------------------------------
-
-  /**
-   * Constructs ...
-   *
-   *
-   * @param binder
-   */
-  public DecoratorBinder(Binder binder)
-  {
-    userManagerDecoratorFactories = Multibinder.newSetBinder(binder,
-      UserManagerDecoratorFactory.class);
-    groupManagerDecoratorFactories = Multibinder.newSetBinder(binder,
-      GroupManagerDecoratorFactory.class);
-    repositoryManagerDecoratorFactories = Multibinder.newSetBinder(binder,
-      RepositoryManagerDecoratorFactory.class);
-  }
-
-  //~--- methods --------------------------------------------------------------
-
-  /**
-   * Method description
-   *
-   *
-   * @param factoryClass
-   */
-  public void bindFactory(Class factoryClass)
-  {
-    if (UserManagerDecoratorFactory.class.isAssignableFrom(factoryClass))
-    {
-      if (logger.isInfoEnabled())
-      {
-        logger.info("bind user manager decorator {}");
-      }
-
-      userManagerDecoratorFactories.addBinding().to(factoryClass);
-    }
-    else if (GroupManagerDecoratorFactory.class.isAssignableFrom(factoryClass))
-    {
-      if (logger.isInfoEnabled())
-      {
-        logger.info("bind group manager decorator {}");
-      }
-
-      groupManagerDecoratorFactories.addBinding().to(factoryClass);
-    }
-    else if (
-      RepositoryManagerDecoratorFactory.class.isAssignableFrom(factoryClass))
-    {
-      if (logger.isInfoEnabled())
-      {
-        logger.info("bind repository manager decorator {}");
-      }
-
-      repositoryManagerDecoratorFactories.addBinding().to(factoryClass);
-    }
-  }
-
-  //~--- fields ---------------------------------------------------------------
-
-  /** Field description */
-  private Multibinder<GroupManagerDecoratorFactory> groupManagerDecoratorFactories;
-
-  /** Field description */
-  private Multibinder<RepositoryManagerDecoratorFactory> repositoryManagerDecoratorFactories;
-
-  /** Field description */
-  private Multibinder<UserManagerDecoratorFactory> userManagerDecoratorFactories;
-}

scm-webapp/src/main/java/sonia/scm/ScmContextListener.java

    */
   private Injector getDefaultInjector(ServletContext servletContext)
   {
-    PluginLoader pluginLoader = new DefaultPluginLoader();
-    BindingExtensionProcessor bindExtProcessor =
-      new BindingExtensionProcessor();
+    DefaultPluginLoader pluginLoader = new DefaultPluginLoader(servletContext);
+    //BindingExtensionProcessor bindExtProcessor =
+    //  new BindingExtensionProcessor();
 
-    pluginLoader.processExtensions(bindExtProcessor);
+    // pluginLoader.processExtensions(bindExtProcessor);
 
     ClassOverrides overrides = ClassOverrides.findOverrides();
-    ScmServletModule main = new ScmServletModule(pluginLoader,
-                              bindExtProcessor, overrides);
+    ScmServletModule main = new ScmServletModule(pluginLoader, overrides);
     List<Module> moduleList = Lists.newArrayList();
 
     moduleList.add(new ScmInitializerModule());
     moduleList.add(ShiroWebModule.guiceFilterModule());
     moduleList.add(main);
     moduleList.add(new ScmSecurityModule(servletContext));
-    moduleList.addAll(bindExtProcessor.getModuleSet());
+    moduleList.addAll(pluginLoader.getModuleSet());
     moduleList.addAll(overrides.getModules());
 
     return Guice.createInjector(moduleList);

scm-webapp/src/main/java/sonia/scm/ScmServletModule.java

 
 //~--- non-JDK imports --------------------------------------------------------
 
-import com.google.common.eventbus.EventBus;
 import com.google.inject.Provider;
 import com.google.inject.multibindings.Multibinder;
 import com.google.inject.name.Names;
 import sonia.scm.io.FileSystem;
 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;
    * @param bindExtProcessor
    * @param overrides
    */
-  ScmServletModule(PluginLoader pluginLoader,
-    BindingExtensionProcessor bindExtProcessor, ClassOverrides overrides)
+  ScmServletModule(DefaultPluginLoader pluginLoader, ClassOverrides overrides)
   {
     this.pluginLoader = pluginLoader;
-    this.bindExtProcessor = bindExtProcessor;
     this.overrides = overrides;
   }
 
 
     bind(SCMContextProvider.class).toInstance(context);
 
-    // bind decorators
-    bindExtProcessor.bindDecorators(binder());
-
     ScmConfiguration config = getScmConfiguration(context);
     CipherUtil cu = CipherUtil.getInstance();
 
     bind(KeyGenerator.class).to(DefaultKeyGenerator.class);
     bind(CipherHandler.class).toInstance(cu.getCipherHandler());
     bind(EncryptionHandler.class, MessageDigestEncryptionHandler.class);
-    bindExtProcessor.bindExtensions(binder());
+    bind(FileSystem.class, DefaultFileSystem.class);
 
-    Class<? extends FileSystem> fileSystem =
-      bindExtProcessor.getFileSystemClass();
-
-    if (fileSystem == null)
-    {
-      fileSystem = DefaultFileSystem.class;
-    }
-
-    bind(FileSystem.class).to(fileSystem);
+    // bind extensions
+    pluginLoader.processExtensions(binder());
 
     // bind security stuff
     bind(AuthenticationManager.class, ChainAuthenticatonManager.class);
   //~--- fields ---------------------------------------------------------------
 
   /** Field description */
-  private BindingExtensionProcessor bindExtProcessor;
-
-  /** Field description */
   private ClassOverrides overrides;
 
   /** Field description */
-  private PluginLoader pluginLoader;
+  private DefaultPluginLoader pluginLoader;
 }

scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java

 
 //~--- non-JDK imports --------------------------------------------------------
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.io.Closeables;
+import com.google.inject.Binder;
+import com.google.inject.Module;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 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.util.IOUtil;
+import sonia.scm.plugin.ext.Extensions;
+import sonia.scm.web.security.DefaultAuthenticationHandler;
 
 //~--- JDK imports ------------------------------------------------------------
 
 
 import java.lang.annotation.Annotation;
 
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLDecoder;
 
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
+
 import javax.xml.bind.JAXB;
 
 /**
   public static final String ENCODING = "UTF-8";
 
   /** Field description */
+  public static final String EXTENSION_JAR = ".jar";
+
+  /** 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$";
 
   /**
    * Constructs ...
    *
+   *
+   * @param servletContext
    */
-  public DefaultPluginLoader()
+  public DefaultPluginLoader(ServletContext servletContext)
   {
+    this.servletContext = servletContext;
     this.annotationScannerFactory = new DefaultAnnotationScannerFactory();
 
     ClassLoader classLoader = getClassLoader();
 
     try
     {
-      load(classLoader);
+      locateCoreFile();
+      loadPlugins(classLoader);
+      scanForAnnotations();
+      findModules();
     }
     catch (IOException ex)
     {
    * @param packages
    * @param annotation
    * @param processor
+   * @param extensionPointProcessor
+   * @param extensionProcessor
    * @param <T>
    *
    * @return
    */
   public <T extends Annotation> AnnotationScanner createAnnotationScanner(
-    ClassLoader classLoader, Collection<String> packages, Class<T> annotation,
-    AnnotationProcessor<T> processor)
+    ClassLoader classLoader, Collection<String> packages,
+    AnnotationProcessor<ExtensionPoint> extensionPointProcessor,
+    AnnotationProcessor<Extension> extensionProcessor)
   {
     AnnotationScanner scanner = annotationScannerFactory.create(classLoader,
                                   packages);
 
-    scanner.addProcessor(annotation, processor);
+    if (extensionPointProcessor != null)
+    {
+      scanner.addProcessor(ExtensionPoint.class, extensionPointProcessor);
+    }
+
+    if (extensionProcessor != null)
+    {
+      scanner.addProcessor(Extension.class, extensionProcessor);
+    }
 
     return scanner;
   }
    * Method description
    *
    *
+   * @param binder
+   */
+  public void processExtensions(Binder binder)
+  {
+    new ExtensionBinder(binder).bind(bounds, extensionPoints, extensions);
+  }
+
+  /**
+   * Method description
+   *
+   *
    * @param processor
    */
   @Override
   public void processExtensions(ExtensionProcessor processor)
   {
-    ClassLoader classLoader = getClassLoader();
-
-    AnnotationCollector<Extension> annotationCollector =
-      new AnnotationCollector<Extension>();
-
-    for (Plugin plugin : installedPlugins)
+    for (AnnotatedClass<Extension> extension : extensions)
     {
-      if (logger.isDebugEnabled())
-      {
-        logger.debug("search extensions from plugin {}",
-          plugin.getInformation().getId());
-      }
-
-      InputStream input = null;
-
-      try
-      {
-        Set<String> packageSet = plugin.getPackageSet();
-
-        if (packageSet == null)
-        {
-          packageSet = new HashSet<String>();
-        }
-
-        packageSet.add(SCMContext.DEFAULT_PACKAGE);
-
-        File pluginFile = new File(plugin.getPath());
-
-        if (pluginFile.exists())
-        {
-          if (logger.isTraceEnabled())
-          {
-            String type = pluginFile.isDirectory()
-              ? "directory"
-              : "jar";
-
-            logger.trace("search extensions in packages {} of {} plugin {}",
-              new Object[] { packageSet,
-              type, pluginFile });
-          }
-
-          if (pluginFile.isDirectory())
-          {
-            createAnnotationScanner(classLoader, packageSet, Extension.class,
-              annotationCollector).scanDirectory(pluginFile);
-          }
-          else
-          {
-            input = new FileInputStream(plugin.getPath());
-            createAnnotationScanner(classLoader, packageSet, Extension.class,
-              annotationCollector).scanArchive(input);
-          }
-        }
-        else
-        {
-          logger.error("could not find plugin file {}", plugin.getPath());
-        }
-      }
-      catch (IOException ex)
-      {
-        logger.error("error during extension processing", ex);
-      }
-      finally
-      {
-        IOUtil.close(input);
-      }
-    }
-
-    Set<AnnotatedClass<Extension>> extensions =
-      annotationCollector.getAnnotatedClasses();
-
-    if (logger.isTraceEnabled())
-    {
-      logger.trace("start processing {} extensions", extensions.size());
-    }
-
-    for (AnnotatedClass<Extension> ac : extensions)
-    {
-      if (logger.isTraceEnabled())
-      {
-        logger.trace("process extension {}", ac.getAnnotatedClass());
-      }
-
-      processor.processExtension(ac.getAnnotation(), ac.getAnnotatedClass());
+      processor.processExtension(extension.getAnnotation(),
+        extension.getAnnotatedClass());
     }
   }
 
     return installedPlugins;
   }
 
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  public Set<Module> getModuleSet()
+  {
+    return moduleSet;
+  }
+
   //~--- methods --------------------------------------------------------------
 
   /**
    * Method description
    *
    *
+   * @param moduleClass
+   */
+  private void addModule(Class moduleClass)
+  {
+    try
+    {
+      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
    * Method description
    *
    *
-   * @param classLoader
+   * @param url
    *
-   * @throws IOException
+   * @return
    */
-  private void load(ClassLoader classLoader) throws IOException
+  private String extractResourcePath(URL url)
   {
-    Enumeration<URL> urlEnum = classLoader.getResources(PATH_PLUGINCONFIG);
+    String path = url.toExternalForm();
 
-    if (urlEnum != null)
+    if (path.startsWith("file:"))
     {
-      while (urlEnum.hasMoreElements())
+      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> extension : extensions)
+    {
+      Class extensionClass = extension.getAnnotatedClass();
+
+      if (Module.class.isAssignableFrom(extensionClass))
       {
-        URL url = urlEnum.nextElement();
-
-        loadPlugin(url);
+        bounds.add(extension);
+        addModule(extensionClass);
       }
-
-      if (logger.isDebugEnabled())
-      {
-        logger.debug("loaded {} plugins", installedPlugins.size());
-      }
-    }
-    else if (logger.isWarnEnabled())
-    {
-      logger.warn("no plugin descriptor found");
     }
   }
 
    */
   private void loadPlugin(URL url)
   {
-    String path = url.toExternalForm();
+    String path = extractResourcePath(url);
 
     if (logger.isTraceEnabled())
     {
 
     try
     {
-      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);
-      }
-
       boolean corePlugin = path.matches(REGE_COREPLUGIN);
 
       if (logger.isInfoEnabled())
     }
   }
 
+  /**
+   * Method description
+   *
+   *
+   * @param classLoader
+   *
+   * @throws IOException
+   */
+  private void loadPlugins(ClassLoader classLoader) throws IOException
+  {
+    Enumeration<URL> 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
+   */
+  private void locateCoreFile() throws MalformedURLException
+  {
+    Set<String> 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<String> packageSet,
+    AnnotationCollector<ExtensionPoint> extensionPointCollector,
+    AnnotationCollector<Extension> 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
+      {
+        Closeables.closeQuietly(input);
+      }
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param processor
+   *
+   * @param binder
+   */
+  private void scanForAnnotations()
+  {
+    ClassLoader classLoader = getClassLoader();
+
+    AnnotationCollector<ExtensionPoint> extensionPointCollector =
+      new AnnotationCollector<ExtensionPoint>();
+    AnnotationCollector<Extension> extensionCollector =
+      new AnnotationCollector<Extension>();
+
+    logger.debug("search extension points in {}", coreFile);
+
+    Set<String> 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<String> packageSet = plugin.getPackageSet();
+
+        if (packageSet == null)
+        {
+          packageSet = new HashSet<String>();
+        }
+
+        packageSet.add(SCMContext.DEFAULT_PACKAGE);
+
+        File pluginFile = new File(plugin.getPath());
+
+        if (pluginFile.exists())
+        {
+          scanFile(classLoader, packageSet, extensionPointCollector,
+            extensionCollector, pluginFile);
+        }
+        else
+        {
+          logger.error("could not find plugin file {}", plugin.getPath());
+        }
+      }
+      catch (IOException ex)
+      {
+        logger.error("error during extension processing", ex);
+      }
+    }
+
+    //J-
+    extensionPoints = extensionPointCollector.getAnnotatedClasses();
+    extensionPoints.add(
+      new AnnotatedClass<ExtensionPoint>(
+        Extensions.createExtensionPoint(true), 
+        ServletContextListener.class
+      )
+    );
+
+    extensions = extensionCollector.getAnnotatedClasses();
+    extensions.add( 
+      new AnnotatedClass<Extension>(
+        Extensions.createExtension(), 
+        DefaultAuthenticationHandler.class
+      ) 
+    );
+    //J+
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param classLoader
+   * @param packageSet
+   * @param extensionPointCollector
+   * @param extensionCollector
+   * @param file
+   *
+   * @throws IOException
+   */
+  private void scanURL(ClassLoader classLoader, Collection<String> packageSet,
+    AnnotationCollector<ExtensionPoint> extensionPointCollector,
+    AnnotationCollector<Extension> extensionCollector, URL file)
+    throws IOException
+  {
+    InputStream content = null;
+
+    try
+    {
+      content = file.openStream();
+      createAnnotationScanner(classLoader, packageSet, extensionPointCollector,
+        extensionCollector).scanArchive(content);
+    }
+    finally
+    {
+      Closeables.closeQuietly(content);
+    }
+  }
+
   //~--- get methods ----------------------------------------------------------
 
   /**
   private AnnotationScannerFactory annotationScannerFactory;
 
   /** Field description */
+  private Set<AnnotatedClass<Extension>> bounds = Sets.newHashSet();
+
+  /** Field description */
+  private URL coreFile;
+
+  /** Field description */
+  private Set<AnnotatedClass<ExtensionPoint>> extensionPoints;
+
+  /** Field description */
+  private Set<AnnotatedClass<Extension>> extensions;
+
+  /** Field description */
+  private Set<Module> moduleSet = Sets.newHashSet();
+
+  /** Field description */
   private Set<Plugin> installedPlugins = new HashSet<Plugin>();
+
+  /** Field description */
+  private ServletContext servletContext;
 }

scm-webapp/src/main/java/sonia/scm/plugin/ext/DefaultAnnotationScanner.java

  */
 
 
+
 package sonia.scm.plugin.ext;
 
 //~--- non-JDK imports --------------------------------------------------------
   @Override
   public void scanDirectory(File directory)
   {
-    Preconditions.checkArgument(!directory.isDirectory(),
+    Preconditions.checkArgument(directory.isDirectory(),
       "file must be a directory");
 
     String basePath = directory.getAbsolutePath();
       {
         if (logger.isDebugEnabled())
         {
-          logger.debug("call processor {} with {} and {}",
-            ap.getClass(), annotation, managedClass);
+          logger.debug("call processor {} with {} and {}", ap.getClass(),
+            annotation, managedClass);
         }
 
         ap.processAnnotation(annotation, managedClass);

scm-webapp/src/main/java/sonia/scm/plugin/ext/ExtensionBinder.java

+/**
+ * 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.plugin.ext;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.inject.Binder;
+import com.google.inject.multibindings.Multibinder;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import sonia.scm.plugin.ExtensionPoint;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.util.Set;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class ExtensionBinder
+{
+
+  /**
+   * the logger for ExtensionBinder
+   */
+  private static final Logger logger =
+    LoggerFactory.getLogger(ExtensionBinder.class);
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs ...
+   *
+   *
+   * @param binder
+   */
+  public ExtensionBinder(Binder binder)
+  {
+    this.binder = binder;
+  }
+
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * Method description
+   *
+   *
+   *
+   * @param bounds
+   * @param extensionPoints
+   * @param extensions
+   */
+  public void bind(Set<AnnotatedClass<Extension>> bounds,
+    Set<AnnotatedClass<ExtensionPoint>> extensionPoints,
+    Set<AnnotatedClass<Extension>> extensions)
+  {
+    if (logger.isInfoEnabled())
+    {
+      logger.info("bind {} extensions to {} extension points",
+        extensions.size(), extensionPoints.size());
+    }
+
+    for (AnnotatedClass<ExtensionPoint> extensionPoint : extensionPoints)
+    {
+      ExtensionPoint extensionPointAnnotation = extensionPoint.getAnnotation();
+      Class extensionPointClass = extensionPoint.getAnnotatedClass();
+
+      if (extensionPointAnnotation.multi())
+      {
+        bindMultiExtensionPoint(bounds, extensionPointClass, extensions);
+      }
+      else
+      {
+        bindExtensionPoint(bounds, extensionPointClass, extensions);
+      }
+    }
+
+    Set<AnnotatedClass<Extension>> extensionsCopy = Sets.newHashSet(extensions);
+
+    Iterables.removeAll(extensionsCopy, bounds);
+
+    for (AnnotatedClass<Extension> extension : extensionsCopy)
+    {
+      logger.info("bind {}, without extensionpoint",
+        extension.getAnnotatedClass());
+      binder.bind(extension.getAnnotatedClass());
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   *
+   * @param found
+   * @param extensionPointClass
+   * @param extensions
+   */
+  private void bindExtensionPoint(Set<AnnotatedClass<Extension>> found,
+    Class extensionPointClass, Set<AnnotatedClass<Extension>> extensions)
+  {
+    for (AnnotatedClass<Extension> extension : extensions)
+    {
+      Class extensionClass = extension.getAnnotatedClass();
+
+      if (extensionPointClass.isAssignableFrom(extensionClass))
+      {
+        found.add(extension);
+        bindSingleInstance(extensionPointClass, extensionClass);
+
+        break;
+      }
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   *
+   * @param found
+   * @param extensionPointClass
+   * @param extensions
+   */
+  private void bindMultiExtensionPoint(Set<AnnotatedClass<Extension>> found,
+    Class extensionPointClass, Set<AnnotatedClass<Extension>> extensions)
+  {
+    if (logger.isInfoEnabled())
+    {
+      logger.info("create multibinder for {}", extensionPointClass.getName());
+    }
+
+    Multibinder multibinder = Multibinder.newSetBinder(binder,
+                                extensionPointClass);
+
+    for (AnnotatedClass<Extension> extension : extensions)
+    {
+      Class extensionClass = extension.getAnnotatedClass();
+
+      if (extensionPointClass.isAssignableFrom(extensionClass))
+      {
+        if (logger.isInfoEnabled())
+        {
+          logger.info("bind {} to multibinder of {}", extensionClass.getName(),
+            extensionPointClass.getName());
+        }
+
+        found.add(extension);
+        multibinder.addBinding().to(extensionClass);
+      }
+    }
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param extensionPointClass
+   * @param extensionClass
+   */
+  private void bindSingleInstance(Class extensionPointClass,
+    Class extensionClass)
+  {
+    if (logger.isInfoEnabled())
+    {
+      logger.info("bind {} to {}", extensionClass.getName(),
+        extensionPointClass.getName());
+    }
+
+    binder.bind(extensionPointClass).to(extensionClass);
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private Binder binder;
+}

scm-webapp/src/main/java/sonia/scm/plugin/ext/Extensions.java

+/**
+ * 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.plugin.ext;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import sonia.scm.plugin.ExtensionPoint;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.lang.annotation.Annotation;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class Extensions
+{
+
+  /**
+   * Method description
+   *
+   *
+   * @return
+   */
+  public static Extension createExtension()
+  {
+    return new Extension()
+    {
+
+      @Override
+      public Class<? extends Annotation> annotationType()
+      {
+        return Extension.class;
+      }
+    };
+  }
+
+  /**
+   * Method description
+   *
+   *
+   * @param multi
+   *
+   * @return
+   */
+  public static ExtensionPoint createExtensionPoint(final boolean multi)
+  {
+    return new ExtensionPoint()
+    {
+
+      @Override
+      public boolean multi()
+      {
+        return multi;
+      }
+
+      @Override
+      public Class<? extends Annotation> annotationType()
+      {
+        return ExtensionPoint.class;
+      }
+    };
+  }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.