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

まず デフォルトストレージアダプタ がどのように Liferay DXP データベースのフォームレコードを JSON コンテンツとして保存するか見てみましょう。 そして、各フォームレコードをファイルシステムに保存するロジックを追加します。
実行中のDDMストレージアダプターを調べる
ストレージアダプターの動作を確認するために、サンプルをデプロイし、サンプルアダプターを使用していくつかのフォームデータを追加します。
サンプルをデプロイする
新しいLiferay インスタンスを起動し、以下を実行します。
docker run -it -m 8g -p 8080:8080 。
http://localhost:8080でLiferayへのサインインします。 メールアドレス test@liferay.com とパスワード test を使用してください。 プロンプトが表示されたら、パスワードを learn に変更します。
次に、以下の手順を実行します。
- 
DDM Storage Adapter プロジェクト をダウンロードして解凍します。 curl https://resources.learn.liferay.com/dxp/latest/en/process-automation/forms/developer-guide/liferay-r2f1.zip -Ounzip liferay-r2f1.zip
- 
モジュールのルートから、ビルドおよびデプロイします。 ./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)tipこのコマンドは、デプロイされたjarをDockerコンテナの/opt/liferay/osgi/modulesにコピーするのと同じです。 
- 
Liferay Dockerコンテナコンソールでデプロイを確認します。 STARTED com.acme.r2f1.impl_1.0.0 [1009]
デプロイされたストレージアダプターを使用する
- 
ブラウザでhttp://localhost:8080を開きます。 
- 
[サイトメニュー] → [コンテンツ & データ] → [フォーム] のフォームアプリケーションに移動します。 
- 
追加 ボタン(  )をクリックして、フォームビルダーを開きます。 )をクリックして、フォームビルダーを開きます。
- 
[フォームビルダー]ビューで、 オプション ボタン(  )をクリックし、 [Settings] ウィンドウを開きます。 )をクリックし、 [Settings] ウィンドウを開きます。
- 
[ストレージの種類を選択する] で、 [R2F1 Dynamic Data Mapping Storage Adapter] タイプを選択し、 [Done] をクリックします。 
- 
フォームにテキストフィールドを追加し、フォームを公開して、何度か送信します。 
- 
フォームデータが保持されていることを確認するには、フォームのレコードに移動します。 
[サイトメニュー] → [コンテンツ] → [フォーム] から、フォームの アクション ボタン( )をクリックして、 [エントリの参照] をクリックします。
)をクリックして、 [エントリの参照] をクリックします。

- 
さらに、各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;
各メソッドは DDMStorageAdapter[ Save / Get / Delete ]Response オブジェクトを返す必要があり、静的なインナー Builder クラスの newBuilder メソッドを使用して構築されます。
すべてのメソッドに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つのタイプがあります。 保存するたびに、現在のddmFormValuesコンテンツを使用して、updateメソッドで既存のファイルを上書きします。
- 
_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)
以下のように動作を確認します。
- 
サイトメニュー → コンテンツ → フォーム にあるフォームアプリケーションにアクセスします。 
- 
追加 ボタン  をクリックして、フォームビルダーを開きます。 をクリックして、フォームビルダーを開きます。
- 
[フォームビルダー]ビューで、 オプション ボタン(  )をクリックし、 [Settings] ウィンドウを開きます。 )をクリックし、 [Settings] ウィンドウを開きます。
- 
リストを選択フィールド [ストレージの種類を選択する] から、 [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を実装することで、フォームレコードを任意のストレージ形式で保存することができます。