/** * The MIT License * * Copyright (c) 2010-2011 Sonatype, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.hudsonci.inject.internal.extension; import com.google.inject.Binder; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.ProvisionException; import com.google.inject.Scopes; import com.google.inject.name.Names; import hudson.Extension; import hudson.ExtensionFinder.Sezpoz; import hudson.ExtensionPoint; import hudson.model.Descriptor; import net.java.sezpoz.SpaceIndex; import net.java.sezpoz.SpaceIndexItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonatype.guice.bean.reflect.ClassSpace; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import static com.google.common.base.Preconditions.checkNotNull; /** * Guice {@link Module} that binds types based on SezPoz index. * * @since 1.397 */ @SuppressWarnings( { "unchecked", "rawtypes" } ) public final class SezPozExtensionModule implements Module { private static final Logger log = LoggerFactory.getLogger(SezPozExtensionModule.class); // ---------------------------------------------------------------------- // Implementation fields // ---------------------------------------------------------------------- private final ClassSpace space; private final boolean globalIndex; // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- public SezPozExtensionModule( final ClassSpace space, final boolean globalIndex ) { this.space = checkNotNull(space); this.globalIndex = globalIndex; } // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- public void configure( final Binder binder ) { for ( final SpaceIndexItem<Extension, Object> item : SpaceIndex.load( Extension.class, Object.class, space, globalIndex ) ) { try { // ignore the legacy SezPoz ExtensionFinder if ( !Sezpoz.class.equals( item.element() ) ) { bindItem( binder, item ); } } catch ( final Throwable e ) { if (item.annotation().optional()) { log.debug("Failed to bind optional extension: {}", item, e); } else { log.warn("Failed to bind extension: {}", item, e); } } } } // ---------------------------------------------------------------------- // Implementation methods // ---------------------------------------------------------------------- private void bindItem( final Binder binder, final SpaceIndexItem<Extension, ?> item ) throws InstantiationException { switch ( item.kind() ) { case TYPE: { final Class impl = (Class) item.element(); binder.bind( impl ).in( Scopes.SINGLETON ); bindHierarchy( binder, Key.get( impl ) ); break; } case METHOD: { final Method method = (Method) item.element(); final String name = method.getDeclaringClass().getName() + '.' + method.getName(); final ExtensionQualifier qualifier = new ExtensionQualifierImpl( item.annotation(), name ); bindProvider( binder, item, Key.get( method.getReturnType(), qualifier ) ); break; } case FIELD: { final Field field = (Field) item.element(); final String name = field.getDeclaringClass().getName() + '.' + field.getName(); final ExtensionQualifier qualifier = new ExtensionQualifierImpl( item.annotation(), name ); bindProvider( binder, item, Key.get( field.getType(), qualifier ) ); break; } default: break; } } private void bindProvider(final Binder binder, final SpaceIndexItem item, final Key key) { binder.bind(key).toProvider(new Provider() { public Object get() { try { return item.instance(); } catch (final InstantiationException e) { throw new ProvisionException(e.toString(), e); } } }).in(Scopes.SINGLETON); bindHierarchy(binder, key); } private static void bindHierarchy( final Binder binder, final Key rootKey ) { final Class root = rootKey.getTypeLiteral().getRawType(); Annotation qualifier = rootKey.getAnnotation(); if ( null == qualifier ) { qualifier = Names.named( root.getName() ); } for ( Class clazz = root; clazz != Object.class; clazz = clazz.getSuperclass() ) { if ( clazz != root && ExtensionPoint.class.isAssignableFrom( clazz ) ) { binder.bind( clazz ).annotatedWith( qualifier ).to( rootKey ); } if ( Descriptor.class.isAssignableFrom( clazz ) ) { binder.bind( Descriptor.class ).annotatedWith( qualifier ).to( rootKey ); } for ( final Class<?> iface : clazz.getInterfaces() ) { if ( ExtensionPoint.class.isAssignableFrom( iface ) ) { binder.bind( iface ).annotatedWith( qualifier ).to( rootKey ); } } } } }