I am struggling with an issue using RequestFactory in GWT. I have a User object : this object has login and password fields and other fields which are of collection type.
public class User {
private String login;
private String password;
private Set<Ressource> ressources;
// Getters and setters removed for brievety
}
I need to persist this object in db so I used RequestFactory because it seems like a CRUD-type operation to me.
Now for the RequestFactory part of the code, this is how I have tried to do it :
I create a UserRequestContext object to create a request object for the new User. Which gives something like :
public interface MyRequestFactory extends RequestFactory {
UserRequestContext userRequestContext();
RessourceRequestContext ressourceRequestContext();
}
and to create the user object I have something like this :
public class UserAddScreen extends Composite {
private UserProxy userProxy;
EventBus eventBus = new SimpleEventBus();
MyRequestFactory requestFactory = GWT.create(MyRequestFactory.class);
...
public UserAddScreen() {
...
requestFactory.initialize(eventBus);
}
public showUserAddScreen() {
// Textbox for password and login
// Listbox for ressources
}
}
I have tried to implement it as a wizard. So at the beginning of the UserAddScreen, I have a a userProxy object. This object fields are initialized at each step of the wizard :
for this last step, I have two list boxes the first one containing the list of all the ressources i have in my DB table Ressources that I got from RessourceRequestContext.getAllRessource (I have a loop to display them as listbox item with the RessourceId as the value) and the second allows me to add the selected Ressources from this first listbox. Here is the first listbox :
final ListBox userRessourcesListBox = new ListBox(true);
Receiver<List<RessourceProxy>> receiver = new Receiver<List<RessourceProxy>>() {
@Override
public void onSuccess(List<RessourceProxy> response) {
for(RessourceProxy ressourceProxy : response) {
ressourcesListBox.addItem(ressourceProxy.getNom() + " " + ressourceProxy.getPrenom(), String.valueOf(ressourceProxy.getId()));
}
}
};
RessourceRequestContext request = requestFactory.ressourceRequestContext();
request.getAllRessource().fire(receiver);
So, as you can see, my code loops over the retrieved proxies from DB and initializes the items within the listbox.
Here are the control buttons :
final Button addButton = new Button(">");
addButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
for (int i = 0; i < ressourcesListBox.getItemCount(); i++) {
boolean foundInUserRessources = false;
if (ressourcesListBox.isItemSelected(i)) {
for (int j = 0; j < userRessourcesListBox
.getItemCount(); j++) {
if (ressourcesListBox.getValue(i).equals(
userRessourcesListBox.getValue(j)))
foundInUserRessources = true;
}
if (foundInUserRessources == false)
userRessourcesListBox.addItem(ressourcesListBox
.getItemText(i), ressourcesListBox
.getValue(i));
}
}
}
});
So when somebody selects one or more users and click on a ">" button, all the selected items go to the second listbox which is named userRessourceListBox
userRessourcesListBox.setWidth("350px");
userRessourcesListBox.setHeight("180px");
After that, I have a FINISH button, which loops over the items in the second listbox (which are the ones I have selected from the first one) and I try to make a request (again) with RequestFactory to retrieve the ressourceProxy object and initialize the userProxy ressources collection with the result
final Button nextButton = new Button("Finish");
nextButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
RessourceRequestContext request = requestFactory.ressourceRequestContext();
for(int i = 0; i < userRessourcesListBox.getItemCount(); i++) {
Receiver<RessourceProxy> receiver = new Receiver<RessourceProxy>() {
@Override
public void onSuccess(RessourceProxy response) {
userProxy.getRessource().add(response);
}
};
request.find(Long.parseLong(userRessourcesListBox.getValue(i))).fire(receiver);
}
creationRequest.save(newUserProxy).fire(new Receiver<Void>() {
@Override
public void onSuccess(Void response) {
Window.alert("Saved");
}
});
}
});
Finally, (in the code above) I try to save the UserProxy object (with the initial request context I have created userProxy with)... but it doesn't work
creationRequest.save(newUserProxy).fire(...)
It seems like when looping over the result in the onSuccess method :
userProxy.getRessource().add(response);
I retrieve the response (of type RessourceProxy) but beyond this method, for example when I try to save the userProxy object AFTER THE LOOP, there are no RessourceProxy objects in the ressourceProxy collection of userProxy...
Have you guys ever experienced something like this ? Perhaps I am not doing it right : do I have to get the ressource with the UserRequestContext ? so that my newUser object and ressources are managed by the same request Context ? if yes then I think it's a little bit weird to have something mixed together : I mean what is the benefit of having a Ressource-related operation in the User-related request context.
any help would be really really ... and I mean really appreciated ;-) Thanks a lot
The message "… has been frozen" means that the object has been either edit()
ed or passed as an argument to a service method, in another RequestContext
instance (it doesn't matter whether it's of the same sub-type –i.e. UserRequestContext
vs. RessourceRequestContext
– or not) which hasn't yet been fire()
d and/or the response has not yet come back from the server (or it came back with violations: when the receiver's onViolation
is called, the objects are still editable, contrary to onSuccess
and onFailure
).
UPDATE: you have a race condition: you loop over the resource IDs and spawn as many requests as the number of items selected by the user, and then, without waiting for their response (remember: it's all asynchronous), you save the user proxy. As soon as you fire()
that last request, the user proxy is no longer mutable (i.e. frozen).
IMO, you'd better keep the RessourceProxy
s retrieved initially and use them directly in the user proxy before saving it (i.e. no more find()
request in the "finish" phase). Put them in a map by ID and get them from the map instead of find
ing them back from the server again.