HV000116: The object to be validated must not be null - what's wrong?

Hi:

I’ve got a edit window that extends AbstractEditor. The window contains a cascading master/detail/detail relationship represented as nested datasources and displayed as a number of tables. The master level is called Schematic. The first detail level is called Game. The final detail level is called Step. So, a Game is made of Steps and a Schematic is made of Games. Everything works pretty well. I can add Schematics, Games and Steps. I can remove Steps and Games. However, when I go to remove the LAST Schematic and make the screen blank again, I get the error message in the title. What am I doing wrong? Why is the framework trying to validate a null entity?

The full dump is this:



java.lang.IllegalArgumentException: HV000116: The object to be validated must not be null.
	at org.hibernate.validator.internal.util.Contracts.assertNotNull(Contracts.java:41)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:191)
	at com.haulmont.cuba.gui.components.EditorWindowDelegate.validateAdditionalRules(EditorWindowDelegate.java:337)
	at com.haulmont.cuba.web.gui.WebWindow$Editor.validateAdditionalRules(WebWindow.java:1600)
	at com.haulmont.cuba.web.gui.WebWindow.validateAll(WebWindow.java:364)
	at com.haulmont.cuba.gui.components.AbstractWindow.validateAll(AbstractWindow.java:208)
	at com.haulmont.cuba.web.gui.WebWindow$Editor.commit(WebWindow.java:1568)
	at com.haulmont.cuba.web.gui.WebWindow$Editor.commit(WebWindow.java:1563)
	at com.haulmont.cuba.gui.components.AbstractEditor.commit(AbstractEditor.java:86)
	at com.haulmont.cuba.gui.components.EditorWindowDelegate$2.actionPerform(EditorWindowDelegate.java:103)
	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.GeneratedMethodAccessor199.invoke(Unknown Source)
	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.GeneratedMethodAccessor198.invoke(Unknown Source)
	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:444)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:409)
	at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
	at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:90)
	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:385)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.serviceAppRequest(CubaApplicationServlet.java:278)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.service(CubaApplicationServlet.java:187)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
	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:240)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
	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:748)

