はじめに
本記事はLiferayのサーバー管理でのスクリプト機能の利用方法になりますのでLiferay PaaSおよびLiferay Self-Hosted利用者向けの記事になります。
(SaaSでは利用できない機能です)
Liferayではサーバー管理のスクリプト機能からGroovyスクリプトを介してLiferay上のAPIを自由に利用可能です。
例えばこのGroovyスクリプトではLocalServiceUtilを介してLiferay上のService APIの実行を始め、Liferay上のクラスにアクセスできるのでとても自由度が高く強力なスクリプトの実行が可能です。
データ移行後のデータパッチなど、カスタムモジュールを作るほどではないけど、大量データに対して更新作業を行いたい時など、これまで弊社ではGroovyスクリプトを使って、以下のようなことを行っています。
(Liferay DXPではSQLでデータを書き換えるとサポート対象外になってしまうので)
- ユーザーの利用規約承認済の状態を一律未承認に戻す
(利用規約の改定時などに便利) - Webコンテンツの中のURLを書き換え
- 一部のコンテンツの権限設定を変更
そんなGroovyスクリプトですが、OSGiのコンポーネントにアクセスしたい時にどうすればいいかわからなくて困ってる。という方の一助になれば幸いです。
GroovyスクリプトでOSGiのコンポーネントを取得する方法
Groovyスクリプトでは前述の通りLiferay上のクラスにアクセスできますので、newで新しくインスタンスを作ったりstaticなメソッドを利用する類の処理は容易に実現可能です。
ただ、OSGi コンポーネントのようにDIの仕組みで管理された生成済インスタンスを利用したい際には少し手間が必要です。
LocalServiceImplやServiceImplといったServiceに関してはstaticなアクセサーのLocalServiceUtilやServiceUtilを介することで利用可能ですが、DispatchTaskExecutorなどのコンポーネントはアクセサーが提供されていません。
なので下図のようなステップでOSGi Registryにアクセスしてコンポーネントのインスタンスを取得する必要があります。

コーディング
過去のバージョンではLiferayでOSGiのAPIをラップしたRegistryUtilというクラスを提供していましたが、DXP 7.4で廃止され、現在はOSGiのユーティリティを使う方針となっています。
具体的なGroovyのコードは以下のようになります。
ここでは例としてcom.liferay.portal.kernel.service.UserLocalServiceというユーザー情報を操作するコンポーネントを指定しているので、適宜任意のコンポーネントに置き換えてください。
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.BundleContext;
import com.liferay.portal.kernel.util.Portal;
import com.liferay.server.admin.web.internal.portlet.ServerAdminPortlet;
// 実行したいService(実装クラスではなくServiceで指定しているクラスorインターフェース)
import com.liferay.portal.kernel.service.UserLocalService;
// 1) 自Bundle 情報を取得
def bundle = FrameworkUtil.getBundle(ServerAdminPortlet);
// 2) BundleからBundleContextを取得
def bundleContext = bundle.bundleContext;
// 3) BundleContextからServiceReference を取得
def serviceReference = bundleContext.getServiceReference(UserLocalService);
// 4) BundleContextからServiceReferenceを元にService(コンポーネントインスタンス)を取得
def userLocalService = bundleContext.getService(serviceReference);
// 5) Serviceのメソッドを呼ぶ
def usersCount = userLocalService.getUsersCount();
println("usersCount: ${usersCount}");
フローに沿って解説すると
1) 自Bundle 情報を取得
まずは自身のBundleを取得する必要があります。
Groovyスクリプトはサーバー管理ポートレットの1機能になりますので、
サーバー管理ポートレットのPortletクラスであるcom.liferay.server.admin.web.internal.portlet.ServerAdminPortletを指定します。
ここを外部のクラスを指定するとBundleがnullになります。
// 1) 自Bundle 情報を取得
def bundle = FrameworkUtil.getBundle(ServerAdminPortlet);
2) BundleからBundleContextを取得
ここは文字通りです。
Bundleが問題なく取得できていれば、getBundleContext()で取得できます。
ここではGroovy風にGetterを略しているだけです。
// 2) BundleからBundleContextを取得
def bundleContext = bundle.bundleContext;
3) BundleContextからServiceReference を取得
取得したBundleContextを介してServiceへの参照を取得します。
この時、引数に指定するのはコンポーネントの実態クラスではなくインターフェースです。
(DIの考え方としては自然)
// 3) BundleContextからServiceReference を取得
def serviceReference = bundleContext.getServiceReference(UserLocalService);
4) BundleContextからServiceReferenceを元にServiceを取得
取得したServiceReferenceを使ってBundleContextを介してService(コンポーネントのインスタンス)を取得します。
// 4) BundleContextからServiceReferenceを元にService(コンポーネントインスタンス)を取得
def userLocalService = bundleContext.getService(serviceReference);
5) Serviceのメソッドを呼ぶ
あとは取得したServiceの任意のメソッドを実行していただければOKです。
もちろん戻り値を再利用することも可能です。
// 5) Serviceのメソッドを呼ぶ
def usersCount = userLocalService.getUsersCount();
println("usersCount: ${usersCount}");
まとめ
いかがでしたでしょうか。
このようにOSGi Registryから任意のコンポーネントを取り出して実行することも容易に実現できます。
Groovyスクリプトはサーバー管理以外にもワークフローなどでも利用でき、ローコードでとても強力で柔軟なスクリプト実行が可能です。
Oxygen Designでは大量データを扱う際にGroovyスクリプト内で並列処理を行う実績も多数あります。
ご興味があれば、ぜひご連絡ください。
まつもとでした!
