Speculation about injection of DataModel

231 views
Skip to first unread message

Heiko Blobner

unread,
Oct 22, 2014, 11:35:00 AM10/22/14
to dataf...@googlegroups.com
I worked through your tutorial on http://www.guigarage.com/2013/12/datafx-controller-framework-preview/.

My question is about the DataModel.
If the flow changes to a view with
  @Inject
  private DataModel model;
implemented, the class DataModel will be instantiated two times. The second time the fields are injected with correct values.
And everytime I come back to this view, the DataModel will be instantiated again (and obviously injected with correct field values).

So my questions:
  1. Why?
    My expectation was that the DataModel is instantiated only once and will be packed into the flow context (similar to registering a custom model into the flow context).
    (My problem is: In my project  I want to install an upnp listener within the DataModel ctor and this object should exists the whole flow lifetime.)
  2. Is there an annotation to mark an clean up method?
  3. Can a custom model be injected into the controller object?
    I have to do each time a
    MyCustomDataModel model = context.getRegisteredObject(MyCustomDataModel.class);
    instead of
    @Inject MyCustomDataModel mod

Hendrik Ebbers

unread,
Oct 22, 2014, 12:15:17 PM10/22/14
to dataf...@googlegroups.com
Hi,

the tutorial is old. All the new tutorials are here: http://www.guigarage.com/2014/05/datafx-8-0-tutorials/
Sadly this tutorials only cover the flow API and not the injection API. I will try to add some more tutorials the next weeks.
About your question:

DataFX supports scopes (like CDI in JEE does). By using the scopes you can define the lifetime of the injected instances. If no scope is defined (as in your case) the default scope / dependent scope will be used. In this scope a new instance of the class will be injected each time. You can define a scope by adding a scope annotation to your model class. Here is an example:

@FlowScoped
public class DataModel {

...

}

This defines that all instances of the DataModel class will have a lifetime of one flow. By doing so anytime a DataModel will be injected in a view controller of a flow the same instance will be used.You can check out the sample in the datafx sources: modules/incubator/samples/src/.../app/

Next to the flow scope the following scopes are provided:

@ViewScoped - lifetime of one view
@ApplicationScoped - lifetime of the application. This will be like a singleton

You can find more information in our JavaOne slides: http://de.slideshare.net/HendrikEbbers/datafx-8-javaone-2014

Last week we released DataFX 8.0 and if you still depend on a beta release (8.0bX) you should update your dependencies to 8.0


Hendrik

Heiko Blobner

unread,
Oct 23, 2014, 2:50:02 AM10/23/14
to dataf...@googlegroups.com
Ok, based on your tutorial #5 I created a DataModel class:

import io.datafx.controller.injection.scopes.FlowScoped;

@FlowScoped
public class DataModel {
    private static int counter = 0;

    public DataModel() {
        counter++;
        print("ctor");
    }

    public void print(String from) {
        System.out.println(from + ": counter = " + counter);
    }
}


and extended each view controller with:
...
    @Inject
    private DataModel model = null;

    @PostConstruct
    public void init() {
        this.model.print("wizard #1");
    }
...

When using FlowScoped, the output looks like:
ctor: counter = 1
ctor: counter = 2
wizard #1: counter = 2
ctor: counter = 3
wizard #2: counter = 3
ctor: counter = 4
wizard #3: counter = 4
ctor: counter = 5
wizard #4: counter = 5
ctor: counter = 6
wizard #5: counter = 6
ctor: counter = 7
wizard #4: counter = 7
ctor: counter = 8
wizard #3: counter = 8

I have the same output with ApplicationScoped.
Where is my mystake?

Hendrik Ebbers

unread,
Oct 23, 2014, 3:25:57 AM10/23/14
to Heiko Blobner, dataf...@googlegroups.com
Hi,

normally I do mainly the same. I type

private DataModel model;

instead of

private DataModel model = null;

but I don’t think that this is a problem. What version of DataFX are you using? Can you send me a small projects that contains the problem?


--
You received this message because you are subscribed to the Google Groups "DataFX" group.
To unsubscribe from this group and stop receiving emails from it, send an email to datafx-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Heiko Blobner

unread,
Oct 23, 2014, 5:07:21 AM10/23/14
to dataf...@googlegroups.com, zwie...@gmail.com
I am using datafx 8.0.

To be honest I quick'n'dirty added the source files to my main project (which uses sbt and and is coded in scala).
But I can provide you an archive. But this can easily be imported by intellij.
Is this ok?

Heiko Blobner

unread,
Oct 23, 2014, 5:15:25 AM10/23/14
to dataf...@googlegroups.com, zwie...@gmail.com
Yes, I think its ok :)
Here it is...
dataFx_inject_project.tar.bz2

Hendrik Ebbers

unread,
Oct 23, 2014, 8:06:07 AM10/23/14
to dataf...@googlegroups.com, zwie...@gmail.com
Hi,

I found the problem :)

Change the Model class to this one:

@FlowScoped
public class DataModel {
    private int counter = 0;

    public DataModel() {
        System.out.println("Constructor");
    }

    public void print(String from) {
        System.out.println(from + ": counter = " + counter++);
    }
}

When using the wizard now you will get an output like:

Constructor
Constructor
wizard #1: counter = 0
Constructor
wizard #2: counter = 1
Constructor
wizard #3: counter = 2
Constructor
wizard #4: counter = 3
Constructor
wizard #5: counter = 4

What does this means?
First we can see that the counter will change. This means that all the views will change the same model instance. Otherwise we would see always "counter=0". So even in your example all the controller instances in the flow share the same model instance. I think this is the behavior that you want / need. You can change it to the Application scope if you need a singleton.
In addition we see that the constructor of the model is called each time when the she change the view. The big question is "why?". All the views share one model and so we don't need to create a new instance and as we see in the example, we don't use a new instance.
When having a look at the internal DataFX sources for the injection a javassist.util.proxy.ProxyFactory is used as a wrapper for each object instance. This will be created whenever new data is injected. Javassist creates a new instance internally that is maybe used for the wrapper. At the moment I have no idea how this is working and how this can be changed. This could cause issues when working with static fields and therefore it should be fixed. For the next DataFX version I plan to create a Injection interface with 2 useable implementations: Google Guice or the current one. Maybe this problem will be resolved when using google Guice. I will have a look at it once I will refactore the APIs. I think this isn't a show stopper for you, right?

Heiko Blobner

unread,
Oct 23, 2014, 8:40:35 AM10/23/14
to dataf...@googlegroups.com, zwie...@gmail.com
No its not a big problem, because now I register my data model directly to the context.
So thank you for your efforts.

I still have one question:
Is there an annotation that I can use to mark a method (inside my data model) which will be called when the app is terminating?

Hendrik Ebbers

unread,
Oct 23, 2014, 8:44:30 AM10/23/14
to dataf...@googlegroups.com, zwie...@gmail.com
When using the flow API you can use the PreDestroy annotation in the controller class. The method will be called when the view changes / flow will be killed. At the moment I don't now if this is working in injected classes, too. Maybe you can test it and if it's not working you can open an issue at bitbucket?
Reply all
Reply to author
Forward
0 new messages