Search code examples
gwtcollectionspersistencerequestfactory

How can I use RequestFactory to create an object and initialize a collection whithin it with objects retrieved from another ReqFactory?


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 :

  1. 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 :

  • the first step is adding the login and password
  • the second step is adding ressources to the userProxy object.

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


Solution

  • 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 RessourceProxys 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 finding them back from the server again.