Package com.netflix.governator.guice.serviceloader

Source Code of com.netflix.governator.guice.serviceloader.ServiceLoaderModule$ServiceSetProvider

package com.netflix.governator.guice.serviceloader;

import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.Callable;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.ProvisionException;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.google.inject.util.Types;
import com.netflix.governator.guice.lazy.LazySingletonScope;

/**
* Simple Guice module to integrate with the {@link ServiceLoader}.
*
* @author elandau
*/
public abstract class ServiceLoaderModule extends AbstractModule {

    public interface ServiceBinder<S> {
        public ServiceBinder<S> usingClassLoader(ClassLoader classLoader);
        public ServiceBinder<S> forInstalledServices(Boolean installed);
        public ServiceBinder<S> usingMultibinding(Boolean usingMultibinding);
    }
   
    static class ServiceBinderImpl<S> extends AbstractModule implements ServiceBinder<S> {
        private final Class<S> type;
        private ClassLoader classLoader;
        private boolean installed = false;
        private boolean asMultibinding = false;
       
        ServiceBinderImpl(Class<S> type) {
            this.type = type;
        }
       
        @Override
        public ServiceBinder<S> usingClassLoader(ClassLoader classLoader) {
            this.classLoader = classLoader;
            return this;
        }
       
        @Override
        public ServiceBinder<S> forInstalledServices(Boolean installed) {
            this.installed = installed;
            return this;
        }
       
        @Override
        public ServiceBinder<S> usingMultibinding(Boolean usingMultibinding) {
            this.asMultibinding = usingMultibinding;
            return this;
        }
       
        protected void configure() {
            Callable<ServiceLoader<S>> loader;
            if (installed) {
                if (classLoader != null) {
                    throw new RuntimeException("Class loader may not be combined with loading installed services");
                }
                loader = new Callable<ServiceLoader<S>>() {
                    @Override
                    public ServiceLoader<S> call() throws Exception {
                        return ServiceLoader.loadInstalled(type);
                    }
                };
            }
            else if (classLoader != null) {
                loader = new Callable<ServiceLoader<S>>() {
                    @Override
                    public ServiceLoader<S> call() throws Exception {
                        return ServiceLoader.load(type, classLoader);
                    }
                };
            }
            else {
                loader = new Callable<ServiceLoader<S>>() {
                    @Override
                    public ServiceLoader<S> call() throws Exception {
                        return ServiceLoader.load(type);
                    }
                };
            }
           
            if (asMultibinding) {
                Multibinder<S> binding = Multibinder.newSetBinder(binder(), type);
                ServiceLoader<S> services;
                try {
                    for (S service : loader.call()) {
                        System.out.println("Adding binding for service : " + service.getClass().getName());
                        ServiceProvider<S> provider = new ServiceProvider<S>(service);
                        binding.addBinding().toProvider(provider).in(Scopes.SINGLETON);
                    }
                } catch (Exception e) {
                    throw new ProvisionException("Failed to load services for '" + type + "'", e);
                }
            }
            else {
                @SuppressWarnings("unchecked")
                TypeLiteral<Set<S>> typeLiteral = (TypeLiteral<Set<S>>) TypeLiteral.get(Types.setOf(type));
                bind(typeLiteral)
                    .toProvider(new ServiceSetProvider<S>(loader))
                    .in(LazySingletonScope.get());
            }
        }
    }
   
    private final List<ServiceBinderImpl<?>> binders = Lists.newArrayList();
   
    @Override
    public final void configure() {
        configureServices();
       
        for (ServiceBinderImpl<?> binder : binders) {
            install(binder);
        }
    }
   
    protected abstract void configureServices();
   
    /**
     * Load services and make them available via a Set<S> binding using
     * multi-binding.  Note that this methods loads services lazily but also
     * allows for additional bindings to be done via Guice modules.
     *
     * @param type
     */
    public <S> ServiceBinder<S> bindServices(final Class<S> type) {
        ServiceBinderImpl<S> binder = new ServiceBinderImpl<S>(type);
        binders.add(binder);
        return binder;
    }
   
    /**
     * Custom provider that enables member injection on services
     *
     * @author elandau
     *
     * @param <S>
     */
    public static class ServiceSetProvider<S> implements ProviderWithExtensionVisitor<Set<S>> {
       
        private Injector injector;
        private Callable<ServiceLoader<S>> loader;
       
        public ServiceSetProvider(Callable<ServiceLoader<S>> loader) {
            this.loader = loader;
        }
       
        @Override
        public Set<S> get() {
            Set<S> services = Sets.newHashSet();
            try {
                for (S obj : loader.call()) {
                    injector.injectMembers(obj);
                    services.add(obj);
                }
            } catch (Exception e) {
                throw new ProvisionException("Failed to laod services", e);
            }
            return services;
        }

        @Override
        public <B, V> V acceptExtensionVisitor(
                BindingTargetVisitor<B, V> visitor,
                ProviderInstanceBinding<? extends B> binding) {
            return visitor.visit(binding);
        }

        @Inject
        @Toolable
        void initialize(Injector injector) {
            this.injector = injector;
        }
    }
   
    /**
     * Custom provider that allows for member injection of a service.  Note that while the
     * service was instantiated at binding time the members injection won't happen until the
     * set of services is injected.
     *
     * @author elandau
     *
     * @param <S>
     */
    public static class ServiceProvider<S> implements ProviderWithExtensionVisitor<S> {
        private S service;
       
        public ServiceProvider(S service) {
            this.service = service;
        }
       
        @Override
        public S get() {
            System.out.println("Get : " + service.getClass().getName());
            return service;
        }

        @Override
        public <B, V> V acceptExtensionVisitor(
                BindingTargetVisitor<B, V> visitor,
                ProviderInstanceBinding<? extends B> binding) {
            return visitor.visit(binding);
        }

        @Inject
        @Toolable
        void initialize(Injector injector) {
            injector.injectMembers(service);
        }
    }

}
TOP

Related Classes of com.netflix.governator.guice.serviceloader.ServiceLoaderModule$ServiceSetProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.