Lifecycle of innerComponent

31 views
Skip to first unread message

Darko Čvagić

unread,
Sep 24, 2015, 7:49:48 AM9/24/15
to JRebirth Users
Hi,

I can't solve the problem about dynamic attaching node to scene.
In my model I have innerComponent, later I want to attach rootNode of that innerComponet to rigtPane of some BorderPane.
I have created DisplayModelWaveBean and used callCommand(AttachModelCommand.class,displayWaveBean).

It is all OK but when I use borderPane.rightProperty.set(null), inerComponent is released.
Same happens when I call callCommand(DetachModelCommand.class,displayWaveBean).

It is also OK, but problem rises when main model get released. InnerComponent have to be released to but it is already released,
and I got error

13:06:37.949 [JIT] ERROR o.j.a.c.e.h.AbstractJrbUncaughtExceptionHandler - Uncaught Exception: null
java.lang.NullPointerException: null
at org.jrebirth.af.core.component.basic.AbstractComponent$4.runInto(AbstractComponent.java:555) ~[BlagajnaFX-1.0-SNAPSHOT.jar:na]
at org.jrebirth.af.core.concurrent.AbstractJrbRunnable.run(AbstractJrbRunnable.java:80) ~[BlagajnaFX-1.0-SNAPSHOT.jar:na]
at org.jrebirth.af.core.concurrent.JRebirthThread.run(JRebirthThread.java:170) ~[BlagajnaFX-1.0-SNAPSHOT.jar:na] 

After that I can't create and show any model?

Am I missed something about attaching model to UI or there is some bug in code?

Tnx.

Sebastien Bordes

unread,
Sep 24, 2015, 8:25:21 AM9/24/15
to jrebirth-users
Darko,

You shouldn't use DMWB to attach the rootNode of an InnerComponent.

The Lifecycle of an InnerComponent depends on the lifecycle of its parent.

I don't know how you create them but you can have a look to an example here:

https://github.com/JRebirth/JRebirth/tree/master/org.jrebirth.af/showcase/analyzer/src/main/java/org/jrebirth/af/showcase/analyzer/ui/workbench

Perhaps we could enhance the InnerComponent declaration to avoid to use static field or an interface holder (an enum wrapper will be great).

Once the inner component rootNode is attached to the MainView rootNode, the InnerComponent Model will not be released until the main one is.

Let me know how you have tried to achieve it and if following code doesn't fix your troubles.



public final class WorkbenchModel extends DefaultModel<WorkbenchModel, WorkbenchView> {

    @LinkInnerComponent
    static InnerComponent<ControlsModel> CONTROLS;

    @LinkInnerComponent
    static InnerComponent<PropertiesModel> PROPERTIES;

    @LinkInnerComponent
    static InnerComponent<EditorModel> EDITOR;
}

public final class WorkbenchView extends DefaultView<WorkbenchModel, BorderPane, WorkbenchController> {

    public WorkbenchView(final WorkbenchModel model) throws CoreException {
        super(model);
    }

    @Override
    protected void initView() {

        getRootNode().setPrefSize(800, 600);

        // Attach the controls view to the top place of the root border pane
        getRootNode().setTop(getModel().getInnerComponent(WorkbenchModel.CONTROLS).getRootNode());

        // Attach the properties view to the right place of the root border pane
        getRootNode().setRight(getModel().getInnerComponent(WorkbenchModel.PROPERTIES).getRootNode());

        // Attach the properties view to the center place of the root border
        // pane
        getRootNode().setCenter(getModel().getInnerComponent(WorkbenchModel.EDITOR).getRootNode());
    }

}


Seb

--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "JRebirth Users".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse jrebirth-user...@googlegroups.com.
Pour obtenir davantage d'options, consultez la page https://groups.google.com/d/optout.



--

Darko Čvagić

unread,
Sep 24, 2015, 4:03:02 PM9/24/15
to JRebirth Users
Hi Seb,


You shouldn't use DMWB to attach the rootNode of an InnerComponent.

