InjectorHolder.java

/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions copyright [year] [name of copyright owner]".
 *
 * Copyright 2013-2015 ForgeRock AS.
 * Portions Copyright 2021 Wren Security.
 */

package org.forgerock.guice.core;

import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;

/**
 * <p>A thread-safe singleton holding the Guice Injector instance that other classes can call to get to use dependency
 * injection.</p>
 *
 * <p>The InjectorHolder should be used sparingly, ideally only to be used at the entry points into the system, i.e.
 * Servlets, Filters, etc, and allow Guice at this entry points to construct all of the required dependencies.
 * If introducing into an existing system then as the code is migrated over to be "Guice enabled" form a logical
 * "boundary" in your code which is the entry point(s) into the Guice "world" and at this entry points call the
 * InjectorHolder in the constructors of the boundary entry points to construct its dependencies.</p>
 *
 * <p>As more of the codebase is migrated to use Guice then this logical boundary will expand and the use of the
 * InjectorHolder should be moved further out with the boundary.</p>
 *
 * @see com.google.inject.Injector
 *
 * @since 1.0.0
 */
public enum InjectorHolder {

    /**
     * The Singleton instance of the InjectorHolder.
     */
    INSTANCE;

    private Injector injector;

    /**
     * Constructs an instance of the InjectorHolder and initialises the Guice Injector.
     */
    private InjectorHolder() {
        InjectorFactory injectorFactory = new InjectorFactory(
                new GuiceModuleCreator(),
                new GuiceInjectorCreator(),
                InjectorConfiguration.getGuiceModuleLoader(),
                new GuiceModuleFilter());

        try {
            injector = injectorFactory.createInjector(InjectorConfiguration.getModuleAnnotation());
        } catch (Exception e) {
            /*
             * This could occur during application server startup, if there is an error in the Guice module bindings.
             * The applications debugging framework may not be available at this point.
             *
             * The error gets consumed by the container startup, which is why we are printing the stack trace.
             */
            e.printStackTrace();
            throw new IllegalStateException(e);
        }
    }

    /**
     * <p>Uses the Guice injector to return the appropriate instance for the given injection type.</p>
     *
     * <p>Avoid using this method, in favour of having Guice inject your dependencies ahead of time.</p>
     *
     * @param clazz The class to get an instance of.
     * @param <T> The type of class to get.
     * @return A non-null instance of the class.
     */
    public static <T> T getInstance(Class<T> clazz) {
        return INSTANCE.injector.getInstance(clazz);
    }

    /**
     * <p>Uses the Guice injector to return the appropriate instance for the given injection key.</p>
     *
     * <p>Avoid using this method, in favour of having Guice inject your dependencies ahead of time.</p>
     *
     * @param key The key that defines the class to get.
     * @param <T> The type of class defined by the key.
     * @return A non-null instance of the class defined by the key.
     */
    public static <T> T getInstance(Key<T> key) {
        return INSTANCE.injector.getInstance(key);
    }

    /**
     * <p>Injects the dependencies of an already constructed instance. This method can be used to inter-operate with
     * objects created by other frameworks or services.</p>
     *
     * <p>Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or absence of an
     * injectable constructor.</p>
     *
     * <p>Preferably let Guice create all your objects for you and you'll never need to use this method.</p>
     *
     * @param instance A non-null instance to inject members on.
     */
    public static void injectMembers(Object instance) {
        INSTANCE.injector.injectMembers(instance);
    }

    /**
     * <p>Returns a new injector that inherits all state from this injector. All bindings, scopes,
     * interceptors and type converters are inherited -- they are visible to the child injector.
     * Elements of the child injector are not visible to its parent.</p>
     *
     * <p>Just-in-time bindings created for child injectors will be created in an ancestor injector
     * whenever possible. This allows for scoped instances to be shared between injectors. Use
     * explicit bindings to prevent bindings from being shared with the parent injector.</p>
     *
     * <p>No key may be bound by both an injector and one of its ancestors. This includes just-in-time
     * bindings. The lone exception is the key for {@code Injector.class}, which is bound by each
     * injector to itself.</p>
     *
     * @param modules An array of Guice modules to use to configure the child injector.
     * @return A non-null child instance of the root injector.
     */
    public static Injector createChildInjector(Module... modules) {
        return INSTANCE.injector.createChildInjector(modules);
    }

    /**
     * <p>Retrieves the Guice injector.</p>
     *
     * <p>Use with care! Always prefer using #getInstance(Class).</p>
     *
     * @return The configured Guice injector.
     */
    static Injector getInjector() {
        return INSTANCE.injector;
    }

    void register(Injector injector) {
        this.injector = injector;
    }
}