JSPフラグメントバンドルへの依存性を追加する - Japan

null JSPフラグメントバンドルへの依存性を追加する
by Yasuyuki Takeo

JSPフラグメントバンドルへの依存性を追加する

画像

こんにちは。
日本ライフレイ、サポータビリティエンジニアの竹生(たけお)です。

今回は、JSPフラグメントバンドルへの依存性を追加する方法を紹介します。

以下の知識レベルの方たち、ユースケースが対象です。ご参照ください。

  • 中級レベル*
  • Liferay DXP開発に必須の知識

*レベルの定義については、こちらのブログの冒頭で紹介しています。

はじめに


※本記事はLiferay Community Blogに投稿されている”Adding Dependencies to JSP Fragment Bundles“”を翻訳したものです。配信元または著者の許可を得て配信しています。
※オリジナルの英記事著者:David H Nebinger サウスカロライナ州のサマーヴィルに住むLiferay, Inc.のSoftware Architect/リードコンサルタント。Liferay Communityサイトにて、多くの技術情報を投稿するなど精力的に活動しています。

先日、JSPフラグメントバンドルへ新しい依存関係を導入できないことに悩んでいました。これができないと、JSPオーバーライドは、すでにサポートされている要素をページ上で再構成、または追加/削除する事以外あまり役に立ちません。

これは、JSPオーバーライドで出来ることのほんの一部です。元のポートレット開発者が検討していなかったような、新機能を追加するケースはよくあります。例えば新しいサービスを追加して、JSP内からそのサービスを利用してエンティティを取得できるようにする、というケースです。

JSPフラグメントバンドルと同等のことをするのに、JSPオーバーライドを初めて試した際、私は失望しました。私のフラグメントバンドルは、GoGoではステータスが "Installed"となりますが、未解決の参照があったために、resolvedステータスまで到達できなかったのです。

元々利用可能なサービス以外のものにアクセスできないのをどう解決すればいいのでしょうか?

私の良き友人であり、同僚のMilen Dyankovが以下のような提案をしてくれました:

仕様によると:

...フラグメントバンドルの要件と機能は、フラグメントがホストに接続されている場合、ホストの要件、および機能の一部として処理されます。

宣言型サービスをフラグメントに提供する場合も、仕様は明確です。

フラグメントに指定されたサービス・コンポーネント・マニフェスト・ヘッダーは、SCRによって無視されます。ただし、バンドルのService-Componentマニフェストヘッダーによって参照されるXMLドキュメントは、添付されたフラグメントに含まれる可能性があります。

言い換えると、ホストにService-Components:OSGI-INF / *.xmlがある場合、フラグメントは新しいXMLファイルをOSGI-INFフォルダに置くことができ、SCRによって処理されます。

Milenは時々、私がただの開発者ということを忘れて、OSGiの達人であれば理解できるであろうアドバイスをしてくれます。そういうわけで、私にはまだ、JSPフラグメントバンドルへの疑問が残りました。

本ブログのでは、その疑問を調べた結果をご紹介したます。

サービスコンポーネントランタイム


SCRは、OSGi宣言型サービス仕様のApache Felix実装です。これは、OSGiコンテナ内のDSコンポーネントのサービスレジストリとライフサイクル管理、バンドルの開始/停止時のサービスの開始/停止、DSコンポーネントの@Reference依存関係の結びつけを担当します。

フラグメントバンドルの処理はApache Felixの実装に依存しているため、実際にはLiferayコンポーネントではなく、Liferayが意味するところのオーバーライドには役に立ちません。 JSPフラグメントバンドルのサービスにアクセスするために行うべきことは、サポートされているOSGiメカニズムを経由する以外ありません。

したがって、上記のMilenのアドバイスの鍵は、「バンドルのサービスコンポーネントマニフェストヘッダーによって参照されるXMLドキュメントが、添付されたフラグメントに含まれる可能性があります。」です。大まかなか解説をすると: バンドルホストのコンポーネントの1つに、オーバーライドXMLファイルを提供し、新しい依存関係を注入できるかもしれないということです。ひとまずやってみましょう。

サービス・コンポーネント・マニフェストXML


BNDのツールは、みなさんがご存知のように、バンドルのjarを作成するのにとても役立ちます。これらのタスクの1つは、サービス・コンポーネント・マニフェストおよびすべてのXMLファイルを生成することです。これらのファイルのすべての内容は、基本的にSCRが依存関係解決、コンポーネントを結びつけるのに必要なメタデータです。

@ComponentでJavaクラスに注釈を付けると、そのコンポーネントがDSサービスであることが示されます。 BNDがアノテーションを処理しているとき、Service Component Manifestにエントリを追加します(したがって、SCRはバンドル開始時にコンポーネントを処理します)。サービス・コンポーネント・マニフェストは、バンドルのMANIFEST.MFファイル内のサービス・コンポーネント・キーであり、各コンポーネントごとに1つずつ、OSGI-INF内の個々のXMLファイルをリストします。

