42 Contexts and Dependency Injectionの使用

Coherenceは、Coherenceクラスタ・メンバー内のContexts and Dependency Injection (CDI)をサポートします。この機能を使用すると、NamedMapNamedCacheSessionインスタンスなどのCoherence管理対象リソースをCDIマネージドBeanに注入したり、CDI Beanをイベント・インターセプタやキャッシュ・ストアなどのCoherence管理対象リソースに注入したり、CDIオブザーバ・メソッドを使用してCoherenceサーバー側イベントを処理できます。

また、Coherence CDIは、デシリアライズ時に一時オブジェクトの自動注入をサポートします。この機能を使用すると、(ドメイン駆動設計(DDD)命名法を使用する)サービスやリポジトリなどのCDIマネージドBeanを、エントリ・プロセッサやデータ・クラス・インスタンスなどの一時オブジェクトに注入して、ドメイン駆動アプリケーションの実装を大幅に簡素化できます。

この章の内容は次のとおりです。

CDIの使用

Contexts and Dependency Injection (CDI)をpom.xmlファイルの依存関係として宣言して、その使用を有効にする必要があります。

CDIを依存関係として宣言するには、pom.xmlに次のコンテンツを追加します。

<dependency>
    <groupId>${coherence.groupId}</groupId>
    <artifactId>coherence-cdi-server</artifactId>
    <version>${coherence.version}</version>
</dependency>

必要な依存関係を設定した後、CDIを使用してCoherenceオブジェクトをマネージドCDI Beanに注入できます。

ノート:

CDIを使用する場合、アプリケーション・コードで使用されるすべてのCoherenceリソースを注入する必要があります。アプリケーションでは、Coherenceリソースを直接作成または初期化するCoherence APIをコールしないでください(特に静的CacheFactoryメソッド)。アプリケーション・コードがこれらのCoherence APIをコールすると、CDIフレームワークがCoherence拡張を初期化する前に起動プロセスの初期段階でCoherenceが初期化されることがあります。一般的な症状として、CoherenceはCDIフレームワークから正しい構成を選択せずに起動します。

CDI BeanへのCoherenceオブジェクトの注入

一般に、CDIおよび依存関係インジェクションにより、アプリケーション・クラスで必要な依存関係を宣言し、実行時に必要に応じてそれらを提供することが容易になります。この機能により、アプリケーションの開発、テストおよび推論が容易になります。そしてコードは非常にわかりやすくなります。

Coherence CDIでは、ClusterSessionNamedMapNamedCacheContinuousQueryCacheConfigurableCacheFactoryなどのCoherenceオブジェクトに対して同じ操作を実行できます。

この項には次のトピックが含まれます:

NamedMap、NamedCacheおよび関連オブジェクトの注入

NamedMapのインスタンスをCDI Beanに注入するには、その注入ポイントを定義する必要があります。

import javax.inject.Inject;

@Inject
private NamedMap<Long, Person> people;

前述の例では、注入するマップ名は、注入するフィールドの名前(people)と同じであると想定されています。そうでない場合は、@Name修飾子を使用して、明示的に取得するマップの名前を指定できます。

import com.oracle.coherence.cdi.Name;
import javax.inject.Inject;

@Inject
@Name("people")
private NamedMap<Long, Person> m_people;

これは、コンストラクタ注入またはセッター注入を使用している場合にも行う必要があります。

import com.oracle.coherence.cdi.Name;
import javax.inject.Inject;

@Inject
public MyClass(@Name("people") NamedMap<Long, Person> people) {
    ...
}

@Inject
public void setPeople(@Name("people") NamedMap<Long, Person> people) {
    ...
}

前述のすべての例では、デフォルトのスコープを使用することを想定していますが、常にそうであるとはかぎりません。たとえば、複数のCoherenceクラスタに接続するExtendクライアントがあるとします。その場合は複数のスコープがあります。

この場合、@SessionName修飾子を使用して、キャッシュまたはマップの指定に使用される構成済のSessionの名前を指定します。

import com.oracle.coherence.cdi.SessionName;
import javax.inject.Inject;

@Inject
@SessionName("Products")
private NamedCache<Long, Product> products;

@Inject
@SessionName("Customers")
private NamedCache<Long, Customer> customers;

前述の例のいずれかのNamedMapまたはNamedCacheをそれぞれAsyncNamedMapおよびAsyncNamedCacheに置き換えて、これらのAPIの非同期バリアントを注入できます。

import com.oracle.coherence.cdi.SessionName;
import javax.inject.Inject;

@Inject
private AsyncNamedMap<Long, Person> people;

@Inject
@SessionName("Products")
private AsyncNamedCache<Long, Person> Product;

この項には次のトピックが含まれます:

NamedMapまたはNamedCacheビューの注入
NamedMapまたはNamedCacheView修飾子を追加して、ビューを注入することもできます。
import com.oracle.coherence.cdi.View;
import javax.inject.Inject;

@Inject
@View
private NamedMap<Long, Person> people;

@Inject
@View
private NamedCache<Long, Product> products;

前述の例は同等であり、どちらもビューの構築時にAlwaysFilterを使用するため、バッキング・マップのすべてのデータをローカル・ビューに取り込みます。ビュー内のデータをサブセットに制限する場合は、カスタムFilterBindingを実装する(推奨。FilterBinding注釈の使用を参照)か、CohQLを使用してフィルタを指定できる組込みの@WhereFilterを便宜上使用できます。

import com.oracle.coherence.cdi.Name;
import com.oracle.coherence.cdi.View;
import com.oracle.coherence.cdi.WhereFilter;
import javax.inject.Inject;

@Inject
@View
@WhereFilter("gender = 'MALE'")
@Name("people")
private NamedMap<Long, Person> men;

@Inject
@View
@WhereFilter("gender = 'FEMALE'")
@Name("people")
private NamedMap<Long, Person> women;

