テンプレート処理中にエラーが発生しました。
Java method "com.liferay.portal.json.JSONFactoryImpl.createJSONObject(String)" threw an exception when invoked on com.liferay.portal.json.JSONFactoryImpl object "com.liferay.portal.json.JSONFactoryImpl@2ad66ae2"; see cause exception in the Java stack trace.

----
FTL stack trace ("~" means nesting-related):
	- Failed at: navigationJSONObject = jsonFactoryUti...  [in template "17855804202317#32484267#LEARN-ARTICLE-NAV" at line 4, column 9]
----
1<#assign 
2	groupFriendlyURL = themeDisplay.getScopeGroup().getFriendlyURL() 
3	groupPathFriendlyURLPublic = themeDisplay.getPathFriendlyURLPublic() + groupFriendlyURL 
4	navigationJSONObject = jsonFactoryUtil.createJSONObject(navigation.getData()) 
5	navigationMenuItems = 
6
7			"Analytics Cloud": { 
8				"image": "/documents/d${groupFriendlyURL}/analytics_c-svg", 
9				"title": "Analytics Cloud", 
10				"url": "analytics-cloud" 
11			}, 
12			"Commerce": { 
13				"image": "/documents/d${groupFriendlyURL}/commerce_product-svg", 
14				"title": "Commerce", 
15				"url": "commerce" 
16			}, 
17			"DXP": { 
18				"image": "/documents/d${groupFriendlyURL}/dxp_p-svg", 
19				"title": "DXP / Portal", 
20				"url": "dxp" 
21			}, 
22			"Liferay Cloud": { 
23				"image": "/documents/d${groupFriendlyURL}/dxp_c-svg", 
24				"title": "Liferay Cloud", 
25				"url": "liferay-cloud" 
26			}, 
27			"Reference": { 
28				"image": "/documents/d${groupFriendlyURL}/reference-svg", 
29				"title": "Reference", 
30				"url": "reference" 
31
32
33 
34	breadcrumbJSONArray = navigationJSONObject.getJSONArray("breadcrumb") 
35	childrenJSONArray = navigationJSONObject.getJSONArray("children") 
36	parentJSONObject = navigationJSONObject.getJSONObject("parent") 
37	productJSONObject = breadcrumbJSONArray.getJSONObject(breadcrumbJSONArray.length()-1)!navigationJSONObject.getJSONObject("self") 
38	siblingsJSONArray = navigationJSONObject.getJSONArray("siblings") 
39/> 
40 
41<div class="learn-article-nav"> 
42	<#if productJSONObject?has_content && productJSONObject.getString("title")?has_content && navigationMenuItems[productJSONObject.getString("title")]?has_content && navigationMenuItems[productJSONObject.getString("title")].title?has_content> 
43		<div 
44			class="dropdown learn-article-nav-root learn-dropdown" 
45
46			<div class="learn-article-nav-item"> 
47				<div class="d-flex"> 
48					<div class="learn-article-nav-image"> 
49						<img 
50							class="lexicon-icon lexicon-icon-caret-bottom product-icon" 
51							role="presentation" 
52							src='${navigationMenuItems[productJSONObject.getString("title")].image}' 
53							viewBox="0 0 512 512" 
54						/> 
55					</div> 
56 
57					<span class="learn-article-nav-text">${navigationMenuItems[productJSONObject.getString("title")].title}</span> 
58				</div> 
59 
60				<div id="dropdown-icon"> 
61					<svg 
62						class="lexicon-icon lexicon-icon-caret-bottom" 
63						role="presentation" 
64						viewBox="0 0 512 512" 
65
66						<use xlink:href="/o/admin-theme/images/clay/icons.svg#caret-bottom"></use> 
67					</svg> 
68				</div> 
69			</div> 
70 
71			<ul class="dropdown-menu learn-dropdown-menu"> 
72				<#list navigationMenuItems as key, value> 
73					<li> 
74						<a 
75							class="dropdown-item learn-article-nav-item" 
76							href="/w/${navigationMenuItems[key].url}/index" 
77							tabindex="4" 
78
79							<span class="d-flex"> 
80								<span class="learn-article-nav-image"> 
81									<img 
82										class="lexicon-icon lexicon-icon-caret-bottom product-icon mt-0 mr-2" 
83										role="presentation" 
84										src="${value.image}"height: 25px; margin-left: 5px; max-width: none; width: 25px; 
85										viewBox="0 0 512 512" 
86									/> 
87								</span> 
88								<span class="learn-article-nav-text">${value.title}</span> 
89							</span> 
90 
91							<#if navigationMenuItems[productJSONObject.getString("title")].url == value.url> 
92								<span> 
93									<@clay["icon"] symbol="check" /> 
94								</span> 
95							</#if> 
96						</a> 
97					</li> 
98				</#list> 
99			</ul> 
100		</div> 
101	</#if> 
102 
103	<div class="learn-article-nav-content"> 
104		<#if parentJSONObject?has_content && parentJSONObject.getString("url")?has_content> 
105			<div class="learn-article-nav-item learn-article-nav-parent liferay-nav-item p-2"> 
106				<div class="mr-2"> 
107					<a 
108						href='${parentJSONObject.getString("url")}' 
109
110						<svg 
111							class="lexicon-icon lexicon-icon-angle-left" 
112							role="presentation" 
113							viewBox="0 0 512 512" 
114
115							<use xlink:href="/o/admin-theme/images/clay/icons.svg#angle-left"></use> 
116						</svg> 
117					</a> 
118				</div> 
119 
120				<span>${parentJSONObject.getString("title")}</span> 
121			</div> 
122		</#if> 
123 
124		<#if childrenJSONArray.length() gt 0> 
125			<ul class="m-0 p-2"> 
126				<#list 0..childrenJSONArray.length()-1 as i> 
127					<li class="learn-article-nav-item"> 
128						<a 
129							class='liferay-nav-item ${(navigationJSONObject.getJSONObject("self").url == childrenJSONArray.getJSONObject(i).url)?then("selected", "")}' 
130							href="${childrenJSONArray.getJSONObject(i).url}" 
131
132							<span>${childrenJSONArray.getJSONObject(i).getString("title")}</span> 
133						</a> 
134					</li> 
135				</#list> 
136			</ul> 
137		<#elseif siblingsJSONArray.length() gt 0> 
138			<ul class="m-0 p-2"> 
139				<#list 0..siblingsJSONArray.length()-1 as i> 
140					<li class="learn-article-nav-item"> 
141						<a 
142							class='liferay-nav-item ${(navigationJSONObject.getJSONObject("self").url == siblingsJSONArray.getJSONObject(i).url)?then("selected", "")}' 
143							href="${siblingsJSONArray.getJSONObject(i).url}" 
144
145							<span>${siblingsJSONArray.getJSONObject(i).getString("title")}</span> 
146						</a> 
147					</li> 
148				</#list> 
149			</ul> 
150		</#if> 
151	</div> 
152</div> 

セマンティックバージョニング

セマンティックバージョニング は、リリース可能なソフトウェアコンポーネントで行われたAPI変更の程度に基づいてバージョン番号をインクリメントするための3層のバージョニングシステムです。 これは、依存するコンシューマおよびAPI実装のパッケージまたはモジュールのプログラムによる互換性を伝達するための標準規格です。 パッケージがプログラム的に(つまり、セマンティックに)プロジェクトと互換性がない場合、 BndLiferayで生成されたモジュールプロジェクトをビルドするときに使用)は、そのプロジェクトのビルドにすぐに失敗します。