これらのXMLファイルは、コンポーネントを実装するJavaクラス、コンポーネントが提供するサービス、コンポーネントのすべての参照詳細およびプロパティを指定して、SCRのコンポーネントを定義します。

したがって、バンドル・ジャーを持っていれば、それを展開し、MANIFEST.MFファイルを調べ、Service-Componentキーを探します。バンドルに定義されている各コンポーネントのOSGI-INF / com.example.package.JavaClass.xmlファイル(パッケージとクラス)があります。

マニフェストとXMLドキュメントについて知ったので、定期的に予定されているプログラムに戻ることができます。

SCR XMLのオーバーライド


バンドルのService Componentマニフェストヘッダーによって参照されるXMLドキュメントが、添付されたフラグメントに含まれる可能性があるため、これらのファイルの1つを上書きできる必要があります。

これは、新しいファイルを追加できないことを示唆していますが、既存のファイルを上書きすることはできます。

だとしたら、新しい依存関係を導入するためのオーバーライドXMLファイルを作成できるのか?(クラスを変更できないため、実際にはオリジナルに直接バインドすることはできません)、少なくともバンドルには新しい依存関係は作成できるのか?

実際にこの新しい知識すべてを使い、テストを試してみましたが、うまくいきませんでした。。。

ジェダイ(Millen)に戻る


"Milen、私のSCR XML上書きは機能していません。"

"XMLファイルがクラスローダによってロードされ、SCRがオーバーライドを無視するようにホストバンドルがフラグメントバンドルの前に来るため、オーバーライドは機能しません。 XMLをオーバーライドすることはできません。フラグメントバンドルにのみ新しいものを追加できます。"

“あなたは新しいXMLファイルを追加できないと言いましたが、ホストバンドルのMANIFEST.MFファイルのService-Componentにリストされているものだけが、ロード中にSCRによって使用されています。”

“OSGI-INF / *のようなワイルドカードを使用するようにService-Componentキーを変更し、SCRはフラグメントバンドルだけでなくホストバンドルからもロードします。これはあまり推奨できるやり方ではないですが、うまくいくでしょう。”

“それはできません。Milen、私はLiferayホストバンドル上でJSPフラグメントバンドルを実行しています。したがってサービスコンポーネントのマニフェスト値を変更することはできません。それができているのであれば、こうしたフラグメントのバンドルをする必要はそもそもなく、変更をホストバンドルに直接適用するだけで実現できてるはずです。”

"SCR XMLのオーバーライドは機能しないなら、他のものを試してみようか..."

プロジェクト例


新しいプランを立てて、検証するためのサンプルプロジェクトに取り掛かりました。今回の例では、JSPフラグメントバンドルのオーバーライドが含まれていなければなりません。ここで必要以上にコーディングする必要はありません。ポータルのJSPやサービスから選んでみましょう。

要件:ログインフォームに、現在のメンバーシップリクエスト数を表示します。

自動的に処理されるサイトアクセスのためのメンバーシップリクエストと、何人がサイトアクセスを待っているのかを数えて表示することで、サイトの人気度を示すことができる例です。

これを実現するには、login-webホスト・バンドルのlogin.jspページ内のMemberRequestLocalServiceにアクセスする必要があります。このサービスはcom.liferay.invitation.invite.members.apiバンドルに定義されており、現在ログインWebモジュールとは何ら接続されていません。

フラグメントバンドルの作成


今回、コマンドラインでブレードを使用するパターンで試しますが、もちろんIDEで提供されるツールを利用することもできます。

blade create -t fragment -h com.liferay.login.web -H 1.1.4 login-web-fragment

ブレード作成-tフラグメント-h com.liferay.login.web -H 1.1.4 login-web-fragment 正しいホストを上書きできるよう、フラグメントバンドルバージョンを選択してください。

ポータル・ソースからlogin.jspページをコピーします。 init.jspのインクルードの後に​​、次の行を追加します:

<%@ page import="com.liferay.invitation.invite.members.service.MemberRequestLocalService" %>

<%
  // get the service from the render request attributes
  MemberRequestLocalService memberRequestLocalService = (MemberRequestLocalService)
    renderRequest.getAttribute("MemberRequestLocalService");

  // get the current count
  int currentRequestCount = memberRequestLocalService.getMemberRequestsCount();

  // display it somewhere on the page...
%>

非常にシンプルです。実際に表示部分は実装していませんが、今回の記事では割愛しています。

ビルドしてデプロイすると、GoGoの状態が "Installed"になっていると思います。このステータスはJSPフラグメントが動作するのに適切なステータスではないので、このままでは動作していません。

依存関係の追加


OSGiがフラグメントバンドルをどのように処理するか、という点に戻る必要があります。OSGiがフラグメントをロードするとき、フラグメントバンドルのMANIFEST.MFアイテムはホストバンドルからのアイテムとマージされます。

build.gradleへの依存関係をリストアップする必要があり、BNDは正しいImport-Package宣言を最後のMANIFEST.MFファイルに追加します。

