Uploaded image for project: 'PUBLIC - Liferay Portal Community Edition'
  1. PUBLIC - Liferay Portal Community Edition
  2. LPS-102672

Liferay Forms assumes that a custom DDMStorageAdapter will persist the submitted form to the Liferay database

    Details

    • Type: Bug
    • Status: Open
    • Resolution: Unresolved
    • Affects Version/s: 7.2.0 GA1, 7.2.10 DXP GA1, 7.2.10 DXP FP1
    • Fix Version/s: None
    • Component/s: Forms
    • Labels:
      None

      Description

      Problem Background

      Liferay Forms has a feature that lets the developer save form submissions using a custom approach by creating a class that implements DDMStorageAdapter. However, Liferay Forms assumes that a custom DDMStorageAdapter will persist the submitted form to the Liferay database, which is an incorrect assumption given that the purpose of a custom storage adapter is to store the form submission somewhere other than the Liferay database.

      The reason why this happens is likely because of the following code in the DDMFormInstanceRecordLocalServiceImpl.createDDMContent(...) method, which attempts to record an association between the form submission and the primary key returned by the custom DDMStorageAdapter, which is assumed to be a valid primary key in the Liferay database:

      DDMFormInstanceRecordLocalServiceImpl.java
      		long primaryKey = ddmStorageAdapterSaveResponse.getPrimaryKey();
      
      		DDMFormInstance ddmFormInstance =
      			ddmFormInstancePersistence.findByPrimaryKey(ddmFormInstanceId);
      
      		DDMStructure ddmStructure = ddmFormInstance.getStructure();
      
      		DDMStructureVersion ddmStructureVersion =
      			ddmStructure.getLatestStructureVersion();
      
      		_ddmStorageLinkLocalService.addStorageLink(
      			_portal.getClassNameId(DDMContent.class.getName()), primaryKey,
      			ddmStructureVersion.getStructureVersionId(), serviceContext);
      

      Steps to Reproduce

      1. Create a new module with a CustomStorageAdapter that looks like this and deploy the module to $LIFERAY_HOME/osgi/modules:

      CustomStorageAdapter.java
      package com.mycompany;
      
      import com.liferay.dynamic.data.mapping.exception.StorageException;
      import com.liferay.dynamic.data.mapping.storage.DDMStorageAdapter;
      import com.liferay.dynamic.data.mapping.storage.DDMStorageAdapterDeleteRequest;
      import com.liferay.dynamic.data.mapping.storage.DDMStorageAdapterDeleteResponse;
      import com.liferay.dynamic.data.mapping.storage.DDMStorageAdapterGetRequest;
      import com.liferay.dynamic.data.mapping.storage.DDMStorageAdapterGetResponse;
      import com.liferay.dynamic.data.mapping.storage.DDMStorageAdapterSaveRequest;
      import com.liferay.dynamic.data.mapping.storage.DDMStorageAdapterSaveResponse;
      import org.osgi.service.component.annotations.Component;
      
      @Component(
      	immediate = true, property = {"ddm.storage.adapter.type=Custom"},
      	service = DDMStorageAdapter.class
      )
      public class CustomStorageAdapter implements DDMStorageAdapter {
      
      	@Override
      	public DDMStorageAdapterSaveResponse save(
      		DDMStorageAdapterSaveRequest ddmStorageAdapterSaveRequest)
      		throws StorageException {
      
      		long primaryKey = System.nanoTime();
      		DDMStorageAdapterSaveResponse.Builder builder =
      			DDMStorageAdapterSaveResponse.Builder.newBuilder(primaryKey);
      
      		return builder.build();
      	}
      
      	@Override
      	public DDMStorageAdapterDeleteResponse delete(
      		DDMStorageAdapterDeleteRequest ddmStorageAdapterDeleteRequest)
      		throws StorageException {
      		return null;
      	}
      
      	@Override
      	public DDMStorageAdapterGetResponse get(
      		DDMStorageAdapterGetRequest ddmStorageAdapterGetRequest)
      		throws StorageException {
      		return null;
      	}
      }
      

      2. Click on "Content & Data -> Forms" in the Control Panel and create a form with a single text field.

      3. Before hitting the Save button, click the "..." button at the top of the page and then click on "Settings".

      4. Select "Custom" from the dropdown list for the custom storage option.

      5. Disable the autosave.

      6. Set the name of the form to be "MyForm"

      7. Save the form.

      8. Publish the form.

      9. Add the Forms portlet to a portal page and select "MyForm" to be displayed.

      10. Fill out the form and click the Save button.

      11. Click on "Content & Data -> Forms" in the Control Panel.

      12. On the "MyForm" entry, click the "..." button next to the "MyForm" row and click on "View Entries"

      Expected Results

      No error message displayed, and no items in the list, since the form submission was not stored in the Liferay database.

      Actual Results

      "Form Entries is temporarily unavailable" is displayed at the top of the page and the console log contains something similar to the following:

      com.liferay.dynamic.data.mapping.exception.NoSuchContentException: No DDMContent exists with the primary key 80411
      	at com.liferay.dynamic.data.mapping.service.persistence.impl.DDMContentPersistenceImpl.findByPrimaryKey(DDMContentPersistenceImpl.java:2987)
      	at com.liferay.dynamic.data.mapping.service.persistence.impl.DDMContentPersistenceImpl.findByPrimaryKey(DDMContentPersistenceImpl.java:3005)
      	at com.liferay.dynamic.data.mapping.service.impl.DDMContentLocalServiceImpl.getContent(DDMContentLocalServiceImpl.java:86)
      	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at com.liferay.portal.spring.aop.AopMethodInvocationImpl.proceed(AopMethodInvocationImpl.java:50)
      	at com.liferay.portal.spring.transaction.TransactionInterceptor.invoke(TransactionInterceptor.java:69)
      	at com.liferay.portal.spring.aop.AopMethodInvocationImpl.proceed(AopMethodInvocationImpl.java:57)
      	at com.liferay.portal.spring.aop.AopInvocationHandler.invoke(AopInvocationHandler.java:49)
      	at com.liferay.dynamic.data.mapping.internal.storage.DDMJSONStorageAdapter.get(DDMJSONStorageAdapter.java:76)
      	at com.liferay.dynamic.data.mapping.service.impl.DDMFormInstanceRecordLocalServiceImpl.getDDMFormValues(DDMFormInstanceRecordLocalServiceImpl.java:267)
      	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at com.liferay.portal.spring.aop.AopMethodInvocationImpl.proceed(AopMethodInvocationImpl.java:50)
      	at com.liferay.portal.spring.transaction.TransactionInterceptor.invoke(TransactionInterceptor.java:69)
      	at com.liferay.portal.spring.aop.AopMethodInvocationImpl.proceed(AopMethodInvocationImpl.java:57)
      	at com.liferay.portal.spring.aop.AopInvocationHandler.invoke(AopInvocationHandler.java:49)
      	at com.liferay.dynamic.data.mapping.service.DDMFormInstanceRecordLocalServiceUtil.getDDMFormValues(DDMFormInstanceRecordLocalServiceUtil.java:358)
      	at com.liferay.dynamic.data.mapping.model.impl.DDMFormInstanceRecordVersionImpl.getDDMFormValues(DDMFormInstanceRecordVersionImpl.java:48)
      	at com.liferay.dynamic.data.mapping.form.web.internal.display.context.DDMFormViewFormInstanceRecordsDisplayContext.getDDMFormValues(DDMFormViewFormInstanceRecordsDisplayContext.java:174)
      

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              neil.griffin Neil Griffin
              Participants of an Issue:
              Recent user:
              Neil Griffin
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Days since last comment:
                9 weeks, 3 days ago

                Packages

                Version Package