Sorry, what is DMWB?
 

The Lifecycle of an InnerComponent depends on the lifecycle of its parent.


Hmm, it should be, but.

My InnerComponent is declared just as You mentioned (not static but, I tried static modifier also)

If I grab inner component rootNode and attach it to the MainView rootNode like this:
getRootNode().setRight(getModel().getInnerComponent(WorkbenchModel.PROPERTIES).getRootNode());
then inner component is not shown. It's method doShow() is not called. 
This is OK for simple UI management where all of UI tree is constructed in initView(), but in my case I have the model and view that is dynamic constructed in 
start() and reload() methods in view, it is not working.

If I call model.callCommand(AttachModelCommand.class,displayWaveBean) then inner component is shown

In either case the problem is that when I detach this inner's component rootNode from main view, the inner component is released.
In word released I don't think to released from memory but model's method release is called.  After that this inner component loose it's reference to local facade and to getKey.

When lifecyle do it's job, main model is released and all inner components get released also.
In that case exception is thrown because inner component was already released. 
Error is thrown in AbstractComponent line 555
getLocalFacade().unregister(getKey());
because getLocalFacade() on already released model is null.

This happens in either case
  1. inner node is attached like:  getRootNode().setRight(getModel().getInnerComponent(WorkbenchModel.PROPERTIES).
    and detached like
    : getRootNode().setRight(null);
  2. inner node is attached like: model.callCommand(AttachModelCommand.class,displayWaveBean)
    and detached like: 
    model.callCommand(DetachModelCommand.class,displayWaveBean)
The reason of this is in prepare method of AbstractView
// Allow to release the model if the root business object doesn't exist anymore
        getRootNode().parentProperty().addListener(new ChangeListener<Node>() {

            @Override
            public void changed(final ObservableValue<? extends Node> observable, final Node oldValue, final Node newValue) {
                if (newValue == null) {
                    getModel().release();
                    getRootNode().parentProperty().removeListener(this);
                }
            }

        });
Model's release method is called whenever rootNode loose it's parent. No meter how it is attached to parent like in case 1 or 2

AND AGAIN
 

The Lifecycle of an InnerComponent depends on the lifecycle of its parent.


Hmm, it should be, but it is not true. Life of inner component ends when it loose it's parent.

It is big problem because after the Error is thrown you can not show any other model even from application menu.
and the biggest problem is you can not close application, main stage is closed but application is still running because there is running thread from AbstractComponent release method.

Tnx for reading. 
Currently I have no answer for this problem.


Darko Čvagić

unread,
Sep 24, 2015, 7:00:56 PM9/24/15
to JRebirth Users
Hi, Seb!

I found workaround and maybe it is a solution!

In AbstractComponent

@Override
    public void release() {

        // Check if some method annotated by Releasable annotation are available
        if (ObjectUtility.checkAllMethodReturnTrue(this, ClassUtility.getAnnotatedMethods(this.getClass(), Releasable.class))) {

            JRebirth.runIntoJIT(new AbstractJrbRunnable("Release " + this.getClass().getCanonicalName()) {

                /**
                 * {@inheritDoc}
                 */
                @Override
                protected void runInto() throws JRebirthThreadException {
                    // try {

                    // setKey(null);

                    // getNotifier().unlistenAll(getWaveReady());

                    callAnnotatedMethod(OnRelease.class);
                    
                    getLocalFacade().unregister(getKey());
                    
                    // thisObject.ready = false;

                    // } catch (final JRebirthThreadException jte) {
                    // LOGGER.error(COMPONENT_RELEASE_ERROR, jte);
                    // }
                }
            });
        }

        // Shall also release all InnerComponent
        for (final Component<?> innerComponent : this.innerComponentMap.values()) {
            innerComponent.release();
        }
    }

the code
getLocalFacade().unregister(getKey());


should be replaced with
if(getLocalFacade()!=null) {
    getLocalFacade().unregister(getKey());
} 

This means "the already unregistered component should not be unregistered again" .

I have tried and it works OK

Now, In my main model I have inner component defined like this
@LinkInnerComponent
InnerComponent<ChildModel> childComponent;

And in view I have DisplayModelWave:
ChildModel rnmodel = model.getInnerComponent(model.childComponent);
DisplayModelWaveBean childWaveBean = DisplayModelWaveBean.create()
                    .showModel(rnmodel)
                    .showModelKey(rnmodel.getKey())
                    .hideModel(rnmodel)
                    .hideModelKey(rnmodel.getKey())
                    .uniquePlaceHolder(rootPane.rightProperty());

Every where in View I can attach ChildModel to the right pane of border pane with:
getModel().callCommand(ShowModelCommand.class,childWaveBean);
or
getModel().callCommand(AttachModelCommand.class,childWaveBean);


and detach it from view with
getModel().callCommand(DetachModelCommand.class,childWaveBean);


It is also working with your approach
rootPane.setRight( getModel().getInnerComponent(getmodel().childComponent).getRootNode() );
and detach with
rootPane.setRight( null )

but, second example is not full because inner component is not SHOWN, just its rootNode is grabbed and inserted in UI tree (View is not started) .

AND AGAIN
 

The Lifecycle of an InnerComponent depends on the lifecycle of its parent.

 No, inner component end its life when it is detached from view, nevertheless of life off its parent.

however InnerComponent is unregistered when model is unregistered if it is not unregistered already with Detach

Seb, I want to hear (read) your opinion about this 'patch' !

Tnx.

Darko. 

Sebastien Bordes

unread,
Sep 25, 2015, 3:25:49 AM9/25/15
to jrebirth-users
Hi Darko,

There are a lot of question to answers, let me know I miss one.

=> DMWB => DisplayModelWaveBean the WaveBean used by ShowModelCommand[PrepareMC, AttachMC] I often use acronym when I named very long class name (Moreover any modern IDE recognize them)

=> In my opinion calling AttachMC is not a good solution but it works right now so you can use it.

Apparently you have put some important code into start, hide, reload methods of your InnerModel. Why didn't you try to call them manually into the same methode of your main Model ?


=> InnerModel wasn't been thought to be added and removed from their parent, this is the reason why you experience auto-release trouble with them, more over sincer InnerModel have been generalized to InnerComponent to support also Command and Service. It's possible to add a method annotated with @Releasable that return tru when you don't want to release the Component (here the InnerModel)

could be

@Releasable
public boolean canRelease(){
return getRootComponent() != null;
}
//Probably required to add some folk


To enhance InnerComponent I will apply some patches:
  1. Fix the potential NPE you mention (it couldn't hurt any duck)
  2. Add a way to disable the AutoRelease (avoiding to attach the listener)
  3. Review the rootComponent and InnerComponent release method to avoid desynchro between release state and garbage collected
  4. Add a way to automatically call the Start method of InnerModel (specific code compared to Command and Service)
  5. Refactor the @LinkInnerComponent, I think that it could be even more easy to use with enum and better annotation.

To conclude, you can use InnerComponent v1 right now, I will prepare InnerComponent v2 with several enhancement to help usage of InnerModel by letting the possibilty to add/remove them dynamically without trouble

Thanks for your exhaustive repo.

Seb


--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "JRebirth Users".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse jrebirth-user...@googlegroups.com.
Pour obtenir davantage d'options, consultez la page https://groups.google.com/d/optout.

Sebastien Bordes

unread,
Sep 28, 2015, 4:48:23 PM9/28/15
to jrebirth-users
A small status point about InnerComponents:

  1. potential NPE fixed
  2. Use @AutoRelease(false) when you don't want to release a model when its rootNode is removed from its parent
  3. Cascading release method call have been reviewed
  4. DoShow and DoHide are now called in cascade (with original wave, usefull to bring some WaveData or WaveBean to InnerModels)
  5. Partially refactored will wit 8.1.0 version to perform a deeper pass

Let me know if you have some remarks or needs.

Reply all
Reply to author
Forward
0 new messages