All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dxee.dject.Dject Maven / Gradle / Ivy

Go to download

A collection of guice extensions, help to improve the developer experience of guice.

There is a newer version: 1.5.1
Show newest version
package com.github.dxee.dject;

import com.github.dxee.dject.annotations.SuppressLifecycleUninitialized;
import com.github.dxee.dject.feature.DjectFeature;
import com.github.dxee.dject.feature.DjectFeatureContainer;
import com.github.dxee.dject.lifecycle.*;
import com.github.dxee.dject.metrics.ProvisionMetricsModule;
import com.github.dxee.dject.metrics.impl.LoggingProvisionModule;
import com.github.dxee.dject.spi.PropertySource;
import com.github.dxee.dject.trace.TracingProvisionListener;
import com.github.dxee.dject.visitors.*;
import com.google.common.base.Preconditions;
import com.google.inject.*;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.Element;
import com.google.inject.spi.ElementVisitor;
import com.google.inject.spi.Elements;
import com.google.inject.util.Modules;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.function.Consumer;

/**
 * Wrapper for Guice's Injector with extended methods.
 */
@Singleton
public final class Dject extends DelegatingInjector {
    private static final Logger LOGGER = LoggerFactory.getLogger(Dject.class);
    private final LifecycleManager manager = new LifecycleManager();
    private final Stage stage;
    private final Module module;
    private final IdentityHashMap, Object> features;

    // From guice
    @Inject
    private LifecycleShutdown lifecycleShutdown;


    public Dject(Builder builder) {
        this.stage = builder.stage;
        this.module = builder.module;
        this.features = builder.features;
        // create guice injector here
        this.injector = createInjector();
        this.injector.injectMembers(this);
    }

    @Singleton
    @SuppressLifecycleUninitialized
    class DjectFeatureContainerImpl implements DjectFeatureContainer {
        private final IdentityHashMap, Object> featureOverrides;

        @Inject
        private PropertySource properties;

        @Inject
        public DjectFeatureContainerImpl(IdentityHashMap, Object> featureOverrides) {
            this.featureOverrides = featureOverrides;
        }

        @SuppressWarnings("unchecked")
        @Override
        public  T get(DjectFeature feature) {
            return featureOverrides.containsKey(feature)
                    ? (T) featureOverrides.get(feature)
                    : (T) properties.get(feature.getKey(), feature.getType(), feature.getDefaultValue());
        }
    }

    /**
     * Create the injector
     * @return Injector
     */
    private Injector createInjector() {
        // Construct the injector using our override structure
        try {
            final DjectFeatureContainerImpl djectFeatureContainer = new DjectFeatureContainerImpl(features);

            Injector injector = Guice.createInjector(
                    stage,
                    // This has to be first to make sure @PostConstruct support is added as early
                    // as possible
                    new ProvisionMetricsModule(),
                    new LifecycleModule(),
                    new LifecycleListenerModule(),
                    new AbstractModule() {
                        @Override
                        protected void configure() {
                            bind(DjectFeatureContainer.class).toInstance(djectFeatureContainer);
                            bind(LifecycleManager.class).toInstance(manager);
                        }
                    },
                    module
            );
            manager.notifyStarted();
            LOGGER.info("Injector created successfully");
            return injector;
        } catch (Exception e) {
            LOGGER.error("Failed to create injector - {}@{}",
                    e.getClass().getSimpleName(),
                    System.identityHashCode(e),
                    e);
            try {
                manager.notifyStartFailed(e);
            } catch (Exception e2) {
                LOGGER.error("Failed to notify injector creation failure", e2);
            }
            throw e;
        }
    }

    /**
     * Shutdown for the lifecycle manager
     */
    public void shutdown() {
        lifecycleShutdown.shutdown();
    }

