2

What is the best way to perform an initial RPC call at startup with GWT?

I would like to retrieve configuration data that are dynamically generated on the server side, when a user loads a page. but If I do an asynchronous RPC call from the browser to retrieve the data, I sometimes don't get a response before the page is fully loaded, resulting in the page not having access to that configuration data. (no Thread.sleep() function for example)

thanks


thanks to @Steve-J's response, I found a solution...

Basically the idea is the following:

  • create a new StartupCompleted event
  • start the initial RPC call in the onModuleLoad()
  • in the onSucess method of that RPC call, fire a StartupCompleted event
  • at the end of the go() method of the App Controller, instead of inserting the default action in the History [ with a History.newItem("default"); ], or to fire the current History state if called through a bookmark [ with a History.fireCurrentHistoryState(); ], don't do anything
  • register a handler for the new StartupCompleted event on the eventbus
  • when the StartupCompleted event is fired, catch it and at that time, insert the default action in the history or fire the current history state

et voila...

The initial RPC call is completed before doing anything else...

Is there anything wrong with that approach?

Note that as suggested by @Tom-Miette it is nice have a simple "loading..." message until the StartupCompleted event is received.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
aregnier
  • 1,574
  • 2
  • 14
  • 19

4 Answers4

3

Not sure if its the best way but a good way is to avoid the initial call altogether and use a jsp page instead of a html page as your entry point into the application. You can then set variables for your initial state in javascript in the page server side e.g.

<script type="text/javascript">
    var InitialState = {
    "userName" : "<%= userName %>",
    "emailAddress" : "<%= emailAddress %>",
    etc...
};

In your GWT app you can read these values using the GWT Dictionary class

Dictionary state = Dictionary.getDictionary("InitialState");
String userName = state.get("userName");
String emailAddress = state.get("emailAddress");
pillingworth
  • 3,238
  • 2
  • 24
  • 49
  • Not sure if it's the best way either, but it sounds like one way to go around the problem. Only concern would be with the amount of data to preload this way, and the fact that you would have to preload everything at once instead of just what is needed. thanks. will try it. – aregnier Oct 28 '11 at 08:19
3

Are you using messaging for control flow? I do the RPC call, and on success, I send the message that triggers the first active popup for the user to interact with. On failure, a different message is fired that triggers a popup to tell the user that he can't proceed.

Don't forget to enable the glass panel on your popups so that the user can't mess with anything in the background while he is waiting.

Update: From the comments: "But my problem is that the user can bookmark a page, and go directly to it without any interaction. I may end up in a case where I have not received the result of the RPC call, but where I am generating the page he has requested. Even if in that generation I try to check if I've received the result, I can't pause until that's the case before completing the generation of the page."

Ah, I see. OK, GWT has a solution for this. You should look at Activities and Places. When the user bookmarks a "page", he is really bookmarking the state of the GWT application. When he returns to that "page", the Activity is fired and automatically calls the start() method. This is your opportunity to retrieve data from a server and do any other setup that is required. Once you have your data, your start() method proceeds to set up what is called the "view". From the user's perspective, the "page" has "loaded".

Steve J
  • 2,676
  • 20
  • 18
  • This. Have your first module entry point be the RPC dispatcher, with the callback creating your UI. – Tassos Bassoukos Oct 27 '11 at 20:16
  • yes I do. But my problem is that the user can bookmark a page, and go directly to it without any interaction. I may end up in a case where I have not received the result of the RPC call, but where I am generating the page he has requested. Even if in that generation I try to check if I've received the result, I can't pause until that's the case before completing the generation of the page. – aregnier Oct 28 '11 at 08:21
  • The entry point is performed first whatever the url when your GWT module starts. Can you wait for the callback before setting up the UI and store these data locally to be accessed in all "pages". – Tom Miette Oct 28 '11 at 13:41
  • How does he bookmark a page? Are you using Activities and Places? When you bookmark a Place and then return to it, the Activity Mapper always executes the startup method first, which is where you would call your server for data, and then you would render the page based on those results. I've extended my answer a bit based on what you've said here. – Steve J Oct 28 '11 at 13:47
  • @Steve-J Actually I'm using pre-"Activities and Places" messaging and bookmarking. (using History.newItem() and History.fireCurrentHistoryState() as was documented on the GWT web site) Unfortunately I can't find a way to pause the flow before the History.newItem() corresponding to the bookmarked url is executed, in case my data hasn't been loaded. – aregnier Oct 31 '11 at 13:14
  • @TomMiette that's what I'm trying to do... but can't find a way to " wait" for it. – aregnier Oct 31 '11 at 13:15
0

FWIW I had a solution that involves suspending EntryPoint.onModuleLoad() until the RPC returns.

I was using dependency injection and GWTP Dispatch which kind of complicated the bootstrap process. I also wanted the result to be a singleton instance in my system (client side).

The result was a GWT native (no command pattern) RPC call that launched dependency injection upon success.

EntryPoint class snippet:

@Override
public void onModuleLoad() {
    SessionInfoProvider.init(new Runnable() {
        @Override
        public void run() {
            // can now complete setup
            AppInjector injector = GWT.create(AppInjector.class);
            injector.bootstrap();
        }
    });
}

SessionInfoProvider snippet:

public class SessionInfoProvider implements Provider<SessionInfo> {
private static SessionInfo.Bean sessionInfo;

/**
 * Needs to be called before the module is used.
 */
public static void init(final Runnable onCompletion) {
    GreetingServiceAsync s = GWT.create(GreetingService.class);
    s.loadSessionInfo(new AsyncCallback<SessionInfo.Bean>() {
        @Override
        public void onFailure(Throwable caught) {
            GWT.log("Failed to retrieve Session Info");
        }

        @Override
        public void onSuccess(SessionInfo.Bean result) {
            GWT.log("Successfully retrieved session info bean.  Username:" + result.getUserName());
            sessionInfo = result;
            onCompletion.run();
        }
    });
}

@Override
public SessionInfo get() {
    if (sessionInfo == null) throw illegalState("Session Info not ready.");
    return sessionInfo;
}
}
Peter L
  • 2,921
  • 1
  • 29
  • 31
0

Cover your html page with a kind of "loading screen":

<div 
id="loading"
style="
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  background: black;
  color: white;"
>
  Loading...
</div>

and just remove it when the RPC has succeeded/failed:

RootPanel.get("loading").getElement().removeFromParent();
Tom Miette
  • 129
  • 3
  • this is an interesting approach... however it only works to prevent the user from doing anything until you have received the results of the initial RPC call. However in my case, the user can bookmark a url and the specific page will "prepared" without the user having to do anything. So I may still get to a point where the initial RPC hasn't returned, but I need the data to finish preparing the page. – aregnier Oct 28 '11 at 08:13