ビューでは、ローカルに格納されるデータ量とネットワーク経由で転送されるデータ量の両方を削減するために、サーバー上のエントリ値の変換もサポートされます。たとえば、バッキング・マップに複雑なPersonオブジェクトがありますが、クライアントUIのドロップダウンに移入するには名前のみが必要です。その場合は、カスタムExtractorBindingを実装する(推奨。カスタム・エクストラクタ注釈の作成を参照)か、組込みの@PropertyExtractorを便宜上使用できます。

import com.oracle.coherence.cdi.Name;
import com.oracle.coherence.cdi.View;
import com.oracle.coherence.cdi.PropertyExtractor;
import javax.inject.Inject;

@Inject
@View
@PropertyExtractor("fullName")
@Name("people")
private NamedMap<Long, String> names;

ノート:

この例の値タイプは、指定された@PropertyExtractorによるサーバー側の変換が原因で、PersonからStringに変更されました。

NamedTopicおよび関連オブジェクトの注入

NamedTopicのインスタンスをCDI Beanに注入するには、その注入ポイントを定義する必要があります。

import com.tangosol.net.NamedTopic;
import javax.inject.Inject;

@Inject
private NamedTopic<Order> orders;

前述の例では、注入するトピック名は、注入するフィールドの名前(この場合はorders)と同じであると想定されています。そうでない場合は、@Name修飾子を使用して、明示的に取得するトピックの名前を指定できます。

import com.oracle.coherence.cdi.Name;
import com.tangosol.net.NamedTopic;
import javax.inject.Inject;

@Inject
@Name("orders")
private NamedTopic<Order> topic;

かわりに'constructor'または'setter input'を使用している場合は、次の操作を実行する必要があります。

import com.oracle.coherence.cdi.Name;
import com.tangosol.net.NamedTopic;
import javax.inject.Inject;

@Inject
public MyClass(@Name("orders") NamedTopic<Order> orders) {
    ...
}

@Inject
public void setOrdersTopic(@Name("orders") NamedTopic<Order> orders) {
    ...
}

前述のすべての例では、デフォルトのスコープを使用することを想定していますが、常にそうであるとはかぎりません。たとえば、複数のCoherenceクラスタに接続するExtendクライアントがあるとします。その場合は複数のスコープがあります。

この場合、@SessionName修飾子を使用して、トピックの指定に使用される構成済のSessionの名前を指定します。

import com.oracle.coherence.cdi.SessionName;
import com.tangosol.net.NamedTopic;
import javax.inject.Inject;

@Inject
@SessionName("Finance")
private NamedTopic<PaymentRequest> payments;

@Inject
@SessionName("Shipping")
private NamedTopic<ShippingRequest> shipments;

前述の例では、CDI BeanにNamedTopicインスタンスを注入できますが、かわりに特定のトピックにPublisherまたはSubscriberを注入する方が簡単で便利です。

これは、前述の例のいずれかのNamedTopic<T>を次のいずれかに置き換えることで簡単に実行できます。

Publisher<T>:

import com.oracle.coherence.cdi.Name;
import com.oracle.coherence.cdi.SessionName;
import javax.inject.Inject;

@Inject
private Publisher<Order> orders;

@Inject
@Name("orders")
private Publisher<Order> m_orders;

@Inject
@SessionName("Finance")
private Publisher<PaymentRequest> payments;

または

Subscriber<T>:
import com.oracle.coherence.cdi.Name;
import com.oracle.coherence.cdi.SessionName;
import javax.inject.Inject;

@Inject
private Subscriber<Order> orders;

@Inject
@Name("orders")
private Subscriber<Order> m_orders;

@Inject
@SessionName("Finance")
private Subscriber<PaymentRequest> payments;

トピック名(注入ポイント名または@Name注釈からの明示的な名前に基づく)、スコープおよびメッセージ・タイプなどのトピック・メタデータは、内部で使用され、NamedTopicを取得し、そこからPublisherまたはSubscriberを取得します。

また、サブスクライバをサブスクライバ・グループに配置する場合(効果的にトピックをキューに変換する場合)、注入ポイントに@SubscriberGroup修飾子を追加することで、これを簡単に実行できます。

import com.oracle.coherence.cdi.SubscriberGroup;
import javax.inject.Inject;

@Inject
@SubscriberGroup("orders-queue")
private Subscriber<Order> orders;

他の注入ポイントのサポート

NamedMapNamedCacheNamedTopicおよび関連インスタンスの注入は、Coherence CDIの最も使用される単一機能ですが、これが唯一の機能ではありません。

次の項では、Coherence CDIを使用して注入できるその他のCoherenceアーティファクトについて説明します。

クラスタおよびOperationalContext注入

アプリケーションの任意の場所にあるClusterインタフェースのインスタンスが必要な場合は、注入を介して簡単に取得できます。

import com.tangosol.net.Cluster;
import javax.inject.Inject;

@Inject
private Cluster cluster;

OperationalContextのインスタンスが必要な場合は、同じ操作を実行できます。

import com.tangosol.net.OperationalContext;
import javax.inject.Inject;

@Inject
private OperationalContext ctx;
名前付きセッション注入

まれに、Sessionを直接使用する必要がある場合、Coherence CDIによってそれを簡単に行うことができるようになります。

Coherenceは、CDIサーバーの起動時にデフォルトのSessionを作成します。セッションは、通常のデフォルトのキャッシュ構成ファイルを使用して作成されます。他の名前付きセッションは、タイプSessionConfigurationのCDI Beanとして構成できます。

たとえば:

import com.oracle.coherence.cdi.SessionInitializer;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class MySession
        implements SessionInitializer
    {
    public String getName()
        {
        return "Foo";
        }
    // implement session configuration methods
    }