    /**
     * Block until lifecycle manager shutdown
     *
     * @throws InterruptedException
     */
    public void awaitShutdown() throws InterruptedException {
        lifecycleShutdown.awaitShutdown();
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private Stage stage = Stage.DEVELOPMENT;
        private Module module;
        private IdentityHashMap, Object> features = new IdentityHashMap<>();

        public Builder withStage(Stage stage) {
            this.stage = stage;
            return this;
        }

        public Builder withModule(Module module) {
            Preconditions.checkArgument(module != null, "module may not be null");

            this.module = module;
            return this;
        }

        public Builder withModules(Module... modules) {
            Preconditions.checkArgument(modules != null, "modules may not be null");

            this.module = Modules.combine(modules);
            return this;
        }

        public Builder withModules(List modules) {
            Preconditions.checkArgument(modules != null, "modules may not be null");

            this.module = Modules.combine(modules);
            return this;
        }

        public Builder withOverrideModules(Module... modules) {
            Preconditions.checkArgument(modules != null, "modules may not be null");

            this.module = Modules.override(module).with(modules);
            return this;
        }

        public Builder withOverrideModules(Collection modules) {
            Preconditions.checkArgument(modules != null, "modules may not be null");

            this.module = Modules.override(module).with(modules);
            return this;
        }

        public Builder withCombineModules(Module... modules) {
            Preconditions.checkArgument(modules != null, "modules may not be null");

            List m = new ArrayList<>();
            m.add(module);
            m.addAll(Arrays.asList(modules));
            this.module = Modules.combine(m);
            return this;
        }

        public Builder withFeatures(IdentityHashMap, Object> features) {
            Preconditions.checkArgument(features != null, "features may not be null");
            features.putAll(features);
            features.forEach((feature, value) -> {
                this.features.put(feature, value);
            });
            this.features = features;
            return this;
        }

        public  Builder withFeature(DjectFeature feature, T value) {
            Preconditions.checkArgument(feature != null, "feature may not be null");
            this.features.put(feature, value);
            return this;
        }

        /**
         * For debug purpose, See {@link LoggingProvisionModule}
         */
        public Builder withLoggingProvision() {
            withCombineModules(new LoggingProvisionModule());
            return this;
        }

        /**
         * For debug purpose, See {@link TracingProvisionListener}
         */
        public Builder withTracingProvision() {
            withCombineModules(new AbstractModule() {
                @Override
                protected void configure() {
                    bindListener(Matchers.any(), TracingProvisionListener.createDefault());
                }
            });
            return this;
        }

        /**
         * Call the provided visitor for all elements of the current module.
         * 

* This call will not modify any bindings * * @param visitor visitor */ public Builder withEachElementVister(ElementVisitor visitor) { Elements.getElements(module) .forEach(element -> element.acceptVisitor(visitor)); return this; } /** * Iterate through all elements of the current module and pass the output of the * ElementVisitor to the provided consumer. 'null' responses from the visitor are ignored. *

* This call will not modify any bindings * * @param visitor visitor */ public Builder withEachElementVister(ElementVisitor visitor, Consumer consumer) { Elements.getElements(module).forEach( element -> Optional.ofNullable(element.acceptVisitor(visitor)).ifPresent(consumer) ); return this; } /** * Log the current binding state. traceEachKey() is useful for debugging a sequence of * operation where the binding snapshot can be dumped to the log after an operation. */ public Builder withTraceEachKey() { return withEachElementVister(new KeyTracingVisitor(), message -> LOGGER.debug(message)); } /** * Log each binding */ public Builder withTraceEachBinding() { return withEachElementVister(new BindingTracingVisitor(), message -> LOGGER.debug(message)); } /** * Log each modulesource */ public Builder withTraceEachModuleSource() { return withEachElementVister(new ModuleSourceTracingVisitor(), message -> LOGGER.debug(message)); } /** * Log each provision listener */ public Builder withTraceEachProvisionListener() { return withEachElementVister(new ProvisionListenerTracingVisitor(), message -> LOGGER.debug(message)); } /** * Log a warning that static injection is being used. Static injection is considered a 'hack' * to alllow for backwards compatibility with non DI'd static code. */ public Builder withWarnOfStaticInjections() { return withEachElementVister(new WarnOfStaticInjectionVisitor(), message -> LOGGER.debug(message)); } /** * Log a warning that instance injection is being used. */ public Builder withWarnOfToInstanceInjections() { return withEachElementVister(new WarnOfToInstanceInjectionVisitor(), message -> LOGGER.debug(message)); } /** * Filter out elements for which the provided visitor returns true. * * @param predicate predicate */ public Builder withFilter(ElementVisitor predicate) { List elements = new ArrayList(); for (Element element : Elements.getElements(Stage.TOOL, module)) { if (element.acceptVisitor(predicate)) { elements.add(element); } } this.module = Elements.getModule(elements); return this; } /** * Filter out all bindings using requestStaticInjection */ public Builder withStripStaticInjections() { return withFilter(new IsNotStaticInjectionVisitor()); } /** * @return Return all elements in the managed module */ public List getElements() { return Elements.getElements(Stage.TOOL, module); } public Dject build() { return new Dject(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy