find and bind extension points automatically

This commit is contained in:
Sebastian Sdorra
2013-01-18 09:39:32 +01:00
parent 8232a24344
commit e581751624
11 changed files with 762 additions and 781 deletions

View File

@@ -46,7 +46,7 @@ import java.io.IOException;
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
@ExtensionPoint @ExtensionPoint(multi=false)
public interface FileSystem public interface FileSystem
{ {

View File

@@ -47,5 +47,8 @@ import java.lang.annotation.Target;
*/ */
@Documented @Documented
@Target({ ElementType.TYPE }) @Target({ ElementType.TYPE })
@Retention(RetentionPolicy.CLASS) @Retention(RetentionPolicy.RUNTIME)
public @interface ExtensionPoint {} public @interface ExtensionPoint
{
boolean multi() default true;
}

View File

@@ -30,8 +30,13 @@
*/ */
package sonia.scm.plugin.ext; package sonia.scm.plugin.ext;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Objects;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
@@ -60,6 +65,64 @@ public class AnnotatedClass<T extends Annotation>
this.annotatedClass = annotatedClass; 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 ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/** /**

View File

@@ -1,498 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm;
//~--- 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;
}

View File

@@ -1,128 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm;
//~--- 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;
}

View File

@@ -173,15 +173,14 @@ public class ScmContextListener extends GuiceServletContextListener
*/ */
private Injector getDefaultInjector(ServletContext servletContext) private Injector getDefaultInjector(ServletContext servletContext)
{ {
PluginLoader pluginLoader = new DefaultPluginLoader(); DefaultPluginLoader pluginLoader = new DefaultPluginLoader(servletContext);
BindingExtensionProcessor bindExtProcessor = //BindingExtensionProcessor bindExtProcessor =
new BindingExtensionProcessor(); // new BindingExtensionProcessor();
pluginLoader.processExtensions(bindExtProcessor); // pluginLoader.processExtensions(bindExtProcessor);
ClassOverrides overrides = ClassOverrides.findOverrides(); ClassOverrides overrides = ClassOverrides.findOverrides();
ScmServletModule main = new ScmServletModule(pluginLoader, ScmServletModule main = new ScmServletModule(pluginLoader, overrides);
bindExtProcessor, overrides);
List<Module> moduleList = Lists.newArrayList(); List<Module> moduleList = Lists.newArrayList();
moduleList.add(new ScmInitializerModule()); moduleList.add(new ScmInitializerModule());
@@ -190,7 +189,7 @@ public class ScmContextListener extends GuiceServletContextListener
moduleList.add(ShiroWebModule.guiceFilterModule()); moduleList.add(ShiroWebModule.guiceFilterModule());
moduleList.add(main); moduleList.add(main);
moduleList.add(new ScmSecurityModule(servletContext)); moduleList.add(new ScmSecurityModule(servletContext));
moduleList.addAll(bindExtProcessor.getModuleSet()); moduleList.addAll(pluginLoader.getModuleSet());
moduleList.addAll(overrides.getModules()); moduleList.addAll(overrides.getModules());
return Guice.createInjector(moduleList); return Guice.createInjector(moduleList);

View File

@@ -35,7 +35,6 @@ package sonia.scm;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.eventbus.EventBus;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names; import com.google.inject.name.Names;
@@ -64,6 +63,7 @@ import sonia.scm.io.DefaultFileSystem;
import sonia.scm.io.FileSystem; import sonia.scm.io.FileSystem;
import sonia.scm.net.HttpClient; import sonia.scm.net.HttpClient;
import sonia.scm.net.URLHttpClient; import sonia.scm.net.URLHttpClient;
import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.plugin.DefaultPluginManager; import sonia.scm.plugin.DefaultPluginManager;
import sonia.scm.plugin.Plugin; import sonia.scm.plugin.Plugin;
import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginLoader;
@@ -216,11 +216,9 @@ public class ScmServletModule extends ServletModule
* @param bindExtProcessor * @param bindExtProcessor
* @param overrides * @param overrides
*/ */
ScmServletModule(PluginLoader pluginLoader, ScmServletModule(DefaultPluginLoader pluginLoader, ClassOverrides overrides)
BindingExtensionProcessor bindExtProcessor, ClassOverrides overrides)
{ {
this.pluginLoader = pluginLoader; this.pluginLoader = pluginLoader;
this.bindExtProcessor = bindExtProcessor;
this.overrides = overrides; this.overrides = overrides;
} }
@@ -239,9 +237,6 @@ public class ScmServletModule extends ServletModule
bind(SCMContextProvider.class).toInstance(context); bind(SCMContextProvider.class).toInstance(context);
// bind decorators
bindExtProcessor.bindDecorators(binder());
ScmConfiguration config = getScmConfiguration(context); ScmConfiguration config = getScmConfiguration(context);
CipherUtil cu = CipherUtil.getInstance(); CipherUtil cu = CipherUtil.getInstance();
@@ -266,17 +261,10 @@ public class ScmServletModule extends ServletModule
bind(KeyGenerator.class).to(DefaultKeyGenerator.class); bind(KeyGenerator.class).to(DefaultKeyGenerator.class);
bind(CipherHandler.class).toInstance(cu.getCipherHandler()); bind(CipherHandler.class).toInstance(cu.getCipherHandler());
bind(EncryptionHandler.class, MessageDigestEncryptionHandler.class); bind(EncryptionHandler.class, MessageDigestEncryptionHandler.class);
bindExtProcessor.bindExtensions(binder()); bind(FileSystem.class, DefaultFileSystem.class);
Class<? extends FileSystem> fileSystem = // bind extensions
bindExtProcessor.getFileSystemClass(); pluginLoader.processExtensions(binder());
if (fileSystem == null)
{
fileSystem = DefaultFileSystem.class;
}
bind(FileSystem.class).to(fileSystem);
// bind security stuff // bind security stuff
bind(AuthenticationManager.class, ChainAuthenticatonManager.class); bind(AuthenticationManager.class, ChainAuthenticatonManager.class);
@@ -583,12 +571,9 @@ public class ScmServletModule extends ServletModule
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */
private BindingExtensionProcessor bindExtProcessor;
/** Field description */ /** Field description */
private ClassOverrides overrides; private ClassOverrides overrides;
/** Field description */ /** Field description */
private PluginLoader pluginLoader; private DefaultPluginLoader pluginLoader;
} }

View File

@@ -35,6 +35,12 @@ package sonia.scm.plugin;
//~--- non-JDK imports -------------------------------------------------------- //~--- 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -46,8 +52,10 @@ import sonia.scm.plugin.ext.AnnotationScanner;
import sonia.scm.plugin.ext.AnnotationScannerFactory; import sonia.scm.plugin.ext.AnnotationScannerFactory;
import sonia.scm.plugin.ext.DefaultAnnotationScannerFactory; import sonia.scm.plugin.ext.DefaultAnnotationScannerFactory;
import sonia.scm.plugin.ext.Extension; import sonia.scm.plugin.ext.Extension;
import sonia.scm.plugin.ext.ExtensionBinder;
import sonia.scm.plugin.ext.ExtensionProcessor; 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 ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
@@ -58,6 +66,7 @@ import java.io.InputStream;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLDecoder; import java.net.URLDecoder;
@@ -66,6 +75,9 @@ import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;
import javax.xml.bind.JAXB; import javax.xml.bind.JAXB;
/** /**
@@ -78,9 +90,18 @@ public class DefaultPluginLoader implements PluginLoader
/** Field description */ /** Field description */
public static final String ENCODING = "UTF-8"; public static final String ENCODING = "UTF-8";
/** Field description */
public static final String EXTENSION_JAR = ".jar";
/** Field description */ /** Field description */
public static final String PATH_PLUGINCONFIG = "META-INF/scm/plugin.xml"; 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 */ /** Field description */
public static final String REGE_COREPLUGIN = public static final String REGE_COREPLUGIN =
"^.*(?:/|\\\\)WEB-INF(?:/|\\\\)lib(?:/|\\\\).*\\.jar$"; "^.*(?:/|\\\\)WEB-INF(?:/|\\\\)lib(?:/|\\\\).*\\.jar$";
@@ -94,16 +115,22 @@ public class DefaultPluginLoader implements PluginLoader
/** /**
* Constructs ... * Constructs ...
* *
*
* @param servletContext
*/ */
public DefaultPluginLoader() public DefaultPluginLoader(ServletContext servletContext)
{ {
this.servletContext = servletContext;
this.annotationScannerFactory = new DefaultAnnotationScannerFactory(); this.annotationScannerFactory = new DefaultAnnotationScannerFactory();
ClassLoader classLoader = getClassLoader(); ClassLoader classLoader = getClassLoader();
try try
{ {
load(classLoader); locateCoreFile();
loadPlugins(classLoader);
scanForAnnotations();
findModules();
} }
catch (IOException ex) catch (IOException ex)
{ {
@@ -121,22 +148,44 @@ public class DefaultPluginLoader implements PluginLoader
* @param packages * @param packages
* @param annotation * @param annotation
* @param processor * @param processor
* @param extensionPointProcessor
* @param extensionProcessor
* @param <T> * @param <T>
* *
* @return * @return
*/ */
public <T extends Annotation> AnnotationScanner createAnnotationScanner( public <T extends Annotation> AnnotationScanner createAnnotationScanner(
ClassLoader classLoader, Collection<String> packages, Class<T> annotation, ClassLoader classLoader, Collection<String> packages,
AnnotationProcessor<T> processor) AnnotationProcessor<ExtensionPoint> extensionPointProcessor,
AnnotationProcessor<Extension> extensionProcessor)
{ {
AnnotationScanner scanner = annotationScannerFactory.create(classLoader, AnnotationScanner scanner = annotationScannerFactory.create(classLoader,
packages); packages);
scanner.addProcessor(annotation, processor); if (extensionPointProcessor != null)
{
scanner.addProcessor(ExtensionPoint.class, extensionPointProcessor);
}
if (extensionProcessor != null)
{
scanner.addProcessor(Extension.class, extensionProcessor);
}
return scanner; return scanner;
} }
/**
* Method description
*
*
* @param binder
*/
public void processExtensions(Binder binder)
{
new ExtensionBinder(binder).bind(bounds, extensionPoints, extensions);
}
/** /**
* Method description * Method description
* *
@@ -146,90 +195,10 @@ public class DefaultPluginLoader implements PluginLoader
@Override @Override
public void processExtensions(ExtensionProcessor processor) public void processExtensions(ExtensionProcessor processor)
{ {
ClassLoader classLoader = getClassLoader(); for (AnnotatedClass<Extension> extension : extensions)
AnnotationCollector<Extension> annotationCollector =
new AnnotationCollector<Extension>();
for (Plugin plugin : installedPlugins)
{ {
if (logger.isDebugEnabled()) processor.processExtension(extension.getAnnotation(),
{ extension.getAnnotatedClass());
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());
} }
} }
@@ -247,8 +216,41 @@ public class DefaultPluginLoader implements PluginLoader
return installedPlugins; return installedPlugins;
} }
/**
* Method description
*
*
* @return
*/
public Set<Module> getModuleSet()
{
return moduleSet;
}
//~--- methods -------------------------------------------------------------- //~--- 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 * Method description
* *
@@ -280,31 +282,47 @@ public class DefaultPluginLoader implements PluginLoader
* Method description * 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());
URL url = urlEnum.nextElement();
loadPlugin(url);
}
if (logger.isDebugEnabled())
{
logger.debug("loaded {} plugins", installedPlugins.size());
}
} }
else if (logger.isWarnEnabled()) else
{ {
logger.warn("no plugin descriptor found");
// 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))
{
bounds.add(extension);
addModule(extensionClass);
}
} }
} }
@@ -316,7 +334,7 @@ public class DefaultPluginLoader implements PluginLoader
*/ */
private void loadPlugin(URL url) private void loadPlugin(URL url)
{ {
String path = url.toExternalForm(); String path = extractResourcePath(url);
if (logger.isTraceEnabled()) if (logger.isTraceEnabled())
{ {
@@ -325,19 +343,6 @@ public class DefaultPluginLoader implements PluginLoader
try 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); boolean corePlugin = path.matches(REGE_COREPLUGIN);
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
@@ -378,6 +383,234 @@ public class DefaultPluginLoader implements PluginLoader
} }
} }
/**
* 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 ---------------------------------------------------------- //~--- get methods ----------------------------------------------------------
/** /**
@@ -408,6 +641,24 @@ public class DefaultPluginLoader implements PluginLoader
/** Field description */ /** Field description */
private AnnotationScannerFactory annotationScannerFactory; 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 */ /** Field description */
private Set<Plugin> installedPlugins = new HashSet<Plugin>(); private Set<Plugin> installedPlugins = new HashSet<Plugin>();
/** Field description */
private ServletContext servletContext;
} }

View File

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

View File

@@ -0,0 +1,211 @@
/**
* 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;
}

View File

@@ -0,0 +1,94 @@
/**
* 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;
}
};
}
}