セマンティックバージョンの形式は次のようになります。

MAJOR.MINOR.MICRO

特定のイベントにより、各層が強制的にインクリメントされます。

  • メジャー: 互換性のない、APIを破る変更が行われました
  • マイナー: APIのプロバイダーのみに影響する変更、または新しい下位互換性のある機能が追加されました
  • マイクロ: 下位互換性のあるバグ修正が行われました

セマンティックバージョニングの詳細については、公式の セマンティックバージョニング サイトおよび OSGi Allianceのセマンティックバージョニング テクニカルホワイトペーパーを参照してください。

Liferayのモジュールはすべて、セマンティックバージョニングを使用しています。

DXPは何百もの独立したOSGiモジュールを含むモジュラープラットフォームであるため、セマンティックバージョニングに従うことが特に重要です。 相互に依存する多くの独立したモジュールがあるため、互換性を宣言する方法がないと、新しいパッケージバージョンをリリースすることが恐ろしくなります。 セマンティックバージョニングのわかりやすいシステムとLiferayのツールは、モジュールの互換性を管理するのに役立ちます。

プロジェクトのベースライン設定

セマンティックバージョニングを手動で実行するのは一見簡単に思えます。 善意の開発者がプロジェクトのセマンティックバージョンを手動で更新したが、後で間違いだと気づくという悲しい歴史があります。 実際のところ、単純なアップデートの影響を予測することは困難です。 これを回避するには、プロジェクトが更新された後にプロジェクトの ベースライン を設定できます。 これにより、プロジェクトがセマンティックバージョニングルールに準拠していることを確認し、人間には必ずしも明らかではないAPIの変更をキャッチできます。 LiferayのBaseline Gradleプラグインを使用して、ベースライン機能を提供できます。 これをGradleビルド構成に追加し、次のコマンドを実行します。

