PDA

View Full Version : GxtScheduler RpcExample not working



binso
14 Sep 2011, 7:47 PM
Hi,<br><br>I am trying to get the RPCExample in GxtScheduler to work but with no luck. I have sorted out the RPC plumbing issues and I am sure (through logging) that the service implementation is being called. However no data is shown in the grid and the message "No bookings for this interval" is returned.<br><br>Please help!!:((<br><br>Regards,<br><br>Brian

Colin Alworth
15 Sep 2011, 8:31 AM
My first step would be to debug both the RPC server side method and the client side onSuccess callback. Make sure the rpc method returns something other than an empty list. Then make sure on your onSuccess method breakpoint, the response object is an actual list, with content.

If the servlet rpc method isn't returning a populated list, there is your problem, the server needs to return useful data. If it is, onSuccess should also see that same list - it would be a very odd problem indeed if it didn't. If the data is making it to the client, I would suspect improper use of the loader or proxy - check out the GWT dev mode log for possible hints there.

Failing that, if the error is on client side, consider making the client code very simple like the explorer samples, to see if a mistake becomes obvious. If it does not, this is a simple example that you can paste to this thread, and other developers can look around to see what might be the problem.

binso
16 Sep 2011, 10:58 PM
Thanks a lot Colin! After having done all the debug stuff it seems the problem is I am unable to return the loaded rpcproxy before my eventStore is created, see code below. How can I ensure that the async code for the rpcproxy runs and returns before the ListLoader runs and returns an empty store?


private RpcProxy<ArrayList<ModelData>> getProxy() {
RpcProxy<ArrayList<ModelData>> eventProxy = new RpcProxy<ArrayList<ModelData>>() {
int pending = 2; // watch for this
@Override
protected void load(Object loadConfig,
AsyncCallback<ArrayList<ModelData>> callback) {

greetingService.getEvents((ListLoadConfig)loadConfig, new AsyncCallback<ArrayList<EventModel>>(){
public void onFailure(Throwable caught)
{
Window.alert("Failed to get response from server" + caught.getMessage());
}


@Override
public void onSuccess(ArrayList<EventModel> result) {
pending--;
EventModel md = (EventModel) result.get(0);
Window.alert("The duration is: " + md.getDuration()); //This returns successfully
}
});
return eventProxy;
}

private ListStore<ModelData> createEventStore(RpcProxy<ArrayList<ModelData>> proxy) {
//RpcProxy<ArrayList<ModelData>> eventProxy = getProxy();

ModelReader eventReader = new ModelReader();
BaseListLoader<BaseListLoadResult<ModelData>> eventLoader = new BaseListLoader<BaseListLoadResult<ModelData>>(
proxy, eventReader);

// Store holding all the events/tasks
ListStore<ModelData> eventStore = new ListStore<ModelData>(
eventLoader);
List<ModelData> mds = eventStore.getModels();
Window.alert("Outside success. EventStore size: " + eventStore.getCount());
return eventStore;

}


Regards,

Binso

Colin Alworth
17 Sep 2011, 7:24 PM
Based on your code sample, I'm not sure that you understand how this being async matters to your code - the server will always take longer to return a value than the next line of code - this is to prevent slow servers or network connections from causing the browser to hang.

Your method createEventStore is returning a store that is in the process of being loaded. Instead of doing the window.alert right after creating the store, add a listener to the Store.Load event on the store instance, and call loader.load() once (I'm guessing this is already happening elsewhere, otherwise your onSuccess method would never be called - probably happening because of whatever is using the store, a grid/combobox/listview).

The async nature of GWT (and most webapps) is important to keep your code responsive and flexible enough to add other optimizations later, such as split points, or using scheduling to break up long running code. It is worth it to get to know the idea as soon as you can, if you're not already familiar. Here is an old post from the GWT group that may help http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/faca1575f306ba0f/3be719c021aa19bd.

binso
17 Sep 2011, 9:48 PM
Hi Colin,

Thanks once again for response I am learning a lot in getting this issue sorted. I have changed my code to the code attached. I have attached the entire class file except for the imports. I now completely understand the async nature of GWT (thanks for the link!), but am failing to translate it into code. I could not find the Store.Load event and have tried using the Store.Add event with no luck as the onSuccess method is not being called.



public class RpcExample2 implements EntryPoint {
private Scheduler<ModelData, ModelData> grid;
private Date startDate;
private Date endDate;


/**
* Create a remote service proxy to talk to the server-side Greeting
* service.
*/
private final ServiceAsync greetingService = GWT.create(Service.class);


//RpcProxy<ArrayList<ModelData>> eventProxy = getProxy();


/**
* This is the entry point method.
*/
public void onModuleLoad() {


RootPanel.get().add(
new Button("create calendar",
new SelectionListener<ButtonEvent>() {


@Override
public void componentSelected(ButtonEvent ce) {
createPanel();
}
}));


}


private void createPanel() {
startDate = new Date();
DateUtil.clearTime(startDate);
startDate = new Date(startDate.getTime() - 1000 * 60 * 60 * 24);
endDate = new Date(startDate.getTime() + 1000 * 60 * 60 * 24 * 7);


/* Creating the grid */
//grid = this.createGrid();
SchedulerGroupingView viewCfg = new SchedulerGroupingView();
viewCfg.setShowGroupedColumn(false);
viewCfg.setForceFit(true);
viewCfg.setEmptyText("No bookings found in this interval");
viewCfg.setRowSelectorDepth(11);
viewCfg.setCellSelectorDepth(6);



/*Creating the resource store*/
//GroupingStore<ModelData> resourceStore = createResourceStore();
RpcProxy<ArrayList<ModelData>> resourceProxy = new RpcProxy<ArrayList<ModelData>>() {
@Override
protected void load(final Object loadConfig,
AsyncCallback<ArrayList<ModelData>> callback) {
greetingService.getResources((BaseListLoadConfig) loadConfig,
new AsyncCallback<ArrayList<ResourceModel>>() {
public void onFailure(Throwable caught) {
Window
.alert("Failed to get response from server"
+ caught.getMessage());
}


@Override
public void onSuccess(
ArrayList<ResourceModel> result) {
// TODO Auto-generated method stub
ResourceModel md = (ResourceModel) result
.get(0);
//grid.getStore().getLoader().load(loadConfig);
Window.alert("The resource name is: "
+ md.getName());
}
});
}
};


ModelReader resourceReader = new ModelReader();
final BaseListLoader<BaseListLoadResult<ResourceModel>> resourceLoader = new BaseListLoader<BaseListLoadResult<ResourceModel>>(resourceProxy, resourceReader);
GroupingStore<ModelData> store = new GroupingStore<ModelData>(resourceLoader);
store.addListener(Store.Add, new StoreListener<ModelData>(){
@Override
public void storeUpdate(StoreEvent<ModelData> se) {
resourceLoader.load();
}
});
store.groupBy("category");

/*Create the eventStore*/
//ListStore<ModelData> eventStore = createEventStore(eventProxy);

RpcProxy<ArrayList<ModelData>> eventProxy = new RpcProxy<ArrayList<ModelData>>() {
int pending = 2; // watch for this


@Override
protected void load(Object loadConfig,
AsyncCallback<ArrayList<ModelData>> callback) {


greetingService.getEvents((ListLoadConfig) loadConfig,
new AsyncCallback<ArrayList<EventModel>>() {
public void onFailure(Throwable caught) {
Window
.alert("Failed to get response from server"
+ caught.getMessage());
}


@Override
public void onSuccess(ArrayList<EventModel> result) {
//Is there anything I can do here to load the events into the store??
}
});
}
};

ModelReader eventReader = new ModelReader();
final BaseListLoader<BaseListLoadResult<ModelData>> eventLoader = new BaseListLoader<BaseListLoadResult<ModelData>>(eventProxy, eventReader);


// Store holding all the events/tasks
ListStore<ModelData> eventStore = new ListStore<ModelData>(eventLoader);
eventStore.addListener(Store.Add, new StoreListener<ModelData>(){
@Override
public void storeUpdate(StoreEvent<ModelData> se) {
eventLoader.load();
}
});
List<ModelData> mds = eventStore.getModels();
Window.alert("Da eventStore size is: "
+ mds.size());

ColumnModel cm = createStaticColumns();
int nbrStaticCols = cm.getColumnCount();


Scheduler<ModelData, ModelData> grid = new Scheduler<ModelData, ModelData>(
store, cm, eventStore, viewCfg, nbrStaticCols);
grid.addListener(Events.Attach, new Listener<GridEvent<ModelData>>() {
public void handleEvent(GridEvent<ModelData> be) {
//eventLoader.load();
//resourceLoader.load();
}
});

grid.setBorders(true);
grid.setStripeRows(true);
grid.setTrackMouseOver(false);
grid.setWidth(900);
grid.setAutoHeight(true);


grid.addPlugin(new TimeTip()); // Enable time tooltip
grid.addPlugin(new DragCreator()); // Enable DragCreator
grid.addPlugin(new ClickCreator()); // Enable DoubleClick Creator
grid.addPlugin(new DragDrop()); // Enable DnD
grid.addPlugin(new Resize()); // Enable Resize


final ViewBehaviour vb = new DayView(grid);
vb.renderer = myRenderer;


ContentPanel cp = new ContentPanel();
cp.setStyleAttribute("padding", "20px");
cp.setHeaderVisible(false);
cp.setWidth(940);
cp.add(grid);



grid.setView(startDate, endDate, vb);


RootPanel.getBodyElement().setAttribute("padding", "20px");
RootPanel.get().add(cp);


}


private ColumnModel createStaticColumns() {
List<ColumnConfig> configs = new ArrayList<ColumnConfig>();


ColumnConfigExt column = new ColumnConfigExt();
column.setId("0");
column.setHeader("Staff");
column.setWidth(120);
column.setSortable(false);
column.setFixed(true);
column.setDataIndex("name");
column.setRenderer(new GridCellRenderer<ModelData>() {
@Override
public Object render(ModelData model, String property,
ColumnData config, int rowIndex, int colIndex,
ListStore<ModelData> store, Grid<ModelData> grid) {
return "<div class=" + model.get("category") + "p>"
+ model.get("name") + "</div>";
}
});
configs.add(column);


column = new ColumnConfigExt();
column.setId("2");
column.setHeader("");
column.setWidth(0);
column.setHidden(true);
column.setDataIndex("category");
configs.add(column);


ColumnModel cm = new ColumnModel(configs);
return cm;
}


private GridCellRenderer<ModelData> myRenderer = new GridCellRenderer<ModelData>() {
@SuppressWarnings("unchecked")
@Override
public Object render(ModelData model, String property,
ColumnData config, int rowIndex, int colIndex,
ListStore<ModelData> store, Grid<ModelData> grid) {
Scheduler<ResourceModel, EventModel> g = (Scheduler) grid;
Date start = (Date) model.get("StartDateTime");
Date end = (Date) model.get("EndDateTime");
HashMap returnValue = new HashMap();


String text = g.viewBehaviour.getFormattedDate(start) + " - "
+ g.viewBehaviour.getFormattedDate(end);
returnValue.put("text", text);
returnValue.put("cls", "");


return returnValue;
}
};

}


What do I need to do to ensure that the store is loaded?:-?

Colin Alworth
18 Sep 2011, 9:08 AM
You've got quite a lot of code there, including more than one proxy - consider simplifying for the sake of this discussion.

The basic idea of stores, loaders, and proxies is that you want to keep how you load separate from the actual data. In many of the examples at http://www.sencha.com/examples/explorer.html data is already there, and store.add() is used to add a bunch of items at once. This could be done by triggering your own rpc call, and skipping the loader and such entirely. If you need to do more than just this, then the loader and such can help.

The Loader is used to pass in some parameters to the server, to indicate which items should be loaded. One example is to ask the server to only send some data (offset, length) so you can page the items. Another would be to ask the server to pre-filter or sort the data. In these cases, the loader sets up the request, and uses a proxy to actually make the request.

Notice that the RpcProxy method comes with a AsyncCallback - if you dont pass that callback directly to the rpc method call, then call the callback.onSuccess method when your own onSuccess is called. The latter is often done to change the data from one format to another.

Something needs to tell the loader to load for the first time, with some initial parameters. Your current Store.Add event doesnt make sense for this, as data won't be added until the load happens...

I don't know what I was talking about when I referred to Store.Load - there is no such thing. In the http://www.sencha.com/examples/explorer.html#remotefiltergrid example, Events.Attach is used to start the load when the grid is first attached. Alternatively you could load right away, calling loader.load() right after creating the store.

Not all stores need loaders, and from the code you have, I'm not sure you need one. I'm also having a hard time reading the two intermixed examples - I'd suggest you start small with one grid and one store, and build up after that is working.

binso
18 Sep 2011, 9:24 AM
Thanks for the input Colin. Actually after having another read through this link (http://groups.google.com/group/Googl...e719c021aa19bd.), I decided to do away with the loaders and add resulting arraylists directly to the stores and then also call the Edit.Attach event on the grid and viola! issue solved. Thank you so much for all the guidance!

Regards,

Binso.