problems with loading stuff/bootstrapping onModuleLoad triggered by an event

104 views
Skip to first unread message

tanteanni

unread,
Oct 26, 2011, 10:11:13 AM10/26/11
to google-we...@googlegroups.com
the scenario is as follows (i use gin):
- onModuleLoad instantiates a ApplicationManager and runs start() on it.
- ApplicationManager sets up all activitymanagers and the layout, gives every manager some display area, sets up history handler and "start history" (historyHandler.handleCurrentHistory();)

all is running fine but i want the application manager to defer the loading until an event (data-loaded-event). so i make the manager listen to it and all the setup-stuff is done in a new method "continueStart()"; called on this event. the former start() (called in onModuleLoad) just displays a loading widget.
but now no activity is started. i debugged into the code. it seems that with this "deferred loading" the ActivityManagers don't hear the PlaceChangeEvent fired by "historyHandler.handleCurrentHistory();" (The Eventbus emitting this event is the same the ActivityManager registered on)

So what's going on here?

my only explanation at the moment is, that this could be related to the fact that all the application setup stuff happens after onModuleLoad has finished (it only calls start(), and in "background" applicationManager is waiting for an event and does the reals setup after "onModulLoad()")

so what to do - thx in advance

tanteanni

unread,
Oct 27, 2011, 5:02:15 AM10/27/11
to google-we...@googlegroups.com
i made an (nearly)minimal example showing this problem. it is a simple sandbox app where i made some experiments with RequestFactory and Activities and Places (no DI, no Gin). The crux is now, that onModuleLoad is starting an AppManager.start(). The AppManager shows only "wait" until data is received and event is fired. I f event is received all the activities/places machinery is started - but here is the problem: no activity is started but PlaceChangeEvent is fired. But all later PalceChangeEvents are handled correctly. So what to do to reproduce:
Start the app see "wait", wait see "received data" (the data sending service waits 10seconds) - no activities started.
Change id in url from 1 to 2 or vice versa - activities are started correctly.
or change start() in AppManager and run "continueStart()" directly:
    public void start() {
//        if (dataReady) {
            continueStart();
//        } else {
//            appLayout.add(new Label("wait!"));
//        }
    }

So why the activities are not started on first load? what happens here?
Sandkasten.7z

Thomas Broyer

unread,
Oct 27, 2011, 5:38:05 AM10/27/11
to google-we...@googlegroups.com
I think that's because the code now runs from within an event, so handlers addition/removal is done after the event is processed; but handleCurrentHistory() fires events synchronously, so the event handlers are not yet registered.

As a workaround, defer continueStart() a bit more so it executes outside the event handling logic, within a Scheduler.scheduleFinally() for instance.

tanteanni

unread,
Oct 27, 2011, 6:34:00 AM10/27/11
to google-we...@googlegroups.com
that was my first guess but i debugged into ActivityManager's- register event code. all activity managers are instatiated and have themselve registered on placechange events before the deferred  handleCurrentHistory() occurs. Or the other way around: if i do not defer the start all is working fine.
it seem only to work if all stuff happens inside onModuleLoad(). and it seem that problem only occur if handleCurrentHistory() happens after onModuleLoad and all the binding and instantiation in onModuleLoad.
(but i am not sure about all that)

Thomas Broyer

unread,
Oct 27, 2011, 8:27:26 AM10/27/11
to google-we...@googlegroups.com
What I meant is that, assuming you're using a SimpleEventBus, the PlaceChangeRequestHandler-s and PlaceChangeHandler-s are in the "deferredDeltas" list, they're not yet fully registered: http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/web/bindery/event/shared/SimpleEventBus.java
You need to defer the handleCurrentHistory() call outside the event dispatching; but you can actually defer the whole continueStart if you prefer (the handlers would then be registered right away, with no queuing in the deferredDeltas)

tanteanni

unread,
Oct 27, 2011, 8:58:12 AM10/27/11
to google-we...@googlegroups.com

