stop scanning classpath, bind extensions from plugin and module descriptor

This commit is contained in:
Sebastian Sdorra
2014-03-28 22:49:55 +01:00
parent cbcd63257d
commit 43b92f0aca
10 changed files with 236 additions and 626 deletions

View File

@@ -35,11 +35,15 @@ package sonia.scm.plugin;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Binder;
import com.google.inject.Module;
import sonia.scm.plugin.ext.ExtensionProcessor;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.Set;
/**
*
@@ -52,12 +56,28 @@ public interface PluginLoader
* Method description
*
*
* @param processor
* @param binder
*/
public void processExtensions(ExtensionProcessor processor);
public void processExtensions(Binder binder);
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Collection<Module> getInjectionModules();
/**
* Method description
*
*
* @return
*/
public Collection<ScmModule> getInstalledModules();
/**
* Method description
*

View File

@@ -17,6 +17,15 @@
<dependencies>
<!-- annotation processor -->
<dependency>
<groupId>sonia.scm.maven</groupId>
<artifactId>scm-annotation-processor</artifactId>
<version>2.0.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>

View File

@@ -181,7 +181,7 @@ public class ScmContextListener extends GuiceServletContextListener
*/
private Injector getDefaultInjector(ServletContext servletContext)
{
DefaultPluginLoader pluginLoader = new DefaultPluginLoader(servletContext);
DefaultPluginLoader pluginLoader = new DefaultPluginLoader();
ClassOverrides overrides = ClassOverrides.findOverrides();
ScmServletModule main = new ScmServletModule(pluginLoader, overrides);
@@ -193,7 +193,7 @@ public class ScmContextListener extends GuiceServletContextListener
moduleList.add(ShiroWebModule.guiceFilterModule());
moduleList.add(main);
moduleList.add(new ScmSecurityModule(servletContext));
moduleList.addAll(pluginLoader.getModuleSet());
moduleList.addAll(pluginLoader.getInjectionModules());
moduleList.addAll(overrides.getModules());
return Guice.createInjector(moduleList);

View File

@@ -39,10 +39,10 @@ import com.google.inject.Provider;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Names;
import com.google.inject.servlet.RequestScoped;
import com.google.inject.servlet.ServletModule;
import com.google.inject.throwingproviders.ThrowingProviderBinder;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,11 +66,11 @@ import sonia.scm.net.HttpClient;
import sonia.scm.net.URLHttpClient;
import sonia.scm.plugin.DefaultPluginLoader;
import sonia.scm.plugin.DefaultPluginManager;
import sonia.scm.plugin.Plugin;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginManager;
import sonia.scm.repository.DefaultRepositoryManager;
import sonia.scm.repository.DefaultRepositoryProvider;
import sonia.scm.repository.HealthCheckContextListener;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryManager;
@@ -132,27 +132,21 @@ import sonia.scm.web.security.DefaultAdministrationContext;
//~--- JDK imports ------------------------------------------------------------
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import sonia.scm.repository.HealthCheckContextListener;
import sonia.scm.repository.HealthChecker;
/**
*
* @author Sebastian Sdorra
*/
public class ScmServletModule extends ServletModule
public class ScmServletModule extends JerseyServletModule
{
/** Field description */
@@ -376,67 +370,18 @@ public class ScmServletModule extends ServletModule
*/
params.put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE.toString());
params.put(ResourceConfig.FEATURE_REDIRECT, Boolean.TRUE.toString());
/*
* TODO remove UriExtensionsConfig and PackagesResourceConfig
* to stop jersey classpath scanning
*/
params.put(ServletContainer.RESOURCE_CONFIG_CLASS,
UriExtensionsConfig.class.getName());
String restPath = getRestPackages();
if (logger.isInfoEnabled())
{
logger.info("configure jersey with package path: {}", restPath);
}
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, restPath);
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "unbound");
serve(PATTERN_RESTAPI).with(GuiceContainer.class, params);
}
/**
* Method description
*
*
* @param packageSet
* @param plugin
*/
private void appendPluginPackages(Set<String> packageSet, Plugin plugin)
{
Set<String> pluginPackageSet = plugin.getPackageSet();
if (pluginPackageSet != null)
{
for (String pluginPkg : pluginPackageSet)
{
boolean append = true;
for (String pkg : packageSet)
{
if (pluginPkg.startsWith(pkg))
{
append = false;
break;
}
}
if (append)
{
if (logger.isDebugEnabled())
{
String name = "unknown";
if (plugin.getInformation() != null)
{
name = plugin.getInformation().getName();
}
logger.debug("plugin {} added rest path {}", name, pluginPkg);
}
packageSet.add(pluginPkg);
}
}
}
}
/**
* Method description
*
@@ -524,44 +469,6 @@ public class ScmServletModule extends ServletModule
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
private String getRestPackages()
{
Set<String> packageSet = new HashSet<String>();
packageSet.add(SCMContext.DEFAULT_PACKAGE);
Collection<Plugin> plugins = pluginLoader.getInstalledPlugins();
if (plugins != null)
{
for (Plugin plugin : plugins)
{
appendPluginPackages(packageSet, plugin);
}
}
StringBuilder buffer = new StringBuilder();
Iterator<String> pkgIterator = packageSet.iterator();
while (pkgIterator.hasNext())
{
buffer.append(pkgIterator.next());
if (pkgIterator.hasNext())
{
buffer.append(";");
}
}
return buffer.toString();
}
/**
* Load ScmConfiguration with JAXB
*

View File

@@ -38,11 +38,13 @@ import org.apache.shiro.authc.DisabledAccountException;
//~--- JDK imports ------------------------------------------------------------
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
/**
*
* @author Sebastian Sdorra
*/
@Provider
public class DisabledAccountExceptionMapper
extends StatusExceptionMapper<DisabledAccountException>
{

View File

@@ -38,11 +38,13 @@ import org.apache.shiro.authc.ExcessiveAttemptsException;
//~--- JDK imports ------------------------------------------------------------
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
/**
*
* @author Sebastian Sdorra
*/
@Provider
public class ExcessiveAttemptsExceptionMapper
extends StatusExceptionMapper<ExcessiveAttemptsException>
{

View File

@@ -35,7 +35,10 @@ package sonia.scm.plugin;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Module;
@@ -43,43 +46,21 @@ import com.google.inject.Module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext;
import sonia.scm.plugin.ext.AnnotatedClass;
import sonia.scm.plugin.ext.AnnotationCollector;
import sonia.scm.plugin.ext.AnnotationProcessor;
import sonia.scm.plugin.ext.AnnotationScanner;
import sonia.scm.plugin.ext.AnnotationScannerFactory;
import sonia.scm.plugin.ext.DefaultAnnotationScannerFactory;
import sonia.scm.plugin.ext.Extension;
import sonia.scm.plugin.ext.ExtensionBinder;
import sonia.scm.plugin.ext.ExtensionProcessor;
import sonia.scm.plugin.ext.Extensions;
import sonia.scm.util.ClassLoaders;
import sonia.scm.util.IOUtil;
import sonia.scm.web.security.DefaultAuthenticationHandler;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;
import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
/**
*
@@ -89,24 +70,11 @@ public class DefaultPluginLoader implements PluginLoader
{
/** Field description */
public static final String ENCODING = "UTF-8";
/** Field description */
public static final String EXTENSION_JAR = ".jar";
public static final String PATH_MODULECONFIG = "META-INF/scm/module.xml";
/** Field description */
public static final String PATH_PLUGINCONFIG = "META-INF/scm/plugin.xml";
/** Field description */
public static final String PATH_WEBINFLIB = "/WEB-INF/lib";
/** Field description */
public static final String PATH_SCMCORE = PATH_WEBINFLIB.concat("/scm-core");
/** Field description */
public static final String REGE_COREPLUGIN =
"^.*(?:/|\\\\)WEB-INF(?:/|\\\\)lib(?:/|\\\\).*\\.jar$";
/** the logger for DefaultPluginLoader */
private static final Logger logger =
LoggerFactory.getLogger(DefaultPluginLoader.class);
@@ -116,90 +84,54 @@ public class DefaultPluginLoader implements PluginLoader
/**
* Constructs ...
*
*
* @param servletContext
*/
public DefaultPluginLoader(ServletContext servletContext)
public DefaultPluginLoader()
{
this.servletContext = servletContext;
this.annotationScannerFactory = new DefaultAnnotationScannerFactory();
ClassLoader classLoader =
ClassLoaders.getContextClassLoader(DefaultPluginLoader.class);
try
{
locateCoreFile();
loadPlugins(classLoader);
scanForAnnotations();
findModules();
JAXBContext context = JAXBContext.newInstance(ScmModule.class,
Plugin.class);
modules = getInstalled(classLoader, context, PATH_MODULECONFIG);
plugins = getInstalled(classLoader, context, PATH_PLUGINCONFIG);
appendExtensions(multiple, single, extensions, modules);
appendExtensions(multiple, single, extensions, plugins);
}
catch (IOException ex)
catch (Exception ex)
{
throw new RuntimeException(ex);
throw Throwables.propagate(ex);
}
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param classLoader
* @param packages
* @param extensionPointProcessor
* @param extensionProcessor
* @param <T>
*
* @return
*/
public <T extends Annotation> AnnotationScanner createAnnotationScanner(
ClassLoader classLoader, Collection<String> packages,
AnnotationProcessor<ExtensionPoint> extensionPointProcessor,
AnnotationProcessor<Extension> extensionProcessor)
{
AnnotationScanner scanner = annotationScannerFactory.create(classLoader,
packages);
if (extensionPointProcessor != null)
{
scanner.addProcessor(ExtensionPoint.class, extensionPointProcessor);
}
if (extensionProcessor != null)
{
scanner.addProcessor(Extension.class, extensionProcessor);
}
return scanner;
}
/**
* Method description
*
*
* @param binder
*/
@Override
public void processExtensions(Binder binder)
{
new ExtensionBinder(binder).bind(bounds, extensionPoints, extensions);
logger.info("start processing extensions");
appendExtensions(multiple, single, extensions, modules);
appendExtensions(multiple, single, extensions, plugins);
if (logger.isInfoEnabled())
{
logger.info(
"found {} extensions for {} multiple and {} single extension points",
extensions.size(), multiple.size(), single.size());
}
/**
* Method description
*
*
* @param processor
*/
@Override
public void processExtensions(ExtensionProcessor processor)
{
for (AnnotatedClass<Extension> extension : extensions)
{
processor.processExtension(extension.getAnnotation(),
extension.getAnnotatedClass());
}
// TODO bind jax-rs providers always as singleton
new ExtensionBinder(binder).bind(multiple, single, extensions);
}
//~--- get methods ----------------------------------------------------------
@@ -211,9 +143,9 @@ public class DefaultPluginLoader implements PluginLoader
* @return
*/
@Override
public Collection<Plugin> getInstalledPlugins()
public Set<Module> getInjectionModules()
{
return installedPlugins;
return ImmutableSet.copyOf(injectionModules);
}
/**
@@ -222,9 +154,22 @@ public class DefaultPluginLoader implements PluginLoader
*
* @return
*/
public Set<Module> getModuleSet()
@Override
public Collection<ScmModule> getInstalledModules()
{
return moduleSet;
return modules;
}
/**
* Method description
*
*
* @return
*/
@Override
public Collection<Plugin> getInstalledPlugins()
{
return plugins;
}
//~--- methods --------------------------------------------------------------
@@ -233,409 +178,103 @@ public class DefaultPluginLoader implements PluginLoader
* Method description
*
*
* @param moduleClass
* @param multiple
* @param single
* @param extensions
* @param mods
*/
private void addModule(Class moduleClass)
private void appendExtensions(Set<Class> multiple, Set<Class> single,
Set<Class> extensions, Iterable<? extends ScmModule> mods)
{
try
for (ScmModule mod : mods)
{
logger.info("add module {}", moduleClass);
moduleSet.add((Module) moduleClass.newInstance());
}
catch (Exception ex)
for (ExtensionPointElement epe : mod.getExtensionPoints())
{
logger.error(
"could not create module instance of ".concat(moduleClass.getName()),
ex);
}
}
/**
* Method description
*
*
* @param path
*
* @return
*/
private String decodePath(String path)
if (epe.isMultiple())
{
File file = new File(path);
if (!file.exists())
{
try
{
path = URLDecoder.decode(path, ENCODING);
}
catch (IOException ex)
{
logger.error("could not decode path ".concat(path), ex);
}
}
return path;
}
/**
* Method description
*
*
* @param url
*
* @return
*/
private String extractResourcePath(URL url)
{
String path = url.toExternalForm();
if (path.startsWith("file:"))
{
path = path.substring("file:".length(),
path.length() - "/META-INF/scm/plugin.xml".length());
multiple.add(epe.getClazz());
}
else
{
// jar:file:/some/path/file.jar!/META-INF/scm/plugin.xml
path = path.substring("jar:file:".length(), path.lastIndexOf('!'));
path = decodePath(path);
single.add(epe.getClazz());
}
}
logger.trace("extrace resource path {} from url {}", path, url);
return path;
}
/**
* Method description
*
*/
private void findModules()
for (Class extensionClass : mod.getExtensions())
{
for (AnnotatedClass<Extension> extension : extensions)
{
Class extensionClass = extension.getAnnotatedClass();
if (Module.class.isAssignableFrom(extensionClass))
{
bounds.add(extension);
addModule(extensionClass);
}
}
}
/**
* Method description
*
*
* @param url
*/
private void loadPlugin(URL url)
{
String path = extractResourcePath(url);
if (logger.isTraceEnabled())
{
logger.trace("try to load plugin from {}", path);
}
try
{
boolean corePlugin = path.matches(REGE_COREPLUGIN);
if (logger.isInfoEnabled())
{
logger.info("load {}plugin {}", corePlugin
? "core "
: " ", path);
}
Plugin plugin = JAXB.unmarshal(url, Plugin.class);
PluginInformation info = plugin.getInformation();
PluginCondition condition = plugin.getCondition();
if (condition != null)
{
info.setCondition(condition);
}
if (info != null)
{
info.setState(corePlugin
? PluginState.CORE
: PluginState.INSTALLED);
}
plugin.setPath(path);
if (logger.isDebugEnabled())
{
logger.debug("add plugin {} to installed plugins", info.getId());
}
installedPlugins.add(plugin);
injectionModules.add((Module) extensionClass.newInstance());
}
catch (Exception ex)
{
logger.error("could not load plugin ".concat(path), ex);
logger.error("could not create instance of module", ex);
}
}
/**
* 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
*/
@SuppressWarnings("unchecked")
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
{
IOUtil.close(input);
}
}
}
/**
* Method description
*
*
* @param processor
*
* @param binder
*/
private void scanForAnnotations()
{
ClassLoader classLoader =
ClassLoaders.getContextClassLoader(DefaultPluginLoader.class);
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);
extensions.add(extensionClass);
}
}
//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+
Iterables.addAll(extensions, mod.getJaxrsProviders());
Iterables.addAll(extensions, mod.getJaxrsResources());
}
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param classLoader
* @param packageSet
* @param extensionPointCollector
* @param extensionCollector
* @param file
* @param context
* @param path
* @param <T>
*
* @return
*
* @throws IOException
* @throws JAXBException
*/
private void scanURL(ClassLoader classLoader, Collection<String> packageSet,
AnnotationCollector<ExtensionPoint> extensionPointCollector,
AnnotationCollector<Extension> extensionCollector, URL file)
throws IOException
private <T> Set<T> getInstalled(ClassLoader classLoader, JAXBContext context,
String path)
throws IOException, JAXBException
{
InputStream content = null;
Builder<T> builder = ImmutableSet.builder();
Enumeration<URL> urls = classLoader.getResources(path);
try
while (urls.hasMoreElements())
{
content = file.openStream();
createAnnotationScanner(classLoader, packageSet, extensionPointCollector,
extensionCollector).scanArchive(content);
}
finally
{
IOUtil.close(content);
URL url = urls.nextElement();
T module = (T) context.createUnmarshaller().unmarshal(url);
builder.add(module);
}
return builder.build();
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private final AnnotationScannerFactory annotationScannerFactory;
private Set<ScmModule> modules;
/** Field description */
private final Set<AnnotatedClass<Extension>> bounds = Sets.newHashSet();
private Set<Class> multiple = Sets.newHashSet();
/** Field description */
private final Set<Module> moduleSet = Sets.newHashSet();
private Set<Plugin> plugins;
/** Field description */
private final Set<Plugin> installedPlugins = new HashSet<Plugin>();
private Set<Class> single = Sets.newHashSet();
/** Field description */
private final ServletContext servletContext;
private Set<Module> injectionModules = Sets.newHashSet();
/** Field description */
private URL coreFile;
/** Field description */
private Set<AnnotatedClass<ExtensionPoint>> extensionPoints;
/** Field description */
private Set<AnnotatedClass<Extension>> extensions;
private Set<Class> extensions = Sets.newHashSet();
}

