How to use InjectorHolder in OpenAM

Introduction

This article describes how Google Guice's Injector and its Injector Holder are used in OpenAM. The relevant code is in a package called Guice Core from ForgeRock. The source code is ** here ** Can be referenced. Let's start with a basic description of the Guice Injector.

Guice Injector Basics

It's almost Christmas / year-end, so I'll explain using two classes, ChristmasGreeting and NewYearGreeting, which implement the SeasonsGreeting interface.

What you should pay attention to is the part where the Guice # createInjector (Module ...) method gets an instance of ʻInjector`.

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class Example01 {

    public interface SeasonsGreeting {
        void greeting();
    }

    public static class ChristmasGreeting implements SeasonsGreeting {
        @Override
        public void greeting() {
            System.out.println("Merry Christmas!");
        }
    }

    public static class NewYearGreeting implements SeasonsGreeting {
        @Override
        public void greeting() {
            System.out.println("Happy New Year!");
        }
    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override protected void configure() {
                bind(SeasonsGreeting.class).to(ChristmasGreeting.class);
            }
        });

        SeasonsGreeting sg = injector.getInstance(SeasonsGreeting.class);
        sg.greeting();
    }
}

The configure () method connects the SeasonsGreeting interface with its implementation, ChristmasGreeting. The ʻinjector generated accordingly will now return an instance of ChristmasGreeting`.

Execution result


Merry Christmas!

In the above example, Guice # createInjector (Module ...) had only one argument, but you can specify multiple arguments as shown below.

public static Injector createInjector(java.lang.Iterable<? extends Module> modules)

If you use this, you can read multiple settings specified by the class that extends Module at once. The InjectorHolder, described below, provides a fixed "place" for repeated use of the ʻInjector` instance thus generated.

About Injector Holder

From the name "Holder" of InjectorHolder, it feels like saving the created instance of ʻInjector and taking it out and using it as needed. However, in reality, ʻInjector is generated inside ʻInjectorHolder` and used internally. It may be closer to the reality if you call it a rapper because you will not be taken out.

In the code below, the enum will come out from the beginning. How to implement a singleton using an enum with only one element (ʻINSTANCE) Introduced in item 3 of [Effective Java 3rd Edition](https://www.amazon.co.jp/Effective-Java-%E7%AC%AC3%E7%89%88-Joshua-Bloch/dp/4621303252) It has been. Here we use it to define ʻInjectorHolder as a singleton.

Notice the part of the private constructor that uses ʻInjectorFactory to generate ʻInjector.


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());

        try {
            injector = injectorFactory.createInjector(InjectorConfiguration.getModuleAnnotation());
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalStateException(e);
        }
    }

    /**
     * @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);
    }

The final getInstance (Class <T> clazz) method now returns an instance of the clazz created using it instead of returning the singleton itself or the ʻInjector` instance registered with the singleton. I have.

Now let's see how ʻInjectorFactory instantiates ʻInjector.

About InjectorFactory

As mentioned earlier, when generating ʻInjector, you can specify multiple instances of a class that extends Module. Each extension class overrides the Configure ()method with different settings. Annotation@GuiceModule` is used to determine which extension class to load. Specifically, the Injector Factory

  1. Search the classpath for the extension class of Module annotated with @GuiceModule.
  2. Instantiate them
  3. Pass as an argument to Guice # createInjector () to generate ʻInjector`

We are doing the processing. The code below is the introductory part. Other parts are omitted because they are long. For details, see [Public Code](https://github.com/openam-jp/forgerock-guice/blob/master/forgerock-guice-core/src/main/java/org/forgerock/guice/core/ See InjectorFactory.java).

final class InjectorFactory {

    /**
     * Creates a new Guice injector which is configured by all modules found by the {@link GuiceModuleLoader}
     * implementation.
     *
     * @param moduleAnnotation The module annotation.
     * @return A non-null Guice injector.
     */
    Injector createInjector(Class<? extends Annotation> moduleAnnotation) {
        /*
         This does not need to by synchronized as it is only ever called from the constructor of the
         InjectorHolder enum, which is thread-safe so no two threads can create an injector at the same time.

         This does mean that this method MUST not be called/used by another other class!
         */
        return injectorCreator.createInjector(createModules(moduleAnnotation));
    }

We have seen how the Injector Holder works. In the following, we will look at how it is used in OpenAM.

How to use in OpenAM

The work of making OpenAM compatible with Google Guice was done long ago. Generally speaking, getting large software like OpenAM compatible with Google Guice is not easy. You'll start with a simple subtree that's closer to the branches and leaves of the dependency tree, and follow the tree towards more complex objects. In a complicated tree, it may not be possible to rewrite at once. In such a case, place the InjectorHolder at the "border" between the rewritten class and the unrewritten class. Create an object of the class that has been rewritten using InjectorHolder in the incomplete class. The way to resolve the dependency after that is to leave it to Google Guice. As the rewriting progresses, the Injector Holder will also move. By doing this, even complicated software can be gradually rewritten.

In the first place, it may not be possible to support Google Guice, or there is no merit to support it. In the case of OpenAM, this is the authentication service. OpenAM has a mechanism to manage various authentication module instances independently for multi-factor authentication. Since it is difficult to make this compatible with Google Guice, Injector Holder uses it at the level of individual authentication modules.

in conclusion

At OpenAM, we have seen that the Injector Holder is placed at the "border" between "non-Guice World" and "Guice World". If you are interested, please see the Source Code published in the OpenAM Consortium.

Recommended Posts

How to use InjectorHolder in OpenAM
How to use Lombok in Spring
How to use classes in Java?
How to use custom helpers in rails
How to use named volume in docker-compose.yml
How to use Docker in VSCode DevContainer
How to use MySQL in Rails tutorial
How to use environment variables in RubyOnRails
Understand in 5 minutes !! How to use Docker
How to use credentials.yml.enc introduced in Rails 5.2
How to use ExpandableListView in Android Studio
How to use Map
How to use rbenv
How to use letter_opener_web
How to use with_option
How to use fields_for
How to use java.util.logging
How to use map
How to use collection_select
How to use active_hash! !!
How to use MapStruct
How to use hidden_field_tag
How to use TreeSet
[How to use label]
How to use identity
How to use hashes
How to use JUnit 5
How to use Dozer.mapper
How to use Gradle
How to use org.immutables
How to use java.util.stream.Collector
How to use VisualVM
How to use Map
[Rails] How to use select boxes in Ransack
How to use "sign_in" in integration test (RSpec)
How to use JQuery in js.erb of Rails6
[Rails] How to use PostgreSQL in Vagrant environment
[Java] How to use Map
How to use Chain API
[Java] How to use Map
How to use Priority Queuing
[Rails] How to use enum
How to use java Optional
How to use JUnit (beginner)
How to use Ruby return
[Rails] How to use enum
How to use @Builder (Lombok)
[Swift] How to use UserDefaults
How to use Swift UIScrollView
How to use Big Decimal
[Java] How to use Optional ②
[Java] How to use removeAll ()
How to use String [] args
[Java] How to use string.format
How to use Java Map
Ruby: How to use cookies
How to use dependent :: destroy
How to use Eclipse Debug_Shell
How to use Apache POI
[Rails] How to use validation
How to use Java variables