The code is rather long, but here it is:

  • <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  • [code]





    <![CDATA[select e from rade$Schematic e]]>































































































    [/code]

    The code behind the form is:

    
    
    <b>package com.paslists.rade.web.schematic;</b>
    <b></b>
    <b>import com.haulmont.cuba.core.global.Metadata;</b>
    <b>import com.haulmont.cuba.gui.components.AbstractEditor;</b>
    <b>import com.haulmont.cuba.gui.components.Component;</b>
    <b>import com.haulmont.cuba.gui.components.LookupField;</b>
    <b>import com.haulmont.cuba.gui.components.Table;</b>
    <b>import com.haulmont.cuba.gui.data.CollectionDatasource;</b>
    <b>import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;</b>
    <b>import com.paslists.rade.entity.Game;</b>
    <b>import com.paslists.rade.entity.Schematic;</b>
    <b>import com.paslists.rade.entity.SchematicStateOmit;</b>
    <b>import com.paslists.rade.entity.Step;</b>
    <b>import com.paslists.rade.entity.key.SchematicstateomitsCompKey;</b>
    <b>import com.paslists.rade.service.CountryService;</b>
    <b></b>
    <b>import javax.inject.Inject;</b>
    <b>import java.util.Map;</b>
    <b></b>
    <b>public class SchematicEdit extends AbstractEditor<Schematic> {</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private Metadata metadata;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private CollectionDatasource<Schematic, Long> schematicsDs;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private CollectionDatasource<SchematicStateOmit, SchematicstateomitsCompKey> stateomitsDs;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private CollectionDatasource<Game, Long> gamesDs;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private CollectionDatasource<Step, Long> stepsDs;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private Table<Schematic> tblSchematics;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private Table<SchematicStateOmit> tblStateOmits;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private Table<Game> tblGames;</b>
    <b></b>
    <b>    @Inject</b>
    <b>    private Table<Step> tblSteps;</b>
    <b></b>
    <b>    public void onBtnCreateSchematicClick() {</b>
    <b>      Schematic schematic = metadata.create(Schematic.class);</b>
    <b>      schematicsDs.addItem(schematic);</b>
    <b>    }</b>
    <b></b>
    <b>    public void onBtnRemoveSchematicClick() {</b>
    <b>        schematicsDs.removeItem(tblSchematics.getSingleSelected());</b>
    <b>    }</b>
    <b></b>
    <b>    public void onBtnCreateStateomitClick() {</b>
    <b>        SchematicStateOmit stateomit = metadata.create(SchematicStateOmit.class);</b>
    <b>        stateomit.setSchematic(tblSchematics.getSingleSelected());</b>
    <b>        stateomitsDs.addItem(stateomit);</b>
    <b>    }</b>
    <b></b>
    <b>    public void onBtnRemoveStateomitClick() {</b>
    <b>        stateomitsDs.removeItem(tblStateOmits.getSingleSelected());</b>
    <b>    }</b>
    <b>    </b>
    <b></b>
    <b>    public void onBtnCreateGameClick() {</b>
    <b>        Game game = metadata.create(Game.class);</b>
    <b>        game.setSchematicid(tblSchematics.getSingleSelected());</b>
    <b>        gamesDs.addItem(game);</b>
    <b>    }</b>
    <b></b>
    <b>    public void onBtnRemoveGameClick() {</b>
    <b>        gamesDs.removeItem(tblGames.getSingleSelected());</b>
    <b>    }</b>
    <b></b>
    <b>    public void onBtnCreateStepClick() {</b>
    <b>        Step step = metadata.create(Step.class);</b>
    <b>        step.setGameid(tblGames.getSingleSelected());</b>
    <b>        stepsDs.addItem(step);</b>
    <b>    }</b>
    <b></b>
    <b>    public void onBtnRemoveStepClick() {</b>
    <b>        stepsDs.removeItem(tblSteps.getSingleSelected());</b>
    <b>    }</b>
    <b>}</b>
    

    In this case I recommend that you simply replace parent of your custom window to AbstractWindow. It does not start automatic Cross-Field validation. It is convenient base class for custom windows that does not behave as a typical Lookup / Editor windows.

    Perfect! Thank you! I will give it a shot.

    Hi,

    It seems that at some point of execution item of editor is null, but it should not happen. As a workaround I recommend that you disable Cross-Field validation in the problem editor using special Editor.setCrossFieldValidate() method. You can call it from init() method of a screen controller.

    Any way, I advice that you debug this case and try to figure out why is your item of editor is null. It would be great if you share a small demo project (not your private project) along with the accurate steps of reproduction, in that case we can try to find the problem too.

    Sorry for the delay getting back to you… I believe the issue is that I am using the AbstractEditor class without an AbstractLookup class first - so there is NO item in the editor. Instead, I have a bunch of nested data sources attached to a table at the top of the page. When you create/edit a table row at the top, then the nested tables contain details for that information.

    When the top level table is empty or the last entry has been deleted, then there is NO item attached any more.

    I did find a workaround. I’m not sure if it is a best practice or not, but here’s what I did:

    
        @Override
        public boolean validateAll() {
    
            if (schematicsDs.size() == 0 && gamesDs.size() == 0 && stepsDs.size() == 0 && stateomitsDs.size() == 0) {
                return true;
            }
            else {
                return super.validateAll();
            }
        }
    
        @Override
        protected boolean postCommit(boolean committed, boolean close) {
    
            if (schematicsDs.size() == 0 && gamesDs.size() == 0 && stepsDs.size() == 0 && stateomitsDs.size() == 0) {
                return true;
            }
            else {
                return super.postCommit(committed, close);
            }
        }
    

    So, if all of the datasources are now empty, then force validation success. Similarly, after commit, if all datasources are empty, force success.

    Is there a better solution?