フォームストレージアダプターの書き込み
LPS-97208 の修正を含む Liferay DXP 7.3 および Liferay DXP 7.2 バージョン (Liferay DXP 7.2 SP3 で予定)
デフォルトでは、フォームはLiferay DXPのデータベースにJSONとして保存されます。 この例では、フォームレコードの永続化イベントにカスタムロジックを挿入するために、新しいストレージアダプターを実装する方法を紹介します。

デフォルトのストレージアダプタ は、フォームレコードを Liferay DXP データベースに JSON コンテンツとして保存します。 次に、各フォーム レコードをファイル システムに保存するロジックを追加します。
実行中のDDMストレージアダプターを調べる
ストレージアダプターの動作を確認するために、サンプルをデプロイし、サンプルアダプターを使用していくつかのフォームデータを追加します。
サンプルをデプロイする
新しいLiferay インスタンスを起動し、以下を実行します。
docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.132-ga132
http://localhost:8080でLiferayにサインインします。 メールアドレス test@liferay.com とパスワード testを使用してください。 プロンプトが表示されたら、パスワードを learnに変更します。
次に、以下の手順に従います。
-
DDM ストレージ アダプタ プロジェクトをダウンロードして解凍します 。
curl https://resources.learn.liferay.com/examples/liferay-r2f1.zip -Ounzip liferay-r2f1.zip -
モジュールのルートから、ビルドおよびデプロイします。
./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)ヒントこのコマンドは、デプロイされたjarをDockerコンテナ上の
/opt/liferay/osgi/modulesにコピーするのと同じです。 -
Liferay Dockerコンテナコンソールでデプロイを確認します。
STARTED com.acme.r2f1.impl_1.0.0 [1009]
デプロイされたストレージアダプターを使用する
-
ブラウザを開いて http://localhost:8080にアクセスします。
-
サイト メニュー → コンテンツ & データ → フォームでフォーム アプリケーションに移動します。
-
追加 (
) をクリックして、フォーム ビルダーを開きます。 -
フォーム ビルダー ビューで、 オプション (
) をクリックし、 設定 ウィンドウを開きます。 -
ストレージ タイプの選択で、 R2F1 ダイナミック データ マッピング ストレージ アダプタ タイプを選択し、 完了をクリックします。
-
フォームに テキスト フィールド を追加し、フォームを公開して、数回送信します。
-
フォームデータが保持されていることを確認するには、フォームのレコードに移動します。
サイト メニュー → コンテンツ → フォームから、フォームの アクション ボタン (
) をクリックし、次に エントリの表示をクリックします。
-
さらに、各CRUDメソッドにロギングが提供され、サンプルのメソッドが呼び出されていることが示されています。
WARN [http-nio-8080-exec-5][R2F1DDMStorageAdapter:82] Acme storage adapter's save method was invoked
拡張ポイントを理解する
この例には、R2F1DDMStorageAdapterという1つのクラスのみが含まれています。これは、フォームエントリーを格納するためのロジックを提供するDDMStorageAdapterを実装しているサービスです。 現在デプロイされている例では、デフォルトの JSON 実装をラップするだけです: DefaultDDMStorageAdapter。 後で、既存のコードにファイル システム ストレージを追加します。
アダプタークラスをOSGiコンテナに登録する
DDMFileSystemStorageAdapterは、DDMStorageAdapterインターフェイスを実装していますが、OSGiサービスとして登録する必要があります。
@Component(
property = "ddm.storage.adapter.type=r2f1-ddm-storage-adapter",
service = DDMStorageAdapter.class
)
public class R2F1DDMStorageAdapter implements DDMStorageAdapter {
r2f1-ddm-storage-adapter キーは、 src/main/resources/content/Language.properties ファイルと bnd.bnd ファイル内の Provide-Capability ヘッダーによって値 R2F1 Dynamic Data Mapping Storage Adapter にローカライズされます。
serviceコンポーネントプロパティは、実装をDDMStorageAdapterサービスとして登録します。
プロパティddm.storage.adapter.typeは識別子を提供し、サービスが一意のDDMStorageAdapterの実装として登録されるようにします。 他のサービスでも次のように参照できるようになりました。
@Reference(target = "(ddm.storage.adapter.type=r2f1-ddm-storage-adapter)")
private DDMStorageAdapter defaultWrapperDDMStorageAdapter;
DDMStorageAdapterインターフェイスを理解する
このインターフェイスでは、フォームレコードのCRUD操作を処理するために、delete、get、saveの3つのメソッドが必要です(更新ロジックも処理します)。
public DDMStorageAdapterDeleteResponse delete(
DDMStorageAdapterDeleteRequest ddmStorageAdapterDeleteRequest)
throws StorageException;
public DDMStorageAdapterGetResponse get(
DDMStorageAdapterGetRequest ddmStorageAdapterGetRequest)
throws StorageException;
public DDMStorageAdapterSaveResponse save(
DDMStorageAdapterSaveRequest ddmStorageAdapterSaveRequest)
throws StorageException;
各メソッドは、静的な内部 Builder クラスの newBuilder メソッドを使用して構築された DDMStorageAdapter[Save/Get/Delete]Response オブジェクトを返す必要があります。
すべてのメソッドにDDMStorageAdapter[Save/Delete/Get]Requestが渡されます。 リクエストオブジェクトには、有用なコンテキスト情報を返すgetterメソッドが含まれています。
ファイルシステムストレージを実装する
この例では、必要なメソッドをすでに上書きしています。 機能用のプライベート ユーティリティ メソッドを作成し、オーバーライドされたメソッドからそれらを呼び出します。
サービスの依存関係を宣言する
このコードは、OSGiコンテナにデプロイされた2つのサービスに依存しています。 org.osgi.service.component.annotations.Referenceによって提供されるDeclarative Services @Referenceアノテーションを使用して、クラスの最後にこれらの宣言を追加します。
@Reference
private DDMContentLocalService _ddmContentLocalService;
@Reference
private DDMFormValuesSerializerTracker _ddmFormValuesSerializerTracker;
com.liferay.dynamic.data.mapping.service.DDMContentLocalServiceおよびcom.liferay.dynamic.data.mapping.io.DDMFormValuesSerializerTrackerをインポートします。
ロガーを作成する
クラスのロガーを作成し、_log変数に設定します。
private static final Log _log = LogFactoryUtil.getLog(
R2F1DDMStorageAdapter.class);
これは、CRUD メソッドの 1 つが呼び出されるたびにログ メッセージを追加するために使用されます。
ファイル削除を実装する
-
プライベート変数
_PATHNAMEを設定することで、ファイルの保存先をコントロールすることができます。 ここでのパスは、Dockerコンテナ内のLiferayのインストール場所を指しています。private static final String _PATHNAME = "/opt/liferay/form-records"; -
_deleteFileユーティリティメソッドを作成します(java.io.Fileクラスをインポート)。private void _deleteFile(long fileId) { File file = new File(_PATHNAME + "/" + fileId); file.delete(); if (_log.isWarnEnabled()) { _log.warn("Deleted file with the ID " + fileId); } } -
上書きされた
deleteメソッドを探します。returnステートメントの直前に以下を追加します。long fileId = ddmStorageAdapterDeleteRequest.getPrimaryKey(); _deleteFile(fileId);
これで、このコードは、データベース内のコピーを削除する前に、まずファイルシステムからファイルを削除します。
ファイル取得を実装する
get メソッドについても同じ手順に従います。つまり、プライベート ユーティリティ メソッドを作成して、それを呼び出します。
-
_getFileユーティリティメソッドを追加します。private void _getFile(long fileId) throws IOException { try { if (_log.isWarnEnabled()) { _log.warn( "Reading the file with the ID " + fileId + ": " + FileUtil.read(_PATHNAME + "/" + fileId)); } } catch (IOException e) { throw new IOException(e); } }com.liferay.portal.kernel.util.FileUtilおよびjava.io.IOExceptionをインポートします。 -
オーバーライドされた
getメソッド (tryブロック内) で、次のコードをreturnステートメントの直前に挿入し、storageId(ddmStorageAdapterGetRequest.getPrimaryKey()によって取得) をfileIdとして設定し、取得したコンテンツを Liferay ログに出力する_getFileユーティリティ メソッドを呼び出します。long fileId = ddmStorageAdapterGetRequest.getPrimaryKey(); _getFile(fileId);
ファイル作成ロジックを実装する
保存リクエストには、新しいレコードが追加される場合と既存のレコードが更新される場合の2つのタイプがあります。 保存するたびに、 update メソッドは、現在の ddmFormValues コンテンツを使用して既存のファイルを上書きします。
-
_saveFileユーティリティメソッドを作成します。private void _saveFile(long fileId, DDMFormValues formValues) throws IOException { try { String serializedDDMFormValues = _serialize(formValues); File abstractFile = new File(String.valueOf(fileId)); FileUtil.write( _PATHNAME, abstractFile.getName(), serializedDDMFormValues); if (_log.isWarnEnabled()) { _log.warn("Saved a file with the ID" + fileId); } } catch (IOException e) { throw new IOException(e); } }com.liferay.dynamic.data.mapping.storage.DDMFormValuesおよびjava.io.Fileをインポートします。 -
_serializeユーティリティメソッドを作成し、DDMFormValuesオブジェクトをJSONに変換します。private String _serialize(DDMFormValues ddmFormValues) { DDMFormValuesSerializer ddmFormValuesSerializer = _ddmFormValuesSerializerTracker.getDDMFormValuesSerializer("json"); DDMFormValuesSerializerSerializeRequest.Builder builder = DDMFormValuesSerializerSerializeRequest.Builder.newBuilder( ddmFormValues); DDMFormValuesSerializerSerializeResponse ddmFormValuesSerializerSerializeResponse = ddmFormValuesSerializer.serialize(builder.build()); return ddmFormValuesSerializerSerializeResponse.getContent(); }com.liferay.dynamic.data.mapping.io.DDMFormValuesSerializer、com.liferay.dynamic.data.mapping.io.DDMFormValuesSerializerSerializeRequest、およびcom.liferay.dynamic.data.mapping.io.DDMFormValuesSerializerSerializeResponseをインポートします。 -
このロジックと
_saveFileへの呼び出しを、既存のreturnステートメントを置き換えてsaveメソッドに追加します。DDMStorageAdapterSaveResponse defaultStorageAdapterSaveResponse = _defaultStorageAdapter.save(ddmStorageAdapterSaveRequest); long fileId = defaultStorageAdapterSaveResponse.getPrimaryKey(); _saveFile(fileId, ddmStorageAdapterSaveRequest.getDDMFormValues()); return defaultStorageAdapterSaveResponse;最初に
_defaultStorageAdapter.save呼び出しが行われ、新しいフォーム エントリの主キーが作成されます。 このプライマリーキーは、fielIdを作成するためにResponseオブジェクトから取得されます。
ストレージアダプターをデプロイしてテストする
先ほどと同じdeployコマンドを使用してストレージアダプターをデプロイします。 モジュールルートから、以下を実行します。
./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
以下のように動作を確認します。
-
サイト メニュー → コンテンツ → フォームでフォーム アプリケーションに移動します。
-
追加
をクリックしてフォーム ビルダーを開きます。 -
フォーム ビルダー ビューで、 オプション (
) をクリックし、 設定 ウィンドウを開きます。 -
リストを選択フィールド[ストレージの種類を選択する]から、[R2F1 Dynamic Data Mapping Storage Adapter]タイプを選択し、[完了]をクリックします。
-
フォームに テキスト フィールド を追加し、フォームを公開して、数回送信します。
-
フォームレコードがコンテナのファイルシステムに書き込まれたことを確認するには、ログを確認します。 メッセージは次のようになります。
WARN [http-nio-8080-exec-5][R2F1DDMStorageAdapter:82] Acme storage adapter's save method was invoked WARN [http-nio-8080-exec-5][R2F1DDMStorageAdapter:134] Saved a file with the ID42088 WARN [http-nio-8080-exec-5][R2F1DDMStorageAdapter:61] Acme storage adapter's get method was invoked WARN [http-nio-8080-exec-5][R2F1DDMStorageAdapter:112] Reading the file with the ID 42088: {"availableLanguageIds":["en_US"],"defaultLanguageId":"en_US","fieldValues":[{"instanceId":"EJ5UglA1","name":"Field51665758","value":{"en_US":"Stretched limousine"}}]}
さいごに
DDMStorageAdapterを実装することで、フォームレコードを任意のストレージ形式で保存することができます。