at the moment the "whole continueStart" is deferred at the end of this "continueStart"  handleCurrentHistory() is called, after instantiating activity mappers and managers. Before that (on construction time of AppManager)  EventBus and PlaceController are instantiated.
you could look in the code i attached in 2nd post. which lines of code should be moved where?

(i still don't understand why it works with out the delay but fails with delay)

in my real code i tried also to delay the start with a Timer - no matter how long the start is deferred it works fine. but as soon as continueStart is run after onModuleLoad() - eventBased delay it fails. in meantime deferred it "synchronously" by implementing start() like this:
        new SchedulerImpl().scheduleDeferred(new ScheduledCommand() {

            @Override
            public void execute() {
                while (!(ttReady && auReady)) {
                    System.out.println("waiting");
                }
                continueStart();

            }
        });

This is working fine. The time of delaying is the same as before: as soon as tt and are become ready. but for interest i want to know/understand what is happening here.


Thomas Broyer

unread,
Oct 27, 2011, 10:42:47 AM10/27/11
to google-we...@googlegroups.com
As I said: handleCurrentHistory() is called from within onAdminUnitSelected, i.e. an "event dispatch". Delay it (either just handleCurrentHistory() or the whole continueStart()) with Scheduler#scheduleFinally to make it execute *outside* the event dispatching, so that the place change handlers are *effectively* registered (on not kept on hold by the SimpleEventBus until the end of the current dispatch).

tanteanni

unread,
Oct 28, 2011, 5:19:43 AM10/28/11
to google-we...@googlegroups.com
so at the moment i delay it (the whole continueStart()) untill some events occur. And you propose not to delay it with scheduleDeferred (until 2 events occurred) but with scheduleFinally? Or how the code shoul look like?
at the moment it is:
...
new SchedulerImpl().scheduleDeferred(new ScheduledCommand() {
          
          @Override
          public void execute() {
          while (!(ttReady && auReady)) {
            //bad i know          
          }
          continueStart();
          
          }
...
    private void continueStart() {
        // set up activity managers
        sidePanelAreaManager.setDisplay(contentArea.getWestSideArea());
        breadCrumbAreaManager.setDisplay(contentArea.getBreadcrumArea());
        contentMenuAreaManager.setDisplay(contentArea.getContentMenuArea());
        contentAreaManager.setDisplay(contentArea.getContentArea());
        // insert dynamic layout in static layout
        staticView.getContentArea().setWidget(contentArea);
        contentArea.setDefaultLayout();

        currentPlace = placeController.getWhere();
        eventBus.addHandler(PlaceChangeEvent.TYPE, this);
        historyHandler.register(placeController, eventBus, startPlace);
        historyHandler.handleCurrentHistory();
        // currentPlace = placeController.getWhere();
        // eventBus.addHandler(PlaceChangeEvent.TYPE, this);
    }

so please tell me what lines to move where or what lines to change 

thx in advance

Thomas Broyer

unread,
Oct 28, 2011, 7:14:27 AM10/28/11
to google-we...@googlegroups.com
I didn't see an Scheduler being used in the code you attached.

Whichever scheduledXxx method, the idea is to get out of the "event dispatching", so the above is fine (except that your "while loop" in the ScheduledCommand can cause an infinite loop: JS is single-threaded; Also, I'd have put the test outside the ScheduledCommand: if everything's loaded and I can continueStart, then launch continueStart, but slightly deferred, to make sure it's not executed while an event is being dispatched).

tanteanni

unread,
Nov 2, 2011, 5:38:06 AM11/2/11
to google-we...@googlegroups.com

ok, thx thomas it begins to become clearer,

my code attached don't use scheduler, thats right. meanwhile (thx to you thomas) i played with Scheduler and Timer. The current code looks like:
"
public void start() {

        Timer timer = new Timer() {

            @Override
            public void run() {
                if (ttReady && auReady) {
                    continueStart();
                    this.cancel();
                }
                      else {
                          System.out.println("waiting: " + ttReady + " " + auReady);
                      }

                  }
        };

        timer.scheduleRepeating(1000);
       
    }
"
but the feeling about this code is not better than the feeling with my infinite loop. i think the problem is, that i don't really understand the backgrounds - especially the scheduler. could you please show me some sample code for my special problem (how should start() look like?) or give some reference to docs explaining the scheduler/the model behind it?

tanteanni

unread,
Nov 2, 2011, 6:33:48 AM11/2/11
to google-we...@googlegroups.com
i found a bit of info about scheduler here. now i got:

"    private boolean tryToContinue() {
        if (ttReady && auReady) {
            continueStart();
            return false;
        } else
            return true;
    }

    public void start() {
        Scheduler.get().scheduleEntry(new RepeatingCommand() {

            @Override
            public boolean execute() {
                return tryToContinue();
            }
        });
"
i used "scheduleEntry" because of Javadoc: "This type of command is appropriate for instrumentation or code that needs to know when "something happens." "
But i am unsure about my implementation/ my understanding about scheduler - is the code above correct?

Jens

unread,
Nov 2, 2011, 6:43:07 AM11/2/11
to google-we...@googlegroups.com
Using SimpleEventBus the following does not behave like you expect:

eventBus.addHandler(FirstEvent.TYPE, new FirstHandler() {
  void onFirstEvent(FirstEvent e) {
    //will be enqueued to be added. So handler is not fully registered
    eventBus.addHandler(SecondEvent.TYPE, new SecondHandler() {
       void onSecondEvent(SecondEvent e) {
          sysout("second event")
       }
    }
    //wont trigger handler from above
    eventBus.fire(new SecondEvent());
    sysout("first event done");
  }
});

The output would be "first event done" and you wont see "second event" on the console. As Thomas said handlers that you add to the event bus while an event is actually dispatched (in this case FirstEvent) will cause the SimpleEventBus to put the new handler into a queue. After all events are dispatched the contents of this queue is added to the list of known handlers ready to be executed. But as all events are already dispatched these new handlers wont get executed.

And thats exactly what happens with your original code. Once your DataReceiver has finished loading your data you fire an event "OnSometimeEvent". The handler of this event calls continueStart() which in turn adds a handler to the event bus via historyHandler.register(...) and then fires a second event via historyHandler.handleCurrentHistory(). Because you register the history handler while your OnSometimeEvent gets dispatched this handler will be stored in a queue in SimpleEventBus. Then the second event will be dispatched (fired by historyHandler.handleCurrentHistory()) and after this event has been executed the handler in the queue will be added to the list of known handlers.

To solve this you either have to refactor your code in a way that all handlers are registered to the event bus before OnSometimeEvent is fired or you have to break your code execution flow by taking the continueStart() method and defer it until OnSometimeEvent has been dispatched.

So:

public void start() {
  if(dataReady) {
    Scheduler.get().scheduleFinally(
          //....
          continueStart();
     );
  } else { //wait }
}

should do the trick. If scheduleFinally does not work try scheduleDeferred which executes the code even later. If you want to understand the Scheduler class you should read something about the browser event loop.

-- J.

tanteanni

unread,
Nov 2, 2011, 7:12:06 AM11/2/11
to google-we...@googlegroups.com
THX for this explanation Jens!

the problem i have with the code you suggested is: how to implement "//wait"

as stated before my first implementation with an (infinite) while loop could block the whole browser. So whats about the other kinds of scheduleXXX? Whats about the code i showed above - using  "scheduleEntry"? According do Javadoc "scheduleEntry" is exactly what i need, but i am unsure how to use it.


Jens

unread,
Nov 2, 2011, 7:30:45 AM11/2/11
to google-we...@googlegroups.com
the problem i have with the code you suggested is: how to implement "//wait"

Put the label into your RootPanel like in the original code. I was a bit lazy and didn't want to retype it.
 
as stated before my first implementation with an (infinite) while loop could block the whole browser.

As javascript is executed on a single thread you never want an infinite while loop. This will always block the browser. You just want to react to events.
 

Thomas Broyer

unread,
Nov 2, 2011, 8:07:11 AM11/2/11
to google-we...@googlegroups.com
Grrr.

onModuleLoad() {
   // launch task 1
   // launch task 2
   // display wait message, or whatever
}

onTask1Finished() {
    task1Finished = true;
    maybeContinue();
}

onTask2Finished() {
    task2Finished = true;
    maybeContinue();
}

maybeContinue() {
    if (task1Finished && task2Finished) {
       // we're running from an event dispatch so handlers we add to the EventBus are deferred, and firing an event synchronously wouldn't trigger them, so we defer the whole thing
       Scheduler.get().scheduleFinally(new ScheduledCommand() {
          @Override
          public void execute() {
             // create your activity handlers, then:
             historyHandler.handleCurrentHistory();
          }
       });
    }
}

The first 3 methods you already have, the last one too, just missing that damn Scheduler bit; i.e. 5 damn lines.

tanteanni

unread,
Nov 2, 2011, 8:46:53 AM11/2/11
to google-we...@googlegroups.com
Thank YOU Thomas,

i guess now i got it - i don't have to "schedule" the data-loading but all the other stuff that need to be properly "initialized", right? Thanks for your patience (i is probably not the first time i say thx for your patience :-)). 

i just wanted jens to ask what happens if dataReady is not ready - i guess nothing? but for interest: my code above (using scheduleEntry) would work, wouldn't it?


i'll will refactor my code ....

Thomas Broyer

unread,
Nov 2, 2011, 9:14:34 AM11/2/11
to google-we...@googlegroups.com


On Wednesday, November 2, 2011 1:46:53 PM UTC+1, tanteanni wrote:
Thank YOU Thomas,

i guess now i got it

Thank God!
 
- i don't have to "schedule" the data-loading but all the other stuff that need to be properly "initialized", right?

Yes. Where did you read about deferring data-loading?
1st message: "defer continueStart() a bit more so it executes outside the event handling logic, within a Scheduler.scheduleFinally() for instance"
2nd message: "You need to defer the handleCurrentHistory() call outside the event dispatching; but you can actually defer the whole continueStart if you prefer"
3rd message: "Delay it (either just handleCurrentHistory() or the whole continueStart()) with Scheduler#scheduleFinally"
4th message: "then launch continueStart, but slightly deferred"

tanteanni

unread,
Nov 2, 2011, 9:30:24 AM11/2/11
to google-we...@googlegroups.com
At the moment i read your last post/ understood the problem i knew that you tried it several times - i saw all your vehement efforts to get it into my brain (but some times brains don't want to see :-) ).

i just refactored my code - in fact i haven't changed much (i already had some scheduler stuff in my code). Because all my tasks are done on construction time (gin injected stuff, ApplicationManager registers himself to onTaskDoneEvents in constructor)  i got rid of the public start() method. Now i only have a private start called in the way you suggested it:
...
    @Override
    public void onTopicTreeLoadedEvent(TopicTreeLoadedEvent event) {
        ttReady = true;
        start();
    }
..
    @Override
    public void onAdminUnitsLoaded(AdminUnitsLoadedEvent event) {
        auReady = true;
        start();
    }
...
private void start() {
        if (ttReady && auReady) {
            Scheduler.get().scheduleFinally(new ScheduledCommand() {

                @Override
                public void execute() {
                    // set up activity managers
                    sidePanelAreaManager.setDisplay(contentArea.getWestSideArea());
                    breadCrumbAreaManager.setDisplay(contentArea.getBreadcrumArea());
                    contentMenuAreaManager.setDisplay(contentArea.getContentMenuArea());
                    contentAreaManager.setDisplay(contentArea.getContentArea());
                    // insert dynamic layout in static layout
                    staticView.getContentArea().setWidget(contentArea);
                    contentArea.setDefaultLayout();
                    historyHandler.register(placeController, eventBus, startPlace);
                    historyHandler.handleCurrentHistory();
                    currentPlace = placeController.getWhere();
                }
            });

        }
    }

Reply all
Reply to author
Forward
0 new messages