OpenAMにおけるInjectorHolderの使われ方

# はじめに

本稿では、Google GuiceのInjectorおよびそれから構成されるInjectorHolderがOpenAMでどのように使われているかについて説明します。関連するコードはForgeRock社のGuice Coreというパッケージにあります。ソースコードは[**ここ**](https://github.com/openam-jp/forgerock-guice/tree/master/forgerock-guice-core/src/main/java/org/forgerock/guice/core)から参照可能です。まずは、Guice Injectorの基本的な説明から始めます。
# Guice Injector の基本
もうすぐクリスマス/年末なので`SeasonsGreeting`インターフェイスを実装する`ChristmasGreeting`と`NewYearGreeting`という２つのクラスを使って説明します。

注目していただきたいのは、`Guice#createInjector(Module...)`メソッドで `Injector` のインスタンスを取得している部分です。

```java
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();
    }
}
```

`configure()` メソッドで`SeasonsGreeting`インターフェイスとその実装である`ChristmasGreeting`を結びつけています。それに従って生成された`injector`は`ChristmasGreeting`のインスタンスを返すようになります。

```text:実行結果
Merry Christmas!
```

上記の例では、`Guice#createInjector(Module...)`の引数はひとつだけでしたが、以下のように複数指定することも可能です。

```java
public static Injector createInjector(java.lang.Iterable<? extends Module> modules)
```
これを使えば`Module`を拡張したクラスで指定された複数の設定を一気に読み込むことが可能です。こうして生成された`Injector`のインスタンスを繰り返し使うために一定の「置き場所」を提供するのが、次に説明するInjectorHolderになります。

# InjectorHolderについて

InjectorHolderの「ホールダー」という名前からは、生成した`Injector`のインスタンスを保存しておき、必要に応じて取り出して使うという感じがします。しかし、実際には`Injector`は`InjectorHolder`の内部で生成され、内部で使用されます。外に出されることは無いため、ラッパーと呼んだ方が実態に近いかもしれません。

以下のコードでは、冒頭から列挙型が出てきます。ただひとつの要素（`INSTANCE`)を持つ列挙型を使ってシングルトンを実装する方法は
[Effective Java 第3版](https://www.amazon.co.jp/Effective-Java-%E7%AC%AC3%E7%89%88-Joshua-Bloch/dp/4621303252)の項目３でも紹介されています。ここではそれを使って`InjectorHolder`をシングルトンとして定義しています。

private コンストラクタ内で`InjectorFactory`を使って`Injector`を生成している部分に注目してください。

```java

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

```

最後の`getInstance(Class<T> clazz)`メソッドはこのシングルトン自身やシングルトンに登録された`Injector`インスタンスを返すのではなく、それを使って生成された`clazz`のインスタンスを返すようになっています。

それでは、`InjectorFactory`がどのようにして`Injector`のインスタンスを生成しているか見てみることにしましょう。

# InjectorFactory について

既に説明したように`Injector`の生成時には、`Module`を拡張したクラスのインスタンスを複数指定することができます。それぞれの拡張クラスは異なる設定で`Configure()`メソッドをオーバーライドしています。それらのうちどの拡張クラスをロードの対象とするかの判定には`@GuiceModule`というアノテーションを使います。具体的には、InjectorFactoryは

1. `@GuiceModule`というアノテーションの付いた`Module`の拡張クラスをクラスパス上で検索して
2. それらをインスタンス化し
3. `Guice#createInjector()`に引数として渡して`Injector`を生成する

という処理を行っています。以下のコードはその導入部分です。その他の部分は長くなるので省略しています。詳しくは[公開されているコード](https://github.com/openam-jp/forgerock-guice/blob/master/forgerock-guice-core/src/main/java/org/forgerock/guice/core/InjectorFactory.java)を見てください。

```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));
    }

```
InjectorHolderの仕組みについて見てきました。以下では、OpenAMでどのように使われているかについて見ていくことにします。

# OpenAMでの使われ方

OpenAMをGoogle Guice対応にするという作業はかなり前に終わっています。一般的に言って、OpenAMのような大規模なソフトウェアをGoogle Guiceに対応させることは容易ではありません。まずは、依存関係ツリーの枝葉に近い単純なサブツリーから開始して、より複雑なオブジェクトの方向に向かってツリーを辿っていくことになります。複雑なツリーでは、一気に書き換えられない場合もあるかもしれません。そうした場合には、書き換えが完了したクラスと未完了のクラスの「境目」にInjectorHolderを配置します。未完了のクラスのなかでInjectorHolderを使い書き換えが完了したクラスのオブジェクトを生成します。その先の依存関係の解決は、Google Guiceに任せるという方法です。書き換えが進むに従ってInjectorHolderも動かしていきます。こうすることにより複雑なソフトウェアでも漸進的に書き換えていくことが可能になります。

そもそもGoogle Guiceに対応させることができない、もしくは対応させるメリットがないということもあります。OpenAMの場合は、認証サービスがそれにあたります。OpenAMは多要素認証を行うために、様々な認証モジュールのインスタンスを独自に管理する仕組みを持っています。これをGoogle Guiceに対応させることは難しいため、InjectorHolderは個々の認証モジュールのレベルで使っています。

# おわりに

OpenAMでは"non-Guice World"と"Guice World"の「境目」にInjectorHolderを配置していることを見てきました。興味があれば、[OpenAMコンソーシアム](https://www.openam.jp/)で公開している[ソースコード](https://github.com/openam-jp)を見て下さい。