次に、フレームワークが私のフラグメントバンドルをロードするとき、私のフラグメントのImport-Packageは、ホストバンドルのImport-Packageに追加され、すべてがうまくいくはずです。

ブレードによって作成されたJSPフラグメントバンドルは、build.gradleファイルにリストされた依存関係を持っていません(実際は完全に空です)。したがって、依存関係を追加してみましょう。


  dependencies {
  compile group: "com.liferay", name: "com.liferay.invitation.invite.members.api", version: "2.1.1"
}

ホストバンドルから欠落している依存関係を追加するだけです。ホストバンドルには、取り込む予定のサービスがあります。

ビルド後、JARファイルを展開してMANIFEST.MFファイルを確認すると、Import-Package宣言があることを確認できました。SCRがロード中に実際にマージを実行する場合は、動作するはずです。

新しいJSPフラグメントバンドルをデプロイし、バンドルステータスをGoGoで確認すると、「Resolved」と表示されます。

これでばっちりです!

参照を挿入する


ポータルにログインしようとすると、「ポートレットは一時的に使用できません」というメッセージが表示され、ログファイルにはNullPointerExceptionと大きなスタックトレースが記録されます。 login.jspはサービスに依存するが設定されていないため、ログイン・ポートレットは完全に壊れてしまっています。

共有したJSPの変更をチェックすると、レンダリング要求の属性からサービスインスタンスが取得されます。しかし、ホストバンドルを最初に挿入するように変更することができない場合、どうすればそこに入るのでしょうか?

PortletFilterインターフェイスを実装する新しいコンポーネント、特にRenderFilterを使用して、別のOSGiモジュールを使用してこれを行います。


@Component(
  immediate = true,
  property = {
      "javax.portlet.name=" + LoginPortletKeys.LOGIN,
      "javax.portlet.name=" + LoginPortletKeys.FAST_LOGIN
  },
  service = PortletFilter.class
)
public class LoginRenderFilter implements RenderFilter {
  @Override
  public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain) throws IOException, PortletException {
    // set the request attribute so it is available when the JSP renders
    request.setAttribute("MemberRequestLocalService", _memberRequestLocalService);

    // let the filter chain do it's thing
    chain.doFilter(request, response);
  }

  @Override
  public void init(FilterConfig filterConfig) throws PortletException { }

  @Override
  public void destroy() { }

  @Reference(unbind = "-")
  protected void setMemberRequestLocalService(final MemberRequestLocalService memberRequestLocalService) {
    _memberRequestLocalService = memberRequestLocalService;
  }

  private MemberRequestLocalService _memberRequestLocalService;
}

ポートレット・フィルターを使用してレンダー要求をインターセプトしています。レンダリングを完了するためにフィルタチェーンを呼び出す前に、リクエスト属性にサービスを注入します。フラグメントバンドルのJSPページが使用されると、その属性が設定され、準備が整います。

新しいコンポーネントをビルドして展開します。起動したら、ブラウザをリフレッシュしてログインします。ログイン・ポートレットが再び表示されます。サービス参照がnullではなく、JSPオーバーライドで使用できることを証明しています。

まとめ


ここまで到達するために、だいぶ回り道しましたが、JSPフラグメントバンドルを作成してポータルJSPをオーバーライドする方法、フラグメントバンドルに依存関係を追加して、ホストバンドルに依存関係として追加する方法、ポートレット・フィルタ・バンドルを使用して、サービス参照をリクエスト属性に挿入することで、JSPページで使用できるようにします。

2つの異なるバンドルjarですが、確かにこれで目的は達成できます

また、SCRとは何か、フラグメントバンドルがどのように動作するか、OSGiバンドルjarの内部構造とBNDがその構築に果たす役割についていくつか紹介しました。

Liferay 7 CE / Liferay DXPで開発する際は役立つ便利な情報だったのではないかと思います。

JSPフラグメントバンドルのための新しい道がいくつか開かれました。下記の概要にしたがえれば問題ないでしょう。

ブログのプロジェクトコードはこちら:
https://github.com/dnebing/jsp-fragment

 


 

本記事は以上です。
いかがでしたでしょうか?

 

記事に関するご意見、ご感想などございましたら、お気軽にyasuyuki.takeo@liferay.comまでご連絡くだい。

 


■ これまで発信してきた日本語による技術コンテンツを、Qiitaでお読みいただけます。よろしければそちらの記事も合わせてご役立てください。
https://qiita.com/yasuflatland-lf

Doorkeeperのライフレイコミュニティメンバー大歓迎!
コーポレートブログの技術コンテンツ更新情報など定期的におとどけしています。
https://liferay.doorkeeper.jp/

業界最高の評価

Gartner

Gartnerレポート:『リーダー』に位置づけ
ライフレイは9年連続で、Gartner社によるデジタルエクスペリエンスプラットフォーム(DXP )分野のマジック・クアドラントにおいて、リーダーに選出されました。

レポートの詳細、ダウンロード