./gradlew baseline

このBaseline Gradle Pluginプラグインは、 Liferay Workspace ではデフォルトで提供されていません。

baselineコマンドを実行すると、プラグインは新しいモジュールのエクスポートされたパブリックAPIを最新のリリースされた非スナップショットモジュールと比較します。 変更がある場合は、OSGiセマンティックバージョニングルールを使用して、最小の新しいバージョンを計算します。 新しいモジュールのバージョンが低い場合、エラーがスローされます。

ツールだけに頼らないでください。 Javaクラスまたはインターフェイスのシグニチャーや、API 使用 の変更(つまり、メソッド呼び出しの順序に関する仮定、または入力および/または出力エンコーディングの変更)に表されていない互換性の変更を識別するには十分ではありません。 ベースラインとは、その名が示すように、互換性の問題が大量に発生することがないような、ある程度の 指標となる 快適さを提供するものです。

ベースラインを設定すると、プロジェクトのセマンティックバージョニングはAPIが表現するのと同じくらい正確になります。

アーティファクトと依存関係のバージョンの管理

セマンティックバージョニングを使用してプロジェクトのアーティファクトと依存関係のバージョンを追跡する方法は2つあります。

  • バージョンの範囲
  • 正確なバージョン(1対1)

複数のDXPバージョンのプロジェクトをビルドし、最大限の互換性を維持する場合は、さまざまなバージョンを追跡する必要があります。 つまり、アプリで複数のパッケージバージョンが動作する場合は、それらのいずれかを使用するようにアプリを構成できます。 さらに、Bndはモジュールが依存する各パッケージのセマンティックに互換性のある範囲を自動的に判断し、モジュールのマニフェストに記録します。

バージョン範囲の構文については、 OSGi Specifications を参照してください。

OSGiバンドルのbnd.bndにインポートされたパッケージのバージョン範囲は次のようになります。

Import-Package: com.liferay.docs.test; version="[1.0.0,2.0.0)"

一般的なビルドツールもこの構文を採用しています。 Gradleでは、依存関係のバージョン範囲は次のようになります。

compile group: "com.liferay.portal", name: "com.liferay.portal.test", version: "[1.0.0,2.0.0)"

最新のリリースバージョンを指定することも、上限のないバージョンの範囲と見なすことができます。 Gradleで指定する方法は次のとおりです。

version: "latest.release"`

さまざまなバージョンを追跡するには、それなりの対価が必要です。 問題をデバッグしているときに、古いビルドを再現するのは困難です。 また、使用するバージョンによって動作が異なるリスクもあります。 また、大きな変更が導入された場合、最新のリリースに依存すると、プロジェクトとの互換性が失われる可能性があります。 バージョンの範囲を指定する場合は慎重に行い、含まれるすべてのバージョンでプロジェクトをテストしてください。

依存関係の正確なバージョンを追跡する方が安全ですが、柔軟性は低くなります。 特定のDXPバージョンに制限されたり、その特定のバージョンにのみ存在するAPIにロックされる可能性があります。 ただし、モジュールをテストするのが非常に簡単で、予期しない障害が発生する可能性が低くなります。

note

bnd.bndファイルでパッケージバージョンを指定する場合、正確なバージョンは通常、version="1.1.2"のように指定されます。 ただし、この構文は技術的には範囲であり、[1.1.2, ∞)と解釈されます。 したがって、パッケージのより高いバージョンが利用可能な場合は、指定したバージョンの代わりにそのバージョンが使用されます。 このような場合、テスト済みの互換性のあるバージョンのバージョン範囲を指定する方がよい場合があります。 真の完全一致を指定したい場合は、[1.1.2]のような構文になります。 詳細については、OSGi仕様書の Version Range セクションを参照してください。

1つのバージョンのみが指定されている場合、Gradleは正確なバージョンを使用します。

これで、依存関係を範囲で追跡する場合と完全一致で追跡する場合の長所と短所がわかりました。

関連トピック