JakartaEEのシングルトンセッションBean
1. 概要
特定のユースケースでSessionBeanの単一インスタンスが必要な場合は常に、SingletonSessionBeanを使用できます。
このチュートリアルでは、Jakarta EEアプリケーションを使用して、例を通じてこれを調査します。
2. Maven
まず、pom.xmlで必要なMaven依存関係を定義する必要があります。
EJBをデプロイするためのEJBAPIと組み込みEJBコンテナの依存関係を定義しましょう。
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>tomee-embedded</artifactId>
<version>1.7.5</version>
</dependency>
最新バージョンは、MavenCentralのJavaEEAPIおよびtomEEにあります。
3. セッションBeanの種類
セッションBeanには3つのタイプがあります。 シングルトンセッションBeanについて説明する前に、3つのタイプのライフサイクルの違いを見てみましょう。
3.1. ステートフルセッションBean
ステートフルセッションBeanは、通信しているクライアントとの会話状態を維持します。
各クライアントはStatefulBeanの新しいインスタンスを作成し、他のクライアントと共有されません。
クライアントとBeanの間の通信が終了すると、SessionBeanも終了します。
3.2. ステートレスセッションBean
ステートレスセッションBeanは、クライアントとの会話状態を維持しません。 Beanには、メソッド呼び出しの期間までのみ、クライアントに固有の状態が含まれます。
ステートフルセッションBeanとは異なり、連続するメソッド呼び出しは独立しています。
コンテナはステートレスBeanのプールを維持し、これらのインスタンスは複数のクライアント間で共有できます。
3.3. シングルトンセッションBean
シングルトンセッションBeanは、アプリケーションのライフサイクル全体にわたってBeanの状態を維持します。
シングルトンセッションBeanはステートレスセッションBeanに似ていますが、アプリケーション全体でシングルトンセッションBeanのインスタンスが1つだけ作成され、アプリケーションがシャットダウンされるまで終了しません。
Beanの単一インスタンスは複数のクライアント間で共有され、同時にアクセスできます。
4. シングルトンセッションBeanの作成
そのためのインターフェースを作成することから始めましょう。
この例では、javax.ejb.Localアノテーションを使用してインターフェースを定義しましょう。
@Local
public interface CountryState {
List<String> getStates(String country);
void setStates(String country, List<String> states);
}
@Local を使用するということは、同じアプリケーション内でBeanにアクセスすることを意味します。 javax.ejb.Remote アノテーションを使用するオプションもあります。これにより、EJBをリモートで呼び出すことができます。
次に、実装EJBbeanクラスを定義します。 アノテーションjavax.ejb.Singleton を使用して、クラスをシングルトンセッションBeanとしてマークします。
さらに、beanにjavax .ejb.Startup アノテーションを付けて、EJBコンテナに起動時にbeanを初期化するように通知しましょう。
@Singleton
@Startup
public class CountryStateContainerManagedBean implements CountryState {
...
}
これは、熱心な初期化と呼ばれます。 @Startup を使用しない場合、EJBコンテナはbeanを初期化するタイミングを決定します。
複数のセッションBeanを定義して、データを初期化し、特定の順序でBeanをロードすることもできます。 したがって、 javax.ejb.DependsOn アノテーションを使用して、beanの他のセッションBeanへの依存関係を定義します。
@DependsOn アノテーションの値は、Beanが依存するBeanクラス名の名前の配列です。
@Singleton
@Startup
@DependsOn({"DependentBean1", "DependentBean2"})
public class CountryStateCacheBean implements CountryState {
...
}
beanを初期化するinitialize()メソッドを定義し、javax.annotation.PostConstructアノテーションを使用してライフサイクルコールバックメソッドにします。
このアノテーションを使用すると、beanのインスタンス化時にコンテナによって呼び出されます。
@PostConstruct
public void initialize() {
List<String> states = new ArrayList<String>();
states.add("Texas");
states.add("Alabama");
states.add("Alaska");
states.add("Arizona");
states.add("Arkansas");
countryStatesMap.put("UnitedStates", states);
}
5. 並行性
次に、シングルトンセッションBeanの同時実行管理を設計します。 EJBは、シングルトンセッションBeanへの同時アクセスを実装するための2つの方法を提供します。コンテナ管理の同時実行とBean管理の同時実行です。
アノテーションjavax.ejb.ConcurrencyManagementは、メソッドの同時実行ポリシーを定義します。 デフォルトでは、EJBコンテナはコンテナ管理の同時実行性を使用します。
@ConcurrencyManagement アノテーションは、javax.ejb.ConcurrencyManagementType値を取ります。 オプションは次のとおりです。
- ConcurrencyManagementType.CONTAINER は、コンテナー管理の同時実行性です。
- Bean管理の同時実行の場合はConcurrencyManagementType.BEAN。
5.1. コンテナ管理の同時実行性
簡単に言えば、コンテナー管理の並行性では、コンテナーはクライアントのメソッドへのアクセス方法を制御します。
@ConcurrencyManagementアノテーションを値javax.ejb.ConcurrencyManagementType.CONTAINERで使用してみましょう。
@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class CountryStateContainerManagedBean implements CountryState {
...
}
シングルトンの各ビジネスメソッドへのアクセスレベルを指定するには、javax.ejb.Lockアノテーションを使用します。 javax.ejb.LockType には、@Lockアノテーションの値が含まれています。javax.ejb.LockTypeは、次の2つの値を定義します。
- LockType.WRITE –この値は、呼び出し元のクライアントに排他ロックを提供し、他のすべてのクライアントがBeanのすべてのメソッドにアクセスできないようにします。 シングルトンBeanの状態を変更するメソッドにこれを使用します。
- LockType.READ – この値は、メソッドにアクセスするために複数のクライアントに同時ロックを提供します。 Beanからデータを読み取るだけのメソッドにこれを使用します。
これを念頭に置いて、 setStates()メソッドを @Lock(LockType.WRITE)アノテーションで定義し、クライアントによる状態の同時更新を防ぎます。
クライアントがデータを同時に読み取れるようにするために、 getStates()に @Lock(LockType.READ)という注釈を付けます。
@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class CountryStateContainerManagedBean implements CountryState {
private final Map<String, List<String> countryStatesMap = new HashMap<>();
@Lock(LockType.READ)
public List<String> getStates(String country) {
return countryStatesMap.get(country);
}
@Lock(LockType.WRITE)
public void setStates(String country, List<String> states) {
countryStatesMap.put(country, states);
}
}
メソッドの実行を長時間停止し、他のクライアントを無期限にブロックするには、 javax.ejb.AccessTimeout アノテーションを使用して、待機中の呼び出しをタイムアウトします。
@AccessTimeout アノテーションを使用して、メソッドのタイムアウトのミリ秒数を定義します。 タイムアウト後、コンテナは javax.ejb.ConcurrentAccessTimeoutException をスローし、メソッドの実行は終了します。
5.2. Bean管理の同時実行性
Beanマネージド同時実行では、コンテナーはクライアントによるシングルトンセッションBeanの同時アクセスを制御しません。 開発者は、自分で並行性を実装する必要があります。
開発者が並行性を実装しない限り、すべてのメソッドにすべてのクライアントが同時にアクセスできます。 Javaは、並行性を実装するためのSynchronizationおよびvolatileプリミティブを提供します。
並行性の詳細については、 java.util.concurrent hereおよびAtomicVariableshereを参照してください。
Bean管理の同時実行性の場合、シングルトンセッションBeanクラスのjavax.ejb.ConcurrencyManagementType.BEAN値を使用して@ConcurrencyManagementアノテーションを定義しましょう。
@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class CountryStateBeanManagedBean implements CountryState {
...
}
次に、 synchronizedキーワードを使用してbeanの状態を変更するsetStates()メソッドを記述します。
public synchronized void setStates(String country, List<String> states) {
countryStatesMap.put(country, states);
}
synchronized キーワードを使用すると、一度に1つのスレッドのみがメソッドにアクセスできます。
getStates()メソッドはBeanの状態を変更しないため、synchronizedキーワードを使用する必要はありません。
6. クライアント
これで、シングルトンセッションBeanにアクセスするためのクライアントを作成できます。
セッションBeanは、JBoss、Glassfishなどのアプリケーションコンテナサーバーにデプロイできます。 簡単にするために、javax .ejb.embedded.EJBContainerクラスを使用します。 EJBContainer は、クライアントと同じJVMで実行され、エンタープライズBeanコンテナーのほとんどのサービスを提供します。
まず、EJBContainerのインスタンスを作成します。 このコンテナインスタンスは、クラスパスに存在するすべてのEJBモジュールを検索して初期化します。
public class CountryStateCacheBeanTest {
private EJBContainer ejbContainer = null;
private Context context = null;
@Before
public void init() {
ejbContainer = EJBContainer.createEJBContainer();
context = ejbContainer.getContext();
}
}
次に、初期化されたコンテナオブジェクトからjavax.naming.Contextオブジェクトを取得します。 Context インスタンスを使用して、 CountryStateContainerManagedBean への参照を取得し、メソッドを呼び出すことができます。
@Test
public void whenCallGetStatesFromContainerManagedBean_ReturnsStatesForCountry() throws Exception {
String[] expectedStates = {"Texas", "Alabama", "Alaska", "Arizona", "Arkansas"};
CountryState countryStateBean = (CountryState) context
.lookup("java:global/singleton-ejb-bean/CountryStateContainerManagedBean");
List<String> actualStates = countryStateBean.getStates("UnitedStates");
assertNotNull(actualStates);
assertArrayEquals(expectedStates, actualStates.toArray());
}
@Test
public void whenCallSetStatesFromContainerManagedBean_SetsStatesForCountry() throws Exception {
String[] expectedStates = { "California", "Florida", "Hawaii", "Pennsylvania", "Michigan" };
CountryState countryStateBean = (CountryState) context
.lookup("java:global/singleton-ejb-bean/CountryStateContainerManagedBean");
countryStateBean.setStates(
"UnitedStates", Arrays.asList(expectedStates));
List<String> actualStates = countryStateBean.getStates("UnitedStates");
assertNotNull(actualStates);
assertArrayEquals(expectedStates, actualStates.toArray());
}
同様に、 Context インスタンスを使用して、Bean管理のシングルトンBeanの参照を取得し、それぞれのメソッドを呼び出すことができます。
@Test
public void whenCallGetStatesFromBeanManagedBean_ReturnsStatesForCountry() throws Exception {
String[] expectedStates = { "Texas", "Alabama", "Alaska", "Arizona", "Arkansas" };
CountryState countryStateBean = (CountryState) context
.lookup("java:global/singleton-ejb-bean/CountryStateBeanManagedBean");
List<String> actualStates = countryStateBean.getStates("UnitedStates");
assertNotNull(actualStates);
assertArrayEquals(expectedStates, actualStates.toArray());
}
@Test
public void whenCallSetStatesFromBeanManagedBean_SetsStatesForCountry() throws Exception {
String[] expectedStates = { "California", "Florida", "Hawaii", "Pennsylvania", "Michigan" };
CountryState countryStateBean = (CountryState) context
.lookup("java:global/singleton-ejb-bean/CountryStateBeanManagedBean");
countryStateBean.setStates("UnitedStates", Arrays.asList(expectedStates));
List<String> actualStates = countryStateBean.getStates("UnitedStates");
assertNotNull(actualStates);
assertArrayEquals(expectedStates, actualStates.toArray());
}
close()メソッドで EJBContainer を閉じて、テストを終了します。
@After
public void close() {
if (ejbContainer != null) {
ejbContainer.close();
}
}
7. 結論
シングルトンセッションBeanは、標準のセッションBeanと同じように柔軟で強力ですが、シングルトンパターンを適用して、アプリケーションのクライアント間で状態を共有できます。
シングルトンBeanの同時実行管理は、コンテナーが複数のクライアントによる同時アクセスを処理するコンテナー管理の同時実行を使用して簡単に実装できます。または、Bean管理の同時実行を使用して独自のカスタム同時実行管理を実装することもできます。
このチュートリアルのソースコードは、GitHubのにあります。