前述のBeanは、Fooという名前のSessionの構成を作成します。セッションは、CDIサーバーの起動時に作成され、その後、他のBeanに注入されます。SessionConfigurationを作成するためのより簡単な方法は、SessionIntializerインタフェースを実装し、クラスに注釈を付けることです。

たとえば:

import com.oracle.coherence.cdi.ConfigUri;
import com.oracle.coherence.cdi.Scope;
import com.oracle.coherence.cdi.SessionInitializer;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@ApplicationScoped
@Named("Foo")
@Scope("Foo")
@ConfigUri("my-coherence-config.xml")
public class MySession
        implements SessionInitializer
    {
    }

前述の構成では、my-coherence-config.xml構成ファイルから作成された基礎となるConfigurableCacheFactoryを使用して、Fooという名前のSession Beanを作成します。

デフォルトのSessionのインスタンスを取得するには、セッションを使用する必要があるクラスにセッションを注入するだけです。

import com.tangosol.net.Session;
import javax.inject.Inject;

@Inject
private Session session;

特定の名前付きSessionが必要な場合は、@Name修飾子を使用して修飾し、Session名を指定できます。

import com.oracle.coherence.cdi.Name;
import javax.inject.Inject;

@Inject
@Name("SessionOne")
private Session sessionOne;

@Inject
@Name("SessionTwo")
private Session sessionTwo;
シリアライザ注入

ほとんどの場合、シリアライザを直接処理する必要はありませんが、Coherence CDIを使用すると、必要なときに名前付きシリアライザの取得(および新しいシリアライザの登録)が簡単になります。

現在のコンテキスト・クラス・ローダーのデフォルトSerializerを取得するには、これを注入します。

import com.tangosol.io.Serializer;
import javax.inject.Inject;

@Inject
private Serializer defaultSerializer;

ただし、@Name修飾子を使用して簡単に実行できる、操作構成で定義されている名前付きシリアライザのいずれかを注入すると役立つ場合があります。

import com.oracle.coherence.cdi.Name;
import javax.inject.Inject;

@Inject
@Name("java")
private Serializer javaSerializer;

@Inject
@Name("pof")
private Serializer pofSerializer;

この例は、操作構成で定義されたシリアライザに加えて、Serializerインタフェースを実装する名前付きBeanに対してBeanManagerルックアップも実行します。つまり、次のようなカスタムSerializer Beanを実装できます。

import com.tangosol.io.Serializer;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@Named("json")
@ApplicationScoped
public class JsonSerializer implements Serializer {
    ...
}

カスタムSerializer BeanはCDIによって自動的に検出および登録されるため、操作構成で定義された名前付きシリアライザのように簡単に注入できます。

import com.oracle.coherence.cdi.Name;
import javax.inject.Inject;

@Inject
@Name("json")
private Serializer jsonSerializer;
特定のPOF構成のPOFシリアライザ

@Name修飾子と@ConfigUri修飾子の両方を使用してPOFシリアライザを注入し、特定のPOF構成ファイルを使用するPOFシリアライザを注入できます。

import com.oracle.coherence.cdi.ConfigUri;
import com.oracle.coherence.cdi.Name;
import javax.inject.Inject;

@Inject
@Name("pof")
@ConfigUri("test-pof-config.xml")
private Serializer pofSerializer;

前述のコードは、test-pof-config.xmlファイルを構成ファイルとして使用するPOFシリアライザを注入します。

Coherence管理対象オブジェクトへのCDI Beanの注入

Coherenceには、多くのサーバー側拡張ポイントがあり、通常はキャッシュ構成ファイルの様々なセクション内で拡張を構成することで、様々な方法でアプリケーションの動作をカスタマイズできます。たとえば、イベント・インターセプタおよびキャッシュ・ストアを実装して、サーバー側のイベントを処理し、外部データ・ストアおよびその他のサービスと統合できます。

Coherence CDIは、カスタム構成ネームスペース・ハンドラを使用して、名前付きCDI Beanをこれらの拡張ポイントに注入する方法を提供します。

<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
        xmlns:cdi="class://com.oracle.coherence.cdi.server.CdiNamespaceHandler"
        xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">

前述のcdiネームスペースのハンドラを宣言した後、通常<class-name>または<class-factory-name>要素を使用する任意の場所に<cdi:bean>要素を指定できます。

<?xml version="1.0"?>

<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
        xmlns:cdi="class://com.oracle.coherence.cdi.server.CdiNamespaceHandler"
        xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config coherence-cache-config.xsd">

    <interceptors>
        <interceptor>
            <instance>
                <cdi:bean>registrationListener</cdi:bean>
            </instance>
        </interceptor>
        <interceptor>
            <instance>
                <cdi:bean>activationListener</cdi:bean>
            </instance>
        </interceptor>
    </interceptors>

    <caching-scheme-mapping>
        <cache-mapping>
            <cache-name>*</cache-name>
            <scheme-name>distributed-scheme</scheme-name>
            <interceptors>
                <interceptor>
                    <instance>
                        <cdi:bean>cacheListener</cdi:bean>
                    </instance>
                </interceptor>
            </interceptors>
        </cache-mapping>
    </caching-scheme-mapping>

    <caching-schemes>
        <distributed-scheme>
            <scheme-name>distributed-scheme</scheme-name>
            <service-name>PartitionedCache</service-name>
            <local-storage system-property="coherence.distributed.localstorage">true</local-storage>
            <partition-listener>
                <cdi:bean>partitionListener</cdi:bean>
            </partition-listener>
            <member-listener>
                <cdi:bean>memberListener</cdi:bean>
            </member-listener>
            <backing-map-scheme>
                <local-scheme/>
            </backing-map-scheme>
            <autostart>true</autostart>
            <interceptors>
                <interceptor>
                    <instance>
                        <cdi:bean>storageListener</cdi:bean>
                    </instance>
                </interceptor>
            </interceptors>
        </distributed-scheme>
    </caching-schemes>
