OSGiサービスのオーバーライドをする
LiferayのOSGiコンテナは、必要に応じてサービスを追加、削除、またはオーバーライドできる動的な環境です。 このフレームワークは、LiferayコンポーネントをOSGiサービスレジストリに登録します。各コンポーネントには、独自の可用性、ランキング、および属性があります。 これらの詳細が合わさって、コンポーネントが参照するサービスにどのようにバインドするかが決まります。
OSGiサービスをオーバーライドするには、次の手順に従います。
-
オーバーライドするサービスと、それを参照するコンポーネントを特定します。
-
次のサービスの詳細を収集します。
-
サービスタイプ :オーバーライドするサービスによって実装されるインターフェイス
-
サービスのクラス名 :既存のサービスのフルネーム
-
-
該当する場合は、サービスを参照するコンポーネントについて次の詳細を収集します。
-
コンポーネント名 :オーバーライドするサービスを参照するコンポーネントのフルネーム
-
参照名 :ターゲットサービスを参照するフィールド名
-
参照ポリシー :参照が
static
かdynamic
か -
参照ポリシーオプション :
policy-option
がgreedy
かreluctant
か -
カーディナリティ :参照がバインドできるまたはバインドする必要があるサービスインスタンスの数。
サービスの 参照ポリシー 、 参照ポリシーオプション 、および カーディナリティ が一緒になって、新しいサービスを採用するためのコンポーネントの条件を決定します。
-
-
オーバーライドするサービスによって実装されたものと同じインターフェイスを使用する新しいサービスを作成します。
-
オーバーライドしているサービスよりも高いランクを自分のサービスに指定します。
-
(オプション)必要に応じて、自分のサービスでオーバーライドするサービスを参照して呼び出します。
サンプルモジュールは、OSGiサービスをオーバーライドする方法を示しています。 これらのモジュールには、新しいOSGiサービスタイプを定義するためのAPI、そのタイプの初期実装、および初期実装を参照する汎用ポートレットが含まれています。 また、初期実装をオーバーライドする方法を示すためのAPIの代替実装も含まれています。
オーバーライドするサンプルモジュールをデプロイする
新しいLiferay インスタンスを起動し、以下を実行します。
docker run -it -m 8g -p 8080:8080 。
http://localhost:8080でLiferayへのサインインします。 メールアドレス test@liferay.com とパスワード test を使用してください。 プロンプトが表示されたら、パスワードを learn に変更します。
次に、以下の手順を実行します。
-
サンプルモジュールをダウンロードし、解凍します。
curl https://resources.learn.liferay.com/dxp/latest/en/liferay-internals/extending-liferay/liferay-s1j6.zip -O
unzip liferay-s1j6.zip
-
s1j6-api
、s1j6-able-impl
、およびs1j6-web
サブフォルダから次のgradlew
コマンドを実行して、各モジュールをビルドして新しいDockerコンテナに個別にデプロイします。../gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
各モジュールのJARは、その
build/libs
フォルダ(例:s1j6-api/build/libs/com.acme.s1j6.api-1.0.0.jar
)に生成されます。ログメッセージは、Liferayが処理を開始し、各モジュールを正常に開始したことを示します。 これらのログには、各サービスのバンドルIDも提供されています。
STARTED com.acme.s1j6.api_1.0.0 [1356] STARTED com.acme.s1j6.able.impl_1.0.0 [1357] STARTED com.acme.s1j6.web_1.0.0 [1358]
-
モジュールがGogoシェルを介して正常にデプロイされたことを確認します。
lb | grep -i "s1j6"
成功した場合、出力は次のようになります。
1356|Active | 15|Acme S1J6 API (1.0.0)|1.0.0 1357|Active | 15|Acme S1J6 Able Implementation (1.0.0)|1.0.0 1358|Active | 15|Acme S1J6 Web (1.0.0)|1.0.0 true
提供されたapi
は、able.impl
モジュールによって実装されるOSGiサービスタイプを定義します。このタイプは、提供されたportlet
によって使用されます。 すべてがデプロイされたので、オーバーライドがどのように機能するかを試すことができます。
OSGiサービスとリファレンスの詳細を収集する
オーバーライドするサービスを特定したら、scr:info
Gogo シェルコマンドを使用して、その重要なサービスとリファレンスの詳細を収集します。 この例では、able.impl
サービスをオーバーライドします。
サービスの詳細を収集するには、次のコマンドを実行します。
scr:info com.acme.s1j6.able.internal.S1J6AbleImpl
Component Description: com.acme.s1j6.able.internal.S1J6AbleImpl
===============================================================
Class: com.acme.s1j6.able.internal.S1J6AbleImpl
Bundle: 1357 (com.acme.s1j6.able.impl:1.0.0)
[...]
Component Configuration Id: 8337
--------------------------------
State: ACTIVE
Service: 17776 [com.acme.s1j6.S1J6]
Used by bundle 1358 (com.acme.s1j6.web:1.0.0)
Config Props: (2 entries)
component.id = 8337
component.name = com.acme.s1j6.able.internal.S1J6AbleImpl
References: (total 0)
この簡略化された出力には、S1J6AbleImpl
の次のサービスの詳細がリストされています。
-
サービスタイプ :
S1J6AbleImpl
は、S1J6
インターフェースcom.acme.s1j6.S1J6
を実装します。 -
サービスのクラス名 :サービスのフルネームは
com.acme.s1j6.able.internal.S1J6AbleImpl
です。
また、サービスがcom.acme.s1j6.web:1.0.0
バンドル内のコンポーネントによって使用されていることも示されます。 コンポーネントの参照構成の詳細を表示するには、コンポーネントのフルネームを指定してscr:info
コマンドを実行します。
scr:info com.acme.s1j6.web.internal.portlet.S1J6Portlet
Component Description: com.acme.s1j6.web.internal.portlet.S1J6Portlet
=====================================================================
Class: com.acme.s1j6.web.internal.portlet.S1J6Portlet
Bundle: 1358 (com.acme.s1j6.web:1.0.0)
[...]
Component Configuration Id: 8338
--------------------------------
[...]
References: (total 1)
- _s1j6: com.acme.s1j6.S1J6 SATISFIED 1..1 static+greedy
target=(*) scope=bundle (1 binding):
* Bound to [17776] from bundle 1357 (com.acme.s1j6.able.impl:1.0.0)
この簡略化された出力には、次の参照構成の詳細がリストされています。
参照名 :S1J6AbleImpl
サービスを参照するフィールドの名前は_s1j6
です。
参照ポリシー :コンポーネントのポリシーはstatic
です(デフォルト)。
参照ポリシーオプション :コンポーネントのポリシーオプションは greedy
です。
カーディナリティ :そのカーディナリティは必須であり、単項です(つまり、1..1
)。
一部の参照構成は、新しいサービスまたは上位のサービスに自動的にバインドされますが、サーバーの再起動が必要なものもあります。 S1J6Portlet
の参照構成は静的で、欲張りで、必須であり、単項であるため、サーバーを新しい上位のサービスにバインドする前に、サーバーを再起動する必要はありません。 新しいサービスまたは上位のサービスが利用可能になったときに、さまざまな参照構成がコンポーネントの動作にどのように影響するかについて詳しくは、 OSGiのドキュメンテーション を参照してください。
収集した詳細でOSGiサービスを作成する
必要なサービスと参照の詳細を収集したら、それらを使用して、ターゲットサービスをオーバーライドして呼び出すためのカスタムサービスを作成できます。
-
@Component
アノテーションを使用して、サービスをOSGiフレームワーク内のコンポーネントとして宣言します。 -
ターゲットのOSGiサービスと同じインターフェースを実装し、
@Component
アノテーション内でそのservice
タイプを識別します。 -
インターフェースのメソッドをオーバーライドします。
-
@Component
アノテーション内にservice.ranking:Integer
プロパティを含めます。 そのランキングが既存のサービスよりも高いことを確認してください。 -
(オプション)既存のサービスの
component.name
を参照して呼び出します。
サンプルのS1J6BakerImpl
モジュールは、S1J6AbleImpl
をオーバーライドするために提供されています。
@Component(property = "service.ranking:Integer=100", service = S1J6.class)
public class S1J6BakerImpl implements S1J6 {
@Override
public String doSomething() {
return _s1j6.doSomething() +
"<br />This is the S1J6 Baker implementation.";
}
@Reference(
target = "(component.name=com.acme.s1j6.able.internal.S1J6AbleImpl)"
)
private S1J6 _s1j6;
}
ここで、S1J6BakerImpl
は、S1J6AbleImpl
と同じサービスタイプ(つまり、com.acme.s1j6.S1J6
)を実装し、必要な@Component
アノテーション、service
属性、およびservice.ranking
プロパティを含みます。 また、既存のサービス(つまり、component.name=com.acme.s1j6.able.internal.S1J6AbleImpl
)を参照し、インターフェイスのメソッドをオーバーライドする一環としてサービスに委任します。 サンプルモジュールには、S1J6AbleImpl
をオーバーライドしてからS1J6BakerImpl
をオーバーライドするための2つの他のS1J6実装も含まれています。
合計すると、含まれている実装のランキングは次のとおりです。
S1J6AbleImpl
:ランキングなし(デフォルトは0)S1J6BakerImpl
:100S1J6CharlieImpl
:101S1J6DogImpl
:101
デプロイされると、最高ランクのサービスが優先され、S1J6Portlet
にバインドされます。 複数のサービスのランキングが同じ場合は、最初に登録されたサービスが優先されます。 ランクの低いサービスは無視されます。
オーバーライドモジュールと構成ファイルをデプロイする
次の手順に従って、S1J6BakerImpl
、S1J6CharlieImpl
、およびS1J6DogImpl
をデプロイします。
-
コンソールで
s1j6-baker-impl
フォルダを開き、以下のgradlew
コマンドを実行してモジュールのJARファイルを構築しDockerコンテナにデプロイします。cd ../s1j6-baker-impl
../gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
ログメッセージには、Liferayが処理を開始し、バンドルIDとともにモジュールを正常に開始したことが示されます。
STARTED com.acme.s1j6.baker_1.0.0 [1359]
-
S1J6BakerImpl
が正常にデプロイされ、Gogoシェルを介してインスタンスにバインドされたことを確認します。scr:info com.acme.s1j6.web.internal.portlet.S1J6Portlet
成功した場合、
S1J6Portlet
はS1J6AbleImpl
よりランクが高いS1J6BakerImpl
にバインドされます。References: (total 1) - _s1j6: com.acme.s1j6.S1J6 SATISFIED 1..1 static target=(*) scope=bundle (1 binding): * Bound to [3248] from bundle 1359 (com.acme.s1j6.baker.impl:1.0.0)
-
S1J6CharlieImpl
とS1J6DogImpl
を同時にs1j6-liferay
からDockerコンテナにデプロイします。cd ..
../gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
STARTED com.acme.s1j6.charlie_1.0.0 [1360] STARTED com.acme.s1j6.dog_1.0.0 [1361]
-
S1J6CharlieImpl
とS1J6DogImpl
の両方がGogoシェルを介してインスタンスに正常にデプロイされたことを確認します。lb -s | grep -i "s1j6"
1356|Active | 15|com.acme.s1j6.api (1.0.0)|1.0.0 1357|Active | 15|com.acme.s1j6.able.impl (1.0.0)|1.0.0 1358|Active | 15|com.acme.s1j6.web (1.0.0)|1.0.0 1359|Active | 15|com.acme.s1j6.baker.impl (1.0.0)|1.0.0 1360|Active | 15|com.acme.s1j6.charlie.impl (1.0.0)|1.0.0 1361|Active | 15|com.acme.s1j6.dog.impl (1.0.0)|1.0.0
-
どのサービスが
S1J6Portlet
にバインドされているかを確認します。scr:info com.acme.s1j6.web.internal.portlet.S1J6Portlet
References: (total 1) - _s1j6: com.acme.s1j6.S1J6 SATISFIED 1..1 static target=(*) scope=bundle (1 binding): * Bound to [3249] from bundle 1360 (com.acme.s1j6.charlie.impl:1.0.0)
S1J6CharlieImpl
とS1J6DogImpl
はどちらも同じランキングであるため、最初に登録されたサービスが優先されてS1J6Portlet
にバインドされます。