Writing a Form Storage Adapter
Liferay DXP 7.3 and Liferay DXP 7.2 versions that include the fix for LPS-97208 (planned for Liferay DXP 7.2 SP3)
By default, forms are stored as JSON in Liferay DXP’s database. This example shows you how to implement a new storage adapter to inject custom logic into a form record persistence event.
The default storage adapter saves form records in the Liferay DXP database as JSON content. Next, add logic to store each Form Record on the file system.
Examine a Running DDM Storage Adapter
To see how storage adapters work, deploy an example and then add some form data using the example adapter.
Deploy the Example
Start a new Liferay instance by running
Sign in to Liferay at http://localhost:8080. Use the email address test@liferay.com and the password test. When prompted, change the password to learn.
Then, follow these steps:
-
Download and unzip the DDM Storage Adapter project.
-
From the module root, build and deploy.
TipThis command is the same as copying the deployed jars to
/opt/liferay/osgi/modules
on the Docker container. -
Confirm the deployment in the Liferay Docker container console.
Use the Deployed Storage Adapter
-
Open your browser to http://localhost:8080.
-
Go to the Forms application in Site Menu → Content & Data → Forms.
-
Click Add (
) to open the Form Builder.
-
In the Form Builder view, click Options (
) and open the Settings window.
-
Under Select a Storage Type, choose the R2F1 Dynamic Data Mapping Storage Adapter type and click Done.
-
Add a Text Field to the form, publish the form, and submit it a few times.
-
To verify the form data were persisted, go to the Form’s Records:
From Site Menu → Content → Forms, click the Form’s Actions button (
), then View Entries.
-
Additionally, logging is provided in each CRUD method to demonstrate that the sample’s methods are being invoked.
Understand the Extension Point
The example contains only one class: R2F1DDMStorageAdapter
, a service implementing a DDMStorageAdapter
to provide logic for storing Form Entries. The deployed example currently just wraps the default JSON implementation: DefaultDDMStorageAdapter
. Later, add file system storage to the code that’s already here.
Register the Adapter Class with the OSGi Container
The DDMFileSystemStorageAdapter
implements the DDMStorageAdapter
interface, but must be registered as an OSGi service:
The r2f1-ddm-storage-adapter
key is localized into the value R2F1 Dynamic Data Mapping Storage Adapter
by the src/main/resources/content/Language.properties
file and the Provide-Capability
header in the bnd.bnd
file.
The service
component property registers your implementation as a DDMStorageAdapter
service.
The property ddm.storage.adapter.type
provides an identifier so that your service is registered as a unique DDMStorageAdapter
implementation. Other services can now reference it like this:
Understand the DDMStorageAdapter Interface
The interface requires three methods to handle CRUD operations on form records: delete
, get
, and save
(which also handles update logic).
Each method must return a DDMStorageAdapter[Save/Get/Delete]Response object, constructed using a static inner Builder
class’s newBuilder
method.
All methods are passed a DDMStorageAdapter[Save/Delete/Get]Request
. The request objects contain getter methods that return useful contextual information.
Implement File System Storage
The example already overrides the necessary methods. Create private utility methods for your functionality and then call them from the overridden methods.
Declare the Service Dependencies
This code relies on two services deployed to an OSGi container. Add these declarations at the end of the class using Declarative Services @Reference
annotations, provided by org.osgi.service.component.annotations.Reference
.
Import com.liferay.dynamic.data.mapping.service.DDMContentLocalService
and com.liferay.dynamic.data.mapping.io.DDMFormValuesSerializerTracker
.
Create a Logger
Create a logger for the class and set it in a _log
variable:
This is used to add some log messages each time one of the CRUD methods is invoked.
Implement File Deletion
-
Set a private variable
_PATHNAME
so you can control where the files are stored. The path here points to the Liferay install location in the Docker container. -
Create a
_deleteFile
utility method (import thejava.io.File
class): -
Find the overridden
delete
method. Immediately before thereturn
statement, add
Now the code first deletes the file from the file system before it deletes the copy in the database.
Implement File Retrieval
Follow the same procedure for the get
method: create a private utility method and then call it.
-
Add the
_getFile
utility method:Import
com.liferay.portal.kernel.util.FileUtil
andjava.io.IOException
. -
In the overridden
get
method (inside thetry
block), insert the following immediately before thereturn
statement, setting thestorageId
(retrieved byddmStorageAdapterGetRequest.getPrimaryKey()
) as thefileId
and calling the_getFile
utility method which prints the retrieved content to the Liferay log.
Implement File Creation Logic
There are two types of save requests: 1) a new record is added or 2) an existing record is updated. At each save, the update
method overwrites the existing file, using the current ddmFormValues
content.
-
Create a
_saveFile
utility method:Import
com.liferay.dynamic.data.mapping.storage.DDMFormValues
andjava.io.File
. -
Create a
_serialize
utility method to convert theDDMFormValues
object to JSON:Import
com.liferay.dynamic.data.mapping.io.DDMFormValuesSerializer
,com.liferay.dynamic.data.mapping.io.DDMFormValuesSerializerSerializeRequest
, andcom.liferay.dynamic.data.mapping.io.DDMFormValuesSerializerSerializeResponse
. -
Add this logic and the call to
_saveFile
to thesave
method by replacing the existingreturn
statement:The
_defaultStorageAdapter.save
call is made first, so that a Primary Key is created for a new form entry. This Primary Key is retrieved from theResponse
object to create thefielId
.
Deploy and Test the Storage Adapter
Use the same deploy
command as earlier to deploy the Storage Adapter. From the module root run
Now verify that it’s working:
-
Go to the Forms application in Site Menu → Content → Forms.
-
Click Add
to open the Form Builder.
-
In the Form Builder view, click Options (
) and open the Settings window.
-
From the select list field called Select a Storage Type, choose the R2F1 Dynamic Data Mapping Storage Adapter type and click Done.
-
Add a Text Field to the form, publish the form, and submit it a few times.
-
To verify the form records were written to the container’s file system, check the log. The messages should look like this:
Conclusion
By implementing a DDMStorageAdapter
, you can save forms records in any storage format you want.