</cache-config>

<cdi:bean>要素を使用して、名前付きCDI Bean (明示的な@Named注釈を持つBean)を注入できます。たとえば、前述のcacheListenerインターセプタBeanは次のようになります。

@ApplicationScoped
@Named("cacheListener")
@EntryEvents(INSERTING)
public class MyCacheListener
        implements EventInterceptor<EntryEvent<Long, String>> {
    @Override
    public void onEvent(EntryEvent<Long, String> e) {
        // handle INSERTING event
    }
}

また、注入できるのは@ApplicationScoped Beanのみであることに注意してください。それは共有される可能性があることを意味します。たとえば、例のキャッシュ・マッピング内のキャッシュ名としてワイルドカード*が使用されているため、cacheListenerの同じインスタンスは、複数のキャッシュからイベントを受け取ります。

イベント自体は、キャッシュ名や発生元のサービスなど、発生したコンテキストの詳細を提供するため、通常はこれで問題ありません。ただし、リスナー・クラス内に存在する可能性がある共有状態は、コンテキスト固有ではなく、複数のスレッドからの同時アクセスに対して安全である必要があることを意味します。後者を保証できない場合は、onEventメソッドをsynchronizedとして宣言して、一度に1つのスレッドのみが共有状態にアクセスできるようにします。

この項には次のトピックが含まれます:

CDIオブザーバを使用したCoherenceサーバー側イベントの処理

前述の例では、Coherence EventInterceptorをCDI Beanとして実装し、キャッシュ構成ファイル内の<cdi:bean>要素を使用して登録できることを示していますが、Coherence CDIでは、標準のCDIイベントおよびオブザーバを使用して同じ目的を達成する、より簡単な方法も提供しています。

たとえば、peopleという名前でLong型のキーとPerson型の値を使用してNamedMapによって発生したイベントを監視するには、次のようなCDIオブザーバを定義します。

private void onMapChange(@Observes @MapName("people") EntryEvent<Long, Person> event) {
    // handle all events raised by the 'people' map/cache
}

この項には次のトピックが含まれます:

特定のイベント・タイプの監視

@Observesメソッド(CDIオブザーバを使用したCoherenceサーバー側イベントの処理を参照)は、peopleマップのすべてのイベントを受け取りますが、イベント修飾子を使用して受け取るイベントのタイプを制御することもできます。

private void onUpdate(@Observes @Updated @MapName("people") EntryEvent<Long, Person> event) {
    // handle UPDATED events raised by the 'people' map/cache
}

private void onChange(@Observes @Inserted @Updated @Removed @MapName("people") EntryEvent<?, ?> event) {
    // handle INSERTED, UPDATED and REMOVED events raised by the 'people' map/cache
}
監視対象イベントのフィルタリング

監視されるイベントは、Coherence Filterを使用してさらに制限できます。フィルタが指定されている場合、イベントはサーバー上でフィルタされ、クライアントには送信されません。フィルタは、それ自体に@FilterBindingの注釈が付いた修飾子注釈を使用して指定されます。

カスタムFilterBindingを実装する(推奨。FilterBinding注釈の使用を参照)か、CohQLを使用してフィルタを指定できる組込みの@WhereFilterを便宜上使用できます。

たとえば、peopleマップのすべてのイベント・タイプを受け取り、lastNameプロパティ値がSmithPeopleについてのみを受け取るには、組込みの@WhereFilter注釈を使用できます。

@WhereFilter("lastName = 'Smith'")
private void onMapChange(@Observes @MapName("people") EntryEvent<Long, Person> event) {
    // handle all events raised by the 'people' map/cache
}
監視対象イベントの変換

イベント・オブザーバがイベントの完全なキャッシュやマップ値を受け取りたくない場合は、イベントを別の値に変換して監視できます。これは、ExtractorBinding注釈またはMapEventTransformerBinding注釈を使用してオブザーバ・メソッドに適用されるMapEventTransformerを使用して実現されます。イベントの変換はサーバー上で行われるため、完全な古い値と新しい値で元のイベントを受け取る必要がなく、オブザーバをより効率的にすることができます。

この項には次のトピックが含まれます:

ExtractorBinding注釈を使用したイベントの変換

ExtractorBinding注釈は、Coherence ValueExtractorを表す注釈です。オブザーバ・メソッドにExtractorBinding注釈を付けると、結果のValueExtractorがイベントの値に適用され、抽出されたプロパティのみを含む新しいイベントがオブザーバに返されます。

たとえば、peopleという名前のマップからのイベントを監視しているが、姓のみを必要とするイベント・オブザーバは、組込みの@PropertyExtractor注釈を使用できます。

@PropertyExtractor("lastName")
private void onMapChange(@Observes @MapName("people") EntryEvent<Long, String> event) {
    // handle all events raised by the 'people' map/cache
}

前述の例ではタイプEntryEvent<Long, Person>のイベントを受け取りましたが、このメソッドはタイプEntryEvent<Long, String>のイベントを受け取ります。これは、プロパティ・エクストラクタが元のイベントのPerson値に適用されてlastNameプロパティが抽出され、String値を使用して新しいイベントが作成されるためです。

多数の組込みExtractorBinding注釈があり、カスタムExtractorBinding注釈を作成することもできます。カスタム・エクストラクタ注釈の作成を参照してください。

複数のエクストラクタ・バインディング注釈を注入ポイントに追加できます。この場合、複数のプロパティが抽出され、イベントには抽出されたプロパティ値のListが含まれます。

たとえば、Personcityフィールドを含むAddress型のaddressフィールドも含まれている場合、これは@ChainedExtractor注釈を使用して抽出できます。これを前の例の@PropertyExtractorと組み合せることで、イベントでlastNamecityの両方を監視できます。

