Trouble on saving altered data

Hi.

Once again I find myself in trouble using nested objects and changes to them. Sorry to ask for your help once more but I am unable to explain what I’m doing wrong (or even what is going wrong). Please bare with me.

To allow for editing and creating objects a number of levels deep, I have created a workaround that saves the parent object (if newly created) before child objects are added. The code below shows the general construction on this (within the editor of the parent):


    @Inject
    private Datasource<Parent> parentDs;

    @Named("childsTable.create")
    private CreateAction childCreateAction;

    private boolean firstSave;

    @Override
    protected void postInit() {
        Parent p = parentDs.getItem();

        // If a required field is empty then this a new item
        if (p.getTitle() == null) {
            firstSave = true;
            // If new, then we do not allow childs to be added until the object is saved
            childCreateAction.setBeforeActionPerformedHandler(() -> {
                // Save the object before adding anything
                if (firstSave) {
                    firstSave = !super.commit();
                }
                return !firstSave;
            });
        }
    }

Doing this works fine and I can create objects multiple levels deep. However, when altering the data of the parent AFTER the child has been added (and thus the parent already saved), there is an error:


java.lang.ClassCastException: org.eclipse.persistence.indirection.IndirectSet cannot be cast to com.haulmont.cuba.core.entity.Entity
	at com.haulmont.cuba.gui.data.impl.DsContextImpl.replaceMasterCopies(DsContextImpl.java:351)
	at com.haulmont.cuba.gui.data.impl.DsContextImpl.addToContext(DsContextImpl.java:329)
	at com.haulmont.cuba.gui.data.impl.DsContextImpl.createCommitContext(DsContextImpl.java:265)
	at com.haulmont.cuba.gui.data.impl.DsContextImpl.commit(DsContextImpl.java:160)
	at com.haulmont.cuba.gui.components.EditorWindowDelegate.commit(EditorWindowDelegate.java:266)
	at com.haulmont.cuba.web.gui.WebWindow$Editor.commitAndClose(WebWindow.java:1578)
	at com.haulmont.cuba.gui.components.AbstractEditor.commitAndClose(AbstractEditor.java:109)
	at com.axxemble.base27.web.risk.RiskEdit.onBtnSaveClick(RiskEdit.java:207)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.haulmont.cuba.gui.xml.DeclarativeAction.actionPerform(DeclarativeAction.java:92)
	at com.haulmont.cuba.web.gui.components.WebButton.performAction(WebButton.java:44)
	at com.haulmont.cuba.web.gui.components.WebButton.lambda$new$61446b05$1(WebButton.java:36)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:510)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:200)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:163)
	at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:1037)
	at com.vaadin.ui.Button.fireClick(Button.java:377)
	at com.haulmont.cuba.web.toolkit.ui.CubaButton.fireClick(CubaButton.java:54)
	at com.vaadin.ui.Button$1.click(Button.java:54)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:158)
	at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:119)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:442)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:414)
	at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
	at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:90)
	at com.haulmont.cuba.web.sys.CubaVaadinServletService$CubaUidlRequestHandler.lambda$synchronizedHandleRequest$0(CubaVaadinServletService.java:314)
	at com.haulmont.cuba.web.sys.CubaVaadinServletService.withUserSession(CubaVaadinServletService.java:196)
	at com.haulmont.cuba.web.sys.CubaVaadinServletService$CubaUidlRequestHandler.synchronizedHandleRequest(CubaVaadinServletService.java:314)
	at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41)
	at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1422)
	at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:384)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.serviceAppRequest(CubaApplicationServlet.java:276)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.service(CubaApplicationServlet.java:185)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.handleNotFiltered(CubaHttpFilter.java:108)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.doFilter(CubaHttpFilter.java:95)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:789)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1437)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)

This error also occurs when doing this only one level deep (parent -> child).

On trial-and-error I found out that when replacing the default super.commitAndClose() with savind the data myself, I managed to avoid the error:


        // Do our own save to avoid issues on the way back
        CommitContext cc = new CommitContext().addInstanceToCommit(r);
        dm.commit(cc);
        // And close (forcefully) - but this doesn't refresh the table on the browser!
        super.close(CLOSE_ACTION_ID, true);

