BNDで避けるべき記法 - BNDで避けるべき記法 - Japan

null BNDで避けるべき記法
by Yasuyuki Takeo

BNDで避けるべき記法

画像

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

前回の記事にてBNDについて紹介しました。そこで今回は、BNDで避けるべき記法に関する記事を紹介します。

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

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

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

はじめに

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

先日、ログエントリごとにプライベートパッケージを公開するフラグメントバンドルを構築していた際、元のbnd.bndファイルで以下コードを見つけました:


-dsannotations-options: inherit

これは見たことない一文だったため、調べてみることにしました。

継承参照

bnd.bndファイルに命令を追加すると、クラス階層が検索され、親クラスのすべての@Referenceアノテーションは、それらが基本クラスで定義されているかのように処理されます。

通常、@Referenceと子Barクラスを持つFooクラスがある場合、親参照はOSGiによっては処理されません。その代わりに、@ReferenceアノテーションをBarクラスに追加し、スーパークラスのsetterメソッドを呼び出さなければなりません(サブクラスに値を設定する必要があるかもしれないので、プライベートメンバーではなく、保護されたsetterで常に@Referenceアノテーションを使用する必要があります。)。

bnd.bndファイルにdsannotations命令を追加すると、これらの@Referenceアノテーションをすべてサブクラスにコピーする必要がなくなりました。

これを知った私の印象は、使いやすそう、今後使ってみたいというものでした。

避けるべき記法

さらに調べていくと、以下@Reference継承のサポートに関するフォーラムを見つけました: https://groups.google.com/forum/#!topic/bndtools-users/6oKC2e-24_E

ここで、これは割と厄介な実装上の問題であることが判明しました。主に、FooとBarを別のバンドルに分割すると、コンテキストが異なります。そのため、別のバンドルでBarを処理する場合、Fooの親クラスを持つバンドルから独自のコンテキスト、クラスローダなどがあります。OSGiは、開発者が気づかぬうちに、どのようにしてこれらのコンテキストを越えることができているのか不思議ではありますが、実際、複雑な処理がされています。本当はあまり深掘りしたくないのですが・・・

しかし、私たちがdsannotations継承を、正しくかつ効果的に使用するためには、このコンテキストの仕組みがどのように動いているか、もっと詳しく知る必要があります。
あまり気は進まないかも知れませんが・・・

その理由1つをとっても、以下記法の使用は避ける必要があります。

より完全な対応は、Felix Meschbergerによるものです。
Apache Felixプロジェクトでは、以前この機能を持つアノテーションがあり、実際に標準化も試みました。 しかし問題は、バンドル境界を越えて2つの実装クラスの間で厄介なカップリングの問題が発生し、Import-PackageヘッダーまたはRequire-Capabilityヘッダーを使用してこの依存関係を適切に表現することはできないことです。

思い当たるいくつかの問題:

  • 一般的に、バインド/アンバインドメソッドを非公開にしたいとします。 SCRがベースクラスのプライベートバインドメソッドを呼び出すのは問題ないのでしょうか?(技術的には可能)。
  • 自分がプライベートメソッドを定義していて、実装者がプライベートメソッドの名前を変更しようとしたらどうなるでしょうか?このプライベートメソッドで、APIとしては露出されていません。バインド/アンバインドメソッドが拡張クラスによって提供される記述子にリストされているのは古いメソッド名のため、コンシューマはバインドに失敗します。
  • プライベートメソッド名をサポートしていない場合、これらのバインド/アンバインドメソッドを保護するか公開する必要があります。したがって、実装詳細メソッドを、強制的にAPIの一部にする必要があります。(個人的にあまりおすすめしません)
  • 注:異なる2つのバンドルは、同じパッケージを異なるコンテンツで共有するべきではないため、パッケージプライベートメソッドは動作しません。

継承を単一のバンドル内に持つことは問題ないと前述しましたが、制約やそれらの説明などを見る限り、あまり賢明な方法でないことがわかりました。振り出しにもどります。

もし使用すべきでないなら、なぜLiferayなのか?

このもっともな疑問が思い浮かぶでしょう。

これはすべて、Liferayのコードがベースになっていることに起因します。 OSGi-ifiedコードではありますが、それでもまだ過去の歴史的バージョンのコードともしっかりつながっているのです。たとえば、ブログはOSGiモジュールを介して行われていますが、コードの大部分は6.xのコードによく似ています。

レガシーなLiferayコードベースは、コンポジションに加えて継承を頻繁に使用します。新しいLiferayの実装であっても、依然として継承に大きく依存しています。

OSGiの最適なパターンは、コンポジションと軽い継承です。この方法によりOSGi宣言型サービスの利点を最大限に生かし、既存のコンポーネントを置き換えるために高いサービスランクで新しいコンポーネントを定義することができます。コンポーネントを結び付けて動的ソリューションを構成できます。

しかし、Liferayが継承を大量に使用しているということは、クラス階層の注入を完了するために、多くの子クラス@Referenceアノテーションの複製が必要な、親クラスがたくさんあることを意味します。

より多くのコンポジションと、より少ない継承へ移行するため、コード修正する予定はありますが、これには時間がかかります。これらの変更をすぐに強制し、@Referenceアノテーションのコピーを削除する代わりに、-dsannotations-options記法を使用して、クラス階層の@Referenceアノテーション処理を強制しました。通常、継承は単一のバンドル内で制限されるため、コンテキスト変更は問題ではありませんが、Felixが提起したポイントには、まだ懸念が残ります。

まとめ

-dsannotations-options BND記法に関する情報、なぜLiferayバンドルで多く見かけるかについて理解していただけたかと思います。 一番重要なのは、自身のプロジェクトで使ってはいけない理由です。

Liferayのバンドルを扱っているのであれば、-dsannotations-optionsが表示されたら、それがなぜそこにあるのか、なぜそれを守る必要があるのか、今ならお分かりいただけると思います。


 

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

 

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

 


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

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

業界最高の評価

 

Gartner

ライフレイは9年連続で、ガートナー社によるデジタルエクスペリエンスプラットフォーム分野のマジック・クアドラントにおいて、Adobe社やIBM社と並ぶリーダーに選出されました。

レポートの詳細はこちら