@PropertyExtractor("lastName")
@ChainedExtractor({"address", "city"})
private void onMapChange(@Observes @MapName("people") EntryEvent<Long, List<String>> event) {
    // handle all events raised by the 'people' map/cache
}

ノート:

複数の抽出値が返されるため、イベントのタイプはEntryEvent<Long, List<String>>になります。イベント値はListで、この場合は両方のプロパティのタイプがStringであるため、値はList<String>です。
MapEventTransformerBinding注釈を使用したイベントの変換

イベント値からプロパティを抽出するよりも複雑なイベント変換が必要な場合は、カスタムMapEventTransformerインスタンスを生成するカスタムMapEventTransformerBindingを作成し、このインスタンスがオブザーバのイベントに適用されます。カスタム・エクストラクタ注釈の作成を参照してください。

特定のサービスおよびスコープでのマップおよびキャッシュのイベントの監視

@MapName修飾子に加えて、受け取るイベントを制限する方法として@ServiceNameおよび@ScopeName修飾子を使用することもできます。

前述の例では、EntryEventsの処理方法のみを示していますが、他のすべてのサーバー側イベント・タイプにも同じことが当てはまります。

private void onActivated(@Observes @Activated LifecycleEvent event) {
    // handle cache factory activation
}

private void onCreatedPeople(@Observes @Created @MapName("people") CacheLifecycleEvent event) {
    // handle creation of the 'people' map/cache
}

private void onExecuted(@Observes @Executed @MapName("people") @Processor(Uppercase.class) EntryProcessorEvent event) {
    // intercept 'Uppercase` entry processor execution against 'people' map/cache
}
非同期オブザーバの使用

前述のすべての例では、各オブザーバ・メソッドに@Observes修飾子を指定して、同期オブザーバを使用していました。ただし、Coherence CDIでは非同期CDIオブザーバも完全にサポートされます。前述の例のいずれかで、@Observes@ObservesAsyncに置き換えるだけで済みます。

private void onActivated(@ObservesAsync @Activated LifecycleEvent event) {
    // handle cache factory activation
}

private void onCreatedPeople(@ObservesAsync @Created @MapName("people") CacheLifecycleEvent event) {
    // handle creation of the 'people' map/cache
}

private void onExecuted(@ObservesAsync @Executed @MapName("people") @Processor(Uppercase.class) EntryProcessorEvent event) {
    // intercept 'Uppercase` entry processor execution against 'people', map/cache
}

ノート:

Coherenceイベントは、コミット前イベントとコミット後イベントの2つのカテゴリに分類されます。InsertingUpdatingRemovingExecutingなど、名前が"ing"で終わるすべてのイベントは、コミット前イベントです。これらのイベントは、データを変更したり、例外をスローして操作を拒否できますが、そのためには、イベントをトリガーした操作を実行しているものと同じスレッドで実行されるように、イベントを同期する必要があります。

つまり、非同期CDIオブザーバを使用して監視できますが、イベント・ペイロードの一部であるエントリのセットを変更したり、例外をスローしてイベントを拒否する場合は、同期CDIオブザーバを使用する必要があります。

一時オブジェクトへのCDI Beanの注入

CDIを使用してCoherenceオブジェクトをアプリケーション・クラスに注入し、CDI BeanをCoherence管理対象オブジェクトに注入することで、依存関係インジェクションが役立つ可能性のある多くのユース・ケースをサポートできますが、Coherenceに固有の重要なユース・ケースには対応していません。

Coherenceは分散システムであり、シリアライズを使用して、データと処理リクエストの両方をクラスタ・メンバー(またはリモート・クライアント)から別のクラスタ・メンバー(またはリモート・クライアント)に送信し、データをメモリーとディスクの両方に格納します。

エントリ・プロセッサやアグリゲータなどの処理リクエストは、実行するターゲット・クラスタ・メンバーでデシリアライズする必要があります。場合によっては、サービス・ルックアップを回避するために、依存関係インジェクションのメリットが得られることがあります。

同様に、データはシリアライズされたバイナリ形式で格納されますが、エントリ・プロセッサやアグリゲータの実行時など、サーバー側の処理のためにユーザー指定のクラスにデシリアライズする必要がある場合があります。この場合、データ・クラスは、(たとえば、ドメイン駆動設計(DDD)をサポートするために)依存関係インジェクションのメリットが得られることも多くあります。

これらの一時オブジェクトはCDIコンテナによって管理されませんが、Coherence CDIはデシリアライズ時に注入をサポートします。ただし、パフォーマンス上の理由から、com.oracle.coherence.cdi.Injectableインタフェースを実装して明示的にオプトインする必要があります。

この項には次のトピックが含まれます:

一時クラスを注入可能にする

技術的には真のマーカー・インタフェースではありませんが、Injectableはすべての意図および目的に対してそのように扱うことができます。デシリアライズ時に注入を開始するには、クラスのimplements句に追加するだけで済みます。

public class InjectableBean
        implements Injectable, Serializable {

    @Inject
    private Converter<String, String> converter;

    private String text;

    InjectableBean() {
    }

    InjectableBean(String text) {
        this.text = text;
    }

    String getConvertedText() {
        return converter.convert(text);
    }
}

アプリケーションに次のConverterサービス実装があると仮定すると、デシリアライズ時にInjectableBeanに注入され、getConvertedTextメソッドは大文字に変換されたテキスト・フィールドの値を返します。

@ApplicationScoped
public class ToUpperConverter
        implements Converter<String, String> {
    @Override
    public String convert(String s) {
        return s.toUpperCase();
    }
}

ノート:

Injectableクラスに@PostConstructコールバック・メソッドがある場合は、注入後にコールされます。ただし、その時点以降はオブジェクトのライフサイクルを制御できないため、@PreDestroyコールバックは呼び出されません。

