Mistre83
Mistre83

Reputation: 2827

JavaFX8 - Thread task with Guice

I'm new (very new) to Guice and JavaFX. I'm building an application that have a thread listening for socket connection and, after an event is received, the thread store value on ObservableArrayList() and the application will notify them to the user.

My problem is how to structure all this behaviour, and how to "share" the ObservableList from the thread and the JavaFX Controller.

I'm reading about Guice that could help to decouple the new creation of an object.

I've tried to setup something, but the @Inject property is null on my runnable task:

Guice Module:

public class AppGuiceModule extends AbstractModule{

    @Override
    protected void configure() {
        bind(EventsDAO.class).toInstance(new EventsDAO());
    }

}

EventsDAO (that have the ObservableArrayList )

@Singleton
public class EventsDAO {
     private ObservableList<ScheduledEvent> localCache = FXCollections.observableArrayList();

     public void addEvent(ScheduledEvent event) {
         localCache.add(event);
     }

     public void removeEvent(ScheduledEvent event) {
         this.localCache.remove(event);
     }
}

With two this, i in my main i go to create the injector:

@Override
    public void start(Stage stage) throws Exception {

        Injector injector = Guice.createInjector(new AppGuiceModule());

        Platform.setImplicitExit(false);

        Thread t = new Thread(new EventsReceiverTask());
        t.start();
        .....

Now, in the Runnable object, i would to @Inject EventsDAO (to save new events) and @Inject this too in my Controller, adding to localCache a listener (yes localCache is private, i will provide a getter).

The runnable object:

public class EventsReceiverTask implements Runnable {

    private static final int port = 4020;

    @Inject
    EventsDAO eventsDao; // This is null, why not injected ?

    private ServerSocket serverSocket;
    private Stage notificationStage;

    public EventsReceiverTask() {
        try {
            this.serverSocket = new ServerSocket(port);
            this.notificationStage = new Stage();

            eventsDao.addEvent(new ScheduledEvent());
        } catch (IOException ex) {
            Logger.getLogger(EventsReceiverTask.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

I dont know if this is the correct way to implement a "producer-consumer" in JavaFX, but i have no idea how to share that components, witthout creating tedious getter and setter, with all statics methods.

Upvotes: 1

Views: 1150

Answers (2)

durron597
durron597

Reputation: 32323

Your problem shows exactly why it is preferred not to use new in a dependency injected system, when possible.

Injections can only occur when the injection framework is the one creating the objects. When you call new, you are creating them, not the framework.

There are two ways to have Guice create an object for you:

  1. Use injector.getInstance(Foo.class);

With inversion of control, the flow depends on the object graph that is built up during program execution. Such a dynamic flow is made possible by object interactions being defined through abstractions.

  1. Have Guice create your objects for you as injections.
    • This is exactly what the @Inject annotation does; instruct Guice to create the object for you in your bound classes.

Generally speaking, a class with a name like EventsReceiverTask is not the sort of top level class that you want to be creating in your main method. They have names more like EventService, to which you would inject a Provider<EventsReceiverTask> that is capable of creating new tasks for you, all of which will be injected with your EventsDAO properly.


Side note: you didn't ask about this, but when you do this, in your module:

bind(EventsDAO.class).toInstance(new EventsDAO());

You are overriding the scope of the binding that you attempted to specify with the @Singleton annotation in your class definition. If you want this object to actually be a singleton, you **must also specify the @Singleton binding in your module, e.g.

bind(EventsDAO.class).toInstance(new EventsDAO()).in(Singleton.class);

From the documentation:

If there's conflicting scopes on a type and in a bind() statement, the bind() statement's scope will be used. If a type is annotated with a scope that you don't want, bind it to Scopes.NO_SCOPE.

Upvotes: 5

Mike Curry
Mike Curry

Reputation: 1609

You never inject the member fields to the task object, so the value is null.

You need something like the inject part below so that Guice actually injects the field value. You might want further refactoring to make this better, but at the most basic level, Guice won't inject the field until you tell it to.

public void start(Stage stage) throws Exception {

    Injector injector = Guice.createInjector(new AppGuiceModule());

    Platform.setImplicitExit(false);

    EventsReceiverTask task = new EventsReceiverTask();

    // You need something like this so that Guice injects the members into the object.
    injector.injectMembers(task);

    Thread t = new Thread(task);
    t.start();
    ....

Upvotes: 0

Related Questions