However, in this case the initial browser window is not refreshed and newly created items are not shown. I know this can be overcome as well but it seems I’m on the wrong track here. It does however point out that somehow the default commit is “out-of-sync”?

I tried to set up a test project with this issue but somehow everything works fine in the test project. So, next I tried to strip the existing project to only have the essential code. In this way, I found out that when using only the above, the issue already occurs. Why this is different from the test project I have no idea.

The project has become rather big and I’ve not yet decided to make it open source :wink: so I am unable to attach it to this topic.

Any ideas what might be wrong here? I am hoping that the error (stacktrace) and description rings a bell to you…

Thanks in advance for your help.

Extra information: found out that even without using the save on newly created parents before adding children may introduce the error following this scenario:
1- create parent & save (without adding chidren)
2- reopen parent for editing and add a child (this does not cause the ‘first save’ to trigger)
3- after adding the child, alter parent data and save

same error occurs

Still no idea what causes this behavior but thought you may want to know. Meanwhile I’m still stripping the original project further to match the test project…

I think my main question would be what the error really indicates? Is there a mismatch in properties/objects/status?

Even more trial-and-error, and looking at the sources in the location where the error is occurring, it turns out that I dod not have to include all the datasource attributes in my dsContext.

This comment set me on the right track:


    // Replace the reference to master entity with actual entity containing in the master datasource,
    // because in case of nested property datasources there may be references to cloned master entities.
    protected void replaceMasterCopies(Entity entity, NestedDatasource datasource)  {
         ...
    }

So after modifying the dsContext on the editors as shown below (commented out the unnecessary stuff) , the error disappeared:


    <dsContext>
        <datasource id="riskDs"
                    class="com.axxemble.base27.entity.Risk"
                    view="risk-view">
            <collectionDatasource id="riskFilesDs"
                                  property="files">
<!--
                <datasource id="riskSysFileDs"
                            property="fileFile"/>
-->
            </collectionDatasource>
            <collectionDatasource id="riskCmDs"
                                  property="counterMeasures">
                <collectionDatasource id="cmCommentsDs"
                                      property="comments"/>
                <collectionDatasource id="cmFilesDs"
                                      property="files">
<!--
                    <datasource id="cmSysFileDs"
                                property="fileFile"/>
-->
                </collectionDatasource>

                <collectionDatasource id="cmParticipantsDs"
                                      property="participants"/>
                <!--
                                <datasource id="cmOwnerDs"
                                            property="owner"/>
                                <datasource id="cmInfoSystemDs"
                                            property="relatedSystem"/>
                                <datasource id="cmRiskDs"
                                            property="risk"/>
                -->
            </collectionDatasource>
<!--
            <datasource id="riskOwnerDs"
                        property="owner"/>
            <datasource id="riskInfoSystemDs"
                        property="relatedSystem"/>
            <datasource id="riskRaDs"
                        property="riskAnalysis"/>
-->
        </datasource>
        <collectionDatasource id="staffsDs"
                              class="com.axxemble.base27.entity.Staff"
                              view="staff-view">
            <query>
                <![CDATA[select e from base$Staff e where e.active = true]]>
            </query>
        </collectionDatasource>
        <collectionDatasource id="informationSystemsDs"
                              class="com.axxemble.base27.entity.InformationSystem"
                              view="_local">
            <query>
                <![CDATA[select e from base$InformationSystem e where e.status < 5]]>
            </query>
        </collectionDatasource>
    </dsContext>

I consider my issue solved.

Hi Berend,

I would appreciate if you could still reproduce the issue in a test project. My own attempts to do it failed, but I would like to fix the code for all possible use cases.

Hi Konstantin,

I am happy to help out. Attached is a project that will generate the same issue as I have experienced. It may have a little bit more than really necessary but at least it’s reproducible.

When running the application:

  • create a meeting and set the title
  • add a topic and set the title for this topic
  • add a comment to this topic and set the comment text
  • save comment (confirm to save on dialog)
  • set related value on topic (e.g. “1”)
  • save topic
  • set related value on meeting (e.g. “1”)
  • save topic

error occurs

Hope this helps.

composition.zip (207.1K)

Thank you Berend,

I’ve fixed the issue for your case. The cause was in declaring a datasource for the reference to the master entity.

:ticket: See the following issue in our bug tracker:

https://youtrack.cuba-platform.com/issue/PL-8759