この機能は、シリアライズ形式に依存せず、JavaシリアライズとPOFシリアライズの両方(またはその他のカスタム・シリアライザ)、およびCoherenceメンバー(またはリモート・クライアント)でデシリアライズされたオブジェクトに対して機能します。

デシリアライズされた一時オブジェクトは真のCDIマネージドBeanではありませんが、デシリアライズ時にCDIマネージド依存関係をこれらのオブジェクトに注入できると、それらのアプリケーション・コンポーネントで必要となるほとんどの依存関係インジェクション要件を満たす可能性があります。

FilterBinding注釈の使用

ビューの作成時またはイベントのサブスクライブ時に、Filtersを使用してビューまたはイベントを変更できます。注入される正確なFilter実装は、ビューまたはイベント・オブザーバの修飾子によって決定されます。具体的には、それ自体に@FilterBindingの注釈が付いた修飾子注釈です。

たとえば、基礎となるマップのフィルタされたビューであるビューの注入ポイントがあるが、必要なフィルタが組込みの修飾子で提供されるものよりも複雑である場合、またはカスタム・フィルタ実装である場合は、次のステップを実行します。

  • 必要なFilterを表すカスタム注釈クラスを作成します。
  • カスタムFilterのインスタンスを生成するためのファクトリとなるカスタム注釈が付いたcom.oracle.coherence.cdi.FilterFactoryを実装するBeanクラスを作成します。
  • ビューの注入ポイントにカスタム注釈を付けます。

この項には次のトピックが含まれます:

カスタム・フィルタ注釈の作成

フィルタ注釈の作成は、@com.oracle.coherence.cdi.FilterBinding注釈が付いた通常のJava注釈クラスの作成と同じです。

次の例では、Coherence CDI拡張がFilterを表すことを認識できるように、この新しい注釈にFilterBindingを付けることが最も重要な部分です。

@Inherited
@FilterBinding  
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomFilter {
}

カスタム・フィルタ・ファクトリの作成

カスタム注釈を作成した後、必要なFilterのインスタンスを生成するFilterFactories実装を作成できます。

@ApplicationScoped    
@CustomFilter         
static class CustomFilterSupplier
        implements FilterFactory<CustomFilter, Object>
    {
    @Override
    public Filter<Object> create(CustomFilter annotation)
        {
        return new CustomComplexFilter(); 
        }
    }
このサンプルで注目すべき点は以下のとおりです。
  • CDIで検出できるように、CustomFilterSupplierクラスに@ApplicationScopedという注釈が付けられています。
  • CustomFilterSupplierクラスには、Filtersを作成する必要があるときにCoherence CDI拡張で検出できるように、新しいフィルタ・バインディング注釈@CustomFilterが付けられています。
  • CustomFilterSupplierは、カスタムFilter実装を作成するFilterFactoriesインタフェースのcreateメソッドを実装します。

注入ポイントへの注釈付け

カスタム注釈と注釈付きFilterFactoriesの両方があるため、Filterを必要とする注入ポイントに新しい注釈を付けることができます。

@Inject
@View
@CustomFilter
private NamedMap<Long, Person> people;

ビューと同様に、カスタム・フィルタ・バインディング注釈をイベント・オブザーバに使用できます。たとえば、同じカスタムFilterに一致するイベントのみを受け取るイベント・オブザーバ・メソッドがある場合、メソッドに同じカスタム・フィルタ注釈を付けることができます。

@CustomFilter
private void onPerson(@Observes @MapName("people") EntryEvent<Long, Person> event) {

ExtractorBinding注釈の使用

エクストラクタ・バインディングは、@ExtractorBindingの注釈が付けられ、com.oracle.coherence.cdi.ExtractorFactoryの実装と組み合せて使用され、Coherence ValueExtractorインスタンスが生成されます。

Coherence CDIモジュールには多数の組込みのエクストラクタ・バインディング注釈があり、カスタム実装を提供する簡単なプロセスです。

この項には次のトピックが含まれます:

組込みExtractorBinding注釈の使用

次のタイプの組込みExtractorBinding注釈を使用できます。

PropertyExtractor

@PropertyExtractor注釈を使用して、オブジェクトから名前付きプロパティを抽出するエクストラクタを取得できます。@PropertyExtractor注釈の値フィールドは、抽出するプロパティの名前です。

たとえば、次の@PropertyExtractor注釈は、値からlastNameプロパティを抽出するValueExtractorを表します。

@PropertyExtractor("lastName")
生成されるエクストラクタはcom.tangosol.util.extractor.UniversalExtractorのインスタンスであるため、この例は次をコールすることと同じです。
new UniversalExtractor("lastName");

@PropertyExtractor注釈を複数回適用して、値からプロパティのListを抽出するMultiExtractorを作成できます。

たとえば、マップ値がfirstNameおよびlastNameプロパティを持つPersonのインスタンスであるpeopleという名前のマップを考えてみます。次のイベント・オブザーバはマップ上のイベントを監視しますが、受け取ったイベントにはイベント・キーと、元のイベントから抽出されたfirstNameおよびlastNameを含むListのみが含まれます。

@PropertyExtractor("firstName")
@PropertyExtractor("lastName")
private void onPerson(@Observes @MapName("people") EntryEvent<Long, List<String>> event) {

ChainedExtractor

@ChainedExtractor注釈を使用して、プロパティの連鎖を抽出できます。

たとえば、Personインスタンスには、cityプロパティを含むaddressプロパティを含めることができます。@ChainedExtractorは抽出するフィールドの連鎖を取得し、この場合はPersonからaddressを抽出し、addressからcityを抽出します。

@ChainedExtractor("address", "city")

各プロパティ名はUniversalExtractorの作成に使用され、これらのエクストラクタの配列はcom.tangosol.util.extractor.ChainedExtractorのインスタンスの作成に使用されます。

前述の例は、次をコールすることと同じです。

UniversalExtractor[] chain = new UniversalExtractor[] {
        new UniversalExtractor("address"),
        new UniversalExtractor("city")
};
ChainedExtractor extractor = new ChainedExtractor(chain);

PofExtractor

@PofExtractor注釈を使用して、POFでエンコードされた値からプロパティを抽出できるエクストラクタを生成できます。@PofExtractor注釈に渡される値は、抽出するプロパティに移動するためのPOFパスです。

たとえば、索引4lastNameプロパティを持つPOFを使用してPerson値をシリアライズした場合は、次に示すように@PofExtractor注釈を使用できます。

@PofExtractor(index = 4)

前述のコードは、Coherence com.tangosol.util.extractor.PofExtractorを作成します。これは、次をコールすることと同じです。

com.tangosol.util.extractor.PofExtractor(null, 4);

場合によっては(たとえば、特定のタイプのNumberを処理する場合)、PofExtractorは抽出されるタイプを認識している必要があります。この場合、@PofExtractor注釈でタイプ値を設定できます。

たとえば、POF索引2Book値にLong型のsalesフィールドがある場合、次の@PofExtractor注釈を使用してsalesフィールドを抽出できます。

@PofExtractor(index = {2}, type = Long.class)

前述のコードは、Coherence com.tangosol.util.extractor.PofExtractorを作成します。これは、次をコールすることと同じです。

com.tangosol.util.extractor.PofExtractor(Long.class, 2);

@PofExtractor注釈のindex値はint配列であるため、複数のPOF索引値を渡して、抽出するプロパティの連鎖を下に移動できます。たとえば、PersonにPOF索引5Addressが含まれ、AddressにPOF索引3cityプロパティが含まれている場合、次に示すように、@PofExtractor注釈を使用してPersonから抽出された市区町村を抽出できます。

@PofExtractor(index = {5, 3})

または、抽出元の値がcom.tangosol.io.pof.schema.annotation.PortableTypeで注釈が付けられ、クラスのPOFシリアライズ・コードがCoherence com.tangosol.io.pof.generator.PortableTypeGeneratorを使用して生成されている場合は、pathフィールドを使用して@PofExtractor注釈にプロパティ名を渡すことができます。

たとえば、POFシリアライズされたPersonからlastNameフィールドを抽出するには、次に示すように@PofExtractor注釈を使用できます。

@PofExtractor(path = "lastName")

address cityの例は次のとおりです。

@PofExtractor(path = {"address", "city"})

Book salesの例は次のとおりです。

@PofExtractor(path = "sales", type Long.class)

カスタムExtractorBinding注釈の使用

組込みのエクストラクタ・バインディングが適切でない場合、またはカスタムのValueExtractor実装が必要な場合、対応するcom.oracle.coherence.cdi.ExtractorFactory実装を使用してカスタム・エクストラクタ・バインディング注釈を作成できます。

カスタム・エクストラクタを作成するには、次のステップを実行します。

  • 必要なValueExtractorを表すカスタム注釈クラスを作成します。
  • カスタムValueExtractorのインスタンスを生成するためのファクトリとなるカスタム注釈が付いたcom.oracle.coherence.cdi.ExtractorFactoryを実装するBeanクラスを作成します。
  • ビューの注入ポイントにカスタム注釈を付けます。

カスタム・エクストラクタ注釈の作成

エクストラクタ注釈の作成は、@com.oracle.coherence.cdi.ExtractorBinding注釈が付いた通常のJava注釈クラスの作成と同じです。

Coherence CDI拡張がValueExtractorを表すことを認識できるように、次の新しい注釈にExtractorBindingを付けることが最も重要な部分です。

@Inherited
@ExtractorBinding  
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomExtractor {
}

カスタム・エクストラクタ・ファクトリの作成

カスタム注釈を作成した後、必要なValueExtractorのインスタンスを生成するExtractorFactory実装を作成できます。

@ApplicationScoped
@CustomExtractor      
static class CustomExtractorSupplier
        implements ExtractorFactory<CustomExtractor, Object, Object>
    {
    @Override
    public ValueExtractor<Object, Object> create(CustomExtractor annotation)
        {
        return new CustomComplexExtractor(); 
        }
    }
このサンプルで注目すべき点は以下のとおりです。
  • CDIで検出できるように、CustomExtractorSupplierクラスに@ApplicationScopedという注釈が付けられています。
  • CustomExtractorSupplierクラスには、ValueExtractorインスタンスを作成する必要があるときにCoherence CDI拡張で検出できるように、新しいエクストラクタ・バインディング注釈@CustomExtractorが付けられています。
  • CustomExtractorSupplierは、カスタムValueExtractor実装を作成するExtractorFactoryインタフェースのcreateメソッドを実装します。

注入ポイントへの注釈付け

カスタム注釈と注釈付きExtractorFactoryの両方があるため、ValueExtractorを必要とする注入ポイントに新しい注釈を付けることができます。

@Inject
@View
@CustomExtractor
private NamedMap<Long, String> people;

ビューと同様に、カスタム・フィルタ・バインディング注釈をイベント・オブザーバに使用できます。たとえば、カスタム・エクストラクタを使用してイベントを変換する変換済イベントのみを受け取るイベント・オブザーバ・メソッドがある場合、次のようにカスタム・フィルタ・バインディング注釈を使用できます。

@CustomExtractor
private void onPerson(@Observes @MapName("people") EntryEvent<Long, String> event) {

MapEventTransformerBinding注釈の使用

Coherence CDIでは、キャッシュ・エントリまたはマップ・エントリのイベントを監視できるイベント・オブザーバがサポートされています。observerメソッドにMapEventTransformerBinding注釈を付けて、オブザーバが監視前に元のイベントにトランスフォーマを適用する必要があることを示すことができます。

イベントの詳細は、CDIオブザーバを使用したCoherenceサーバー側イベントの処理を参照してください。

組込みのMapEventTransformerBinding注釈はありません。この機能は、カスタムのMapEventTransformer実装の使用をサポートします。

MapEventTransformerBinding注釈を作成して使用するには、次のステップを実行します。

  • 必要なMapEventTransformerを表すカスタム注釈クラスを作成します。
  • カスタムMapEventTransformerのインスタンスを生成するためのファクトリとなるカスタム注釈が付いたcom.oracle.coherence.cdi.MapEventTransformerFactoryを実装するBeanクラスを作成します。
  • ビューの注入ポイントにカスタム注釈を付けます。

この項には次のトピックが含まれます:

カスタム・エクストラクタ注釈の作成

エクストラクタ注釈の作成は、@com.oracle.coherence.cdi.MapEventTransformerBinding注釈が付いた通常のJava注釈クラスの作成と同じです。

Coherence CDI拡張がMapEventTransformerを表すことを認識できるように、次の新しい注釈にMapEventTransformerBindingを付けることが最も重要な部分です。

@Inherited
@MapEventTransformerBinding  
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomTransformer {
}

カスタム・エクストラクタ・ファクトリの作成

カスタム注釈を作成した後、必要なMapEventTransformerのインスタンスを生成するMapEventTransformerFactory実装を作成できます。

@ApplicationScoped
@CustomTransformer      
static class CustomExtractorSupplier
        implements ExtractorFactory<CustomExtractor, Object, Object>
    {
    @Override
    public ValueExtractor<Object, Object> create(CustomExtractor annotation)
        {
        return new CustomComplexExtractor(); 
        }
    }
このサンプルで注目すべき点は以下のとおりです。
  • CDIで検出できるように、CustomTransformerSupplierクラスに@ApplicationScopedという注釈が付けられています。
  • CustomTransformerSupplierクラスには、MapEventTransformerインスタンスを作成する必要があるときにCoherence CDI拡張で検出できるように、新しいエクストラクタ・バインディング注釈CustomTransformerが付けられています。
  • CustomTransformerSupplierは、カスタムMapEventTransformer実装を作成するMapEventTransformerFactoryインタフェースのcreateメソッドを実装します。

注入ポイントへの注釈付け

カスタム注釈と注釈付きMapEventTransformerFactoryの両方があるため、MapEventTransformerを必要とするオブザーバ・メソッドに新しい注釈を付けることができます。

@CustomTransformer
private void onPerson(@Observes @MapName("people") EntryEvent<Long, String> event) {

CDIレスポンス・キャッシングの使用

CDIレスポンス・キャッシングを使用すると、Javaメソッドに透過的にキャッシュを適用できます。coherence-CDI依存関係を追加すると、CDIレスポンス・キャッシングが有効になります。

プロジェクトのpom.xmlファイルで、次に示すようにcoherence-cdiを依存関係として宣言します:
<dependency>
    <groupId>${coherence.groupId}</groupId>
    <artifactId>coherence-cdi</artifactId>
    <version>${coherence.version}</version>
</dependency>

ノート:

この機能は、累積パッチ更新(CPU) 35122413以降をインストールした後でのみ使用できます。

この項には次のトピックが含まれます:

レスポンス・キャッシング注釈

レスポンス・キャッシングに使用される特定のキャッシュは、クラスまたはメソッドの@CacheNameおよび@SessionName注釈で宣言できます。

次のレスポンス・キャッシング注釈がサポートされています:

@CacheAdd

@CacheAdd注釈でマークされたメソッドは、常に起動され、その実行結果はキャッシュに格納されます。キーは、すべてのパラメータの値で構成されます(このケースでは、文字列パラメータnameのみ)。

@Path("{name}")
@POST
@CacheAdd
@CacheName("messages")
public Message addMessage(@PathParam("name") String name)
    {
    return new Message("Hello " + name);
    }
@CacheGet

戻り値がキャッシュに存在する場合は、フェッチされて返されます。それ以外の場合は、ターゲット・メソッドが呼び出され、呼出し結果がキャッシュに格納されてから、コール元に返されます。

@Path("{name}")
@GET
@CacheGet
@CacheName("messages")
public Message getMessage(@PathParam("name") String name)
    {
    return new Message("Hello " + name);
    }
@CachePut

@CacheValue注釈付きパラメータの値はキャッシュに格納され、ターゲット・メソッドが呼び出され、呼出し結果がコール元に返されます。

この例では、渡されたメッセージは、値がnameパラメータとして渡されたキーのキャッシュに格納されます。

@Path("{name}")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@CachePut
@CacheName("messages")
public Response putMessage(@CacheKey @PathParam("name") String name,
                           @CacheValue Message message)
    {
    return Response.status(Response.Status.CREATED).build();
    }
@CacheRemove

この注釈は、キャッシュからキーを削除し、メソッド呼出しの結果を返します。

この例では、nameパラメータとして値が渡されたキーがキャッシュから削除されます。

    
@Path("{name}")
@DELETE
@CacheRemove
public Response removeMessage(@PathParam("name") String name)
    {
    return Response.ok().build();
    }
@CacheKey

キャッシュ・キーは、@CacheValue注釈で明示的に注釈付けされていないすべてのパラメータの値から組み立てられます。1つ以上のパラメータに@CacheKey注釈が付けられている場合、それらのパラメータのみがキーの作成に使用されます。

この例では、lastNameおよびfirstNameパラメータの値のみがキャッシュ・キーの作成に使用されます。

@Path("{lastName}/{firstName}")
@GET
@CacheGet
public Message getMessage(@PathParam("lastName") @CacheKey String lastName,
                          @PathParam("firstName") @CacheKey String firstName,
                          @HeaderParam("Accept-Language") String acceptLanguage)
    {
    return new Message("Hello " + firstName + " " + lastName);
    }