View File

@@ -211,7 +211,8 @@ public class DefaultPluginManager implements PluginManager
// ugly workaround
Plugin newPlugin = new Plugin();
newPlugin.setInformation(plugin);
// TODO check
// newPlugin.setInformation(plugin);
installedPlugins.put(id, newPlugin);
}
}

View File

@@ -38,6 +38,8 @@ package sonia.scm.plugin.ext;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Scopes;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.multibindings.Multibinder;
@@ -45,18 +47,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.EagerSingleton;
import sonia.scm.plugin.ExtensionPoint;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.util.Set;
import javax.ws.rs.ext.Provider;
/**
*
* @author Sebastian Sdorra
*/
public class ExtensionBinder
public final class ExtensionBinder
{
/**
@@ -84,54 +87,58 @@ public class ExtensionBinder
* Method description
*
*
*
* @param bounds
* @param extensionPoints
* @param mutipleExtensionPoints
* @param singleExtensionPoints
* @param extensions
*/
public void bind(Set<AnnotatedClass<Extension>> bounds,
Set<AnnotatedClass<ExtensionPoint>> extensionPoints,
Set<AnnotatedClass<Extension>> extensions)
public void bind(Set<Class> mutipleExtensionPoints,
Set<Class> singleExtensionPoints, Set<Class> extensions)
{
if (logger.isInfoEnabled())
{
logger.info("bind {} extensions to {} extension points",
extensions.size(), extensionPoints.size());
extensions.size(),
mutipleExtensionPoints.size() + singleExtensionPoints.size());
}
for (AnnotatedClass<ExtensionPoint> extensionPoint : extensionPoints)
{
ExtensionPoint extensionPointAnnotation = extensionPoint.getAnnotation();
Class extensionPointClass = extensionPoint.getAnnotatedClass();
Set<Class> boundClasses = Sets.newHashSet();
if (extensionPointAnnotation.multi())
for (Class extensionPointClass : mutipleExtensionPoints)
{
bindMultiExtensionPoint(bounds, extensionPointClass, extensions);
bindMultiExtensionPoint(boundClasses, extensionPointClass, extensions);
}
for (Class extensionPointClass : singleExtensionPoints)
{
bindExtensionPoint(boundClasses, extensionPointClass, extensions);
}
Set<Class> extensionsCopy = Sets.newHashSet(extensions);
Iterables.removeAll(extensionsCopy, boundClasses);
for (Class extension : extensionsCopy)
{
AnnotatedBindingBuilder abb = binder.bind(extension);
if (isProvider(extension))
{
logger.info("bind provider {} as singleton", extension);
abb.in(Scopes.SINGLETON);
}
else if (isEagerSingleton(extension))
{
logger.info("bind {} as eager singleton, without extensionpoint",
extension);
abb.asEagerSingleton();
}
else
{
bindExtensionPoint(bounds, extensionPointClass, extensions);
logger.info("bind {}, without extensionpoint", extension);
binder.bind(extension);
}
}
Set<AnnotatedClass<Extension>> extensionsCopy = Sets.newHashSet(extensions);
Iterables.removeAll(extensionsCopy, bounds);
for (AnnotatedClass<Extension> extension : extensionsCopy)
{
boolean eagerSingleton = isEagerSingleton(extension.getAnnotatedClass());
String as = Util.EMPTY_STRING;
if (eagerSingleton)
{
as = " as eager singleton";
}
logger.info("bind {}{}, without extensionpoint",
extension.getAnnotatedClass(), as);
binder.bind(extension.getAnnotatedClass());
}
}
/**
@@ -140,23 +147,31 @@ public class ExtensionBinder
*
*
* @param found
*
* @param boundClasses
* @param extensionPointClass
* @param extensions
*/
@SuppressWarnings("unchecked")
private void bindExtensionPoint(Set<AnnotatedClass<Extension>> found,
Class extensionPointClass, Set<AnnotatedClass<Extension>> extensions)
private void bindExtensionPoint(Set<Class> boundClasses,
Class extensionPointClass, Set<Class> extensions)
{
for (AnnotatedClass<Extension> extension : extensions)
{
Class extensionClass = extension.getAnnotatedClass();
boolean bound = false;
for (Class extensionClass : extensions)
{
if (extensionPointClass.isAssignableFrom(extensionClass))
{
found.add(extension);
bindSingleInstance(extensionPointClass, extensionClass);
if (bound)
{
throw new IllegalStateException(
"extension point ".concat(extensionPointClass.getName()).concat(
" is not multiple and is already bound to another class"));
}
break;
bindSingleInstance(extensionPointClass, extensionClass);
boundClasses.add(extensionClass);
bound = true;
}
}
}
@@ -167,12 +182,14 @@ public class ExtensionBinder
*
*
* @param found
*
* @param boundClasses
* @param extensionPointClass
* @param extensions
*/
@SuppressWarnings("unchecked")
private void bindMultiExtensionPoint(Set<AnnotatedClass<Extension>> found,
Class extensionPointClass, Set<AnnotatedClass<Extension>> extensions)
private void bindMultiExtensionPoint(Set<Class> boundClasses,
Class extensionPointClass, Iterable<Class> extensions)
{
if (logger.isInfoEnabled())
{
@@ -182,10 +199,8 @@ public class ExtensionBinder
Multibinder multibinder = Multibinder.newSetBinder(binder,
extensionPointClass);
for (AnnotatedClass<Extension> extension : extensions)
for (Class extensionClass : extensions)
{
Class extensionClass = extension.getAnnotatedClass();
if (extensionPointClass.isAssignableFrom(extensionClass))
{
boolean eagerSingleton = isEagerSingleton(extensionClass);
@@ -203,8 +218,6 @@ public class ExtensionBinder
extensionClass.getName(), extensionPointClass.getName(), as);
}
found.add(extension);
ScopedBindingBuilder sbb = multibinder.addBinding().to(extensionClass);
if (eagerSingleton)
@@ -212,6 +225,8 @@ public class ExtensionBinder
sbb.asEagerSingleton();
logger.info("bind {} as eager singleton");
}
boundClasses.add(extensionClass);
}
}
}
@@ -261,13 +276,26 @@ public class ExtensionBinder
*
* @return
*/
private boolean isEagerSingleton(Class<?> extensionClass)
private boolean isEagerSingleton(Class extensionClass)
{
return extensionClass.isAnnotationPresent(EagerSingleton.class);
}
/**
* Method description
*
*
* @param extensionClass
*
* @return
*/
private boolean isProvider(Class extensionClass)
{
return extensionClass.isAnnotationPresent(Provider.class);
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Binder binder;
private final Binder binder;
}

View File

@@ -52,11 +52,13 @@ import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sonia.scm.plugin.ext.Extension;
/**
*
* @author Sebastian Sdorra
*/
@Extension
@Singleton
public class DefaultAuthenticationHandler implements AuthenticationHandler
{