Exciting life of Entity Proxies in contexts of RequestFactory

Introduction

Since  GWT 2.1  we can use special Request Factory interface to implement  data access layer to server side objects.  There are several pages  which describe how to use it properly. For me the most resourceful are :

– official explanation from GWT site http://code.google.com/webtoolkit/doc/latest/DevGuideRequestFactory.html

– dynatablerf project, which shows request factory in action: http://code.google.com/p/google-web-toolkit/source/browse/trunk/samples/dynatablerf/?r=8464

– google groups where a lots of issues have been solved and questions have been answered : http://groups.google.com/group/google-web-toolkit

– blogs and tutorials, e.g. Thomas Broyer blog: http://tbroyer.posterous.com/(moved)  http://blog.ltgt.net/

I am using those pages really often, but still, sometimes  I am running into problems which are not described anywhere.. E.g. I was having some issues while using RequestContext’s and EntityProxies  in some typical user workflows. After learning about how it works and how it should be used correctly I have decided to share this knowledge, because I was not able to find anywhere before. I will not explain how the request factory works, if you need to make first steps on that, follow the link  http://code.google.com/webtoolkit/doc/latest/DevGuideRequestFactory.html.

I) I assume you already know

  • GWT basics + GWT Request Factory basics
  • For looking on code examples :
    • Basic knowledge about ORM ( best will be JDO, but if you know JPA you will understand it as well)
    • usage of GEP plugin to configure and manage your project in Eclipse

II) Technologies used

  • GWT 2.2.0
  • App Engine 1.4.3
  • Java Data Objects (JDO)

III) Tools Used

  • Eclipse Helios
  • Google Eclipse Plugin
  • Google App Engine

IV) How it should be done: typical READ UPDATE workflow

Lets assume we have some entities in database. What we want to do is
1) retrieve an entity
2) make some changes,
3) update the entity in the database
If you will be on server side and try to use some orm framework those operations  may look like that


Student s = dao.getById(studentId);
s.setName("ted");
dao.update(s);

While working with Request Factory  you need to have another approach and follow different process.  So we will go trough this path and analyze the steps.  All operations I will perform are summarized in the picture below in the column Operations. Taking deeper look on the picture you will also find out that I will use several Request Contexts and handle with different entites (s1, s2…).

workflow rf

1) Get entity from the server.

You need to initialize RequestContext instance, then call method which retrieves entity, and afterwords fire the request

 req0 = requestFactory.studentRequest();
 req0.findStudent(id).fire(new Receiver<StudentProxy>() {

     @Override
     public void onSuccess(StudentProxy response) {
         s1 = response;
     }
 });

2) Mark response for changes

The response you have from the server is a special object used by request factory to store properties of  your entity along with other informations needed by RF to work. This object is of type which extends AutoBean<T extends StudentProxy> and if you look at it in debug mode you can see its properties:

autobean in debug mode

some of those properties will be discussed in this article, those are

– frozen : flag which informs if entity(bean) has been locked for changes

– tags[requestContext] : informs which request context is responsible for managing this entity ( e.g. holding its changes)

– you can see also values table – it keeps all properties which you can set and get through the methods you have declared while implementing your Entity Proxy

So as you can see in our example the bean is : FROZEN and its requestContext is set to null. In this state you cannot perform any changes on this bean, all you can do is only call its getters. In order to UNLOCK the bean for changes you need to

a) create new requestContext

b) mark the bean for changes by calling edit() method.

attention  Pay attention: the edit(arg) method will RETURN unlocked proxy (instead of unlocking the “arg”) – don’t perform changes on the one  you have used as an argument.  It is a common mistake to do req1.edit(s1) and try to do operations with s1.. this may end up with some red messages comming from the console..

 StudentRequest req1 = requestFactory.studentRequest();  

 StudentProxy s2 = req1.edit(s1);

3) Apply changes

Now the s2 bean is ready for any changes. So you can set the properties without running into any exceptions

 s2.setName("Stefan");

4) Save changes

In order to save the changes into database you must call some update method. Here you must use the same request which you used for editing the entity. If you use another one you will recieve exception, because only req1 has informations about this entity and its changes.

        req1.update(s2).fire(new Receiver<StudentProxy>() {

            @Override
            public void onSuccess(StudentProxy response) {
                s3 = response;
            }
        });

5) After server response

Server has successfully updated entity and returned back new copy of entity. Since now s2 entity is useless and you should not use it anymore.

6) More changes?

Here we are in the same situation as in point 2)  so if you want to make more changes, you have to repeat the process: create new RequestContext, edit entity with it, send changes to the server using RequestContext.

7) Create Read Update workflow

CRU  workflow will be quite similar. After creating and saving the entity with usage of some request ( lets say req0) we will end up in point 1) in which we got newly created entity from the server.

V) Possible traps along the way

As you can see life of entities in your application cannot be called boring:) they are edited, changed, freezed and so on..:) Also while you are working with  them you may get into some troubles if you not follow the described workflow correctly. I will give some most probable trouble scenarios – along with exceptions which are thrown, so if you see one of those while running your app you will know were to look for the solution.

Lets go back to the picture and see the last column : “What could happen if” – there I have put some operations which are not allowed for current states of request contexts and proxies.

1) Trying to edit locked entity.

If an entity is frozen  ( locked for changes) you cannot:

– change its properties

– use it in requestContext method calls.

If you will try to do that, you will recieve exception :  java.lang.IllegalStateException: The AutoBean has been frozen.

When entity may be frozen? 

a) every entity returned as a response is frozen

b) every entity which has been used in requestContext call will get frozen.

In first situation solution is easy – you just have to unlock given entity. In order to do that you must use instance of your RequestContext class and call edit() method.

 StudentRequest req1 = requestFactory.studentRequest();  

 StudentProxy s2 = req1.edit(s1);

In second situation you should not use given entity any more, It cannot be edited because it has already a requestContext assigned. If you want to change it you must retrieve instance of this entity from server again and follow instructions for point a).

2) Trying to call requestContext.edit() on entity which has already requestContext assigned.

If you have retrieved entity from the server or created a new one, and afterwords you are trying to use ANOTHER RequestContext to edit it e.g. in this way:

       StudentRequest req = requestFactory.studentRequest();
       s1 = req.create(StudentProxy.class);
       // s1 is connected with "req" and one context is just enough for it
       StudentRequest reqZZZ = requestFactory.studentRequest();
       reqZZZ.edit(s1); // you cannot do it - here exception will be thrown

you will surely recieve an exception:

java.lang.IllegalArgumentException: Attempting to edit an EntityProxy  previously edited by another RequestContext 
You may run into this problem in situation where you have a bean, but you have no track of request context which has created or edited the bean in some previous method call. In this situation you must save the previous requestContext somewhere, or send it along with the entity to the point of intrest… The best solution may be to  create some special layer which holds currently used request.

3) Trying to reuse Request Context which has already been fired

You can use request context to create and edit many different entities ( also of different type). You can also accumulate the methods which should be fired. But what you cannot do is to try to use it twice to fire a request. If you have created reqest and call fire() method on it, you cannot repeat that..  If you do you will get: java.lang.IllegalStateException:A request is already in progress exception. 

Solution is simply create new requestContext.

VI)  More complicated traps

Those 3 situations I have mentioned are quite easy to solve. But the more complicated your data model is, and the more unusual widgets and user work-flows are you using the more problems may occur. At first time I was really unhappy with the fact that the RequestContext.edit() method is not working “in place”. E.g my problem was

I have an Student object which has a list of Subjects he is studying.

– GUI : I have widget which displays info about student and his subjects, you can edit info about several subjects – change its descriptions etc. After you are done you click save button which save ALL the changes.

save button triggers method dao.update(Student student) which makes cascade update on student and list of his subjects.

and now how to do it properly? Before I am going to edit any property of subject I need to edit it.. so

List<SubjectProxy> subjects = student.getSubjects();

showSubjectsInEditableList(subjects);

// method called after user will provide new name of subject

public void changeNameOfSubject(String name, Subject selectedSubject)

{

SubjectProxy subject = req.edit(selectedSubject);

subject.setName(name);

// but subject is not on the list student.getSubjects()..

}

public void save()

{

// here changes which have been made will not be propagated...

req.update(student);

}

What we may do in this situation is e.g. replace old value from the list with new value

public void changeNameOfSubject(String name, Subject selectedSubject)

{

SubjectProxy subject = req.edit(selectedSubject);

subject.setName(name);

student.getSubjects().remove(selectedSubject); // here nothing will happen!

student.getSubjects().add(subject);

}

I am not fan of this solution and am still trying to find something more elegant..  Anyway working with request factory sometimes can be a little but surprising. But still taking into account all the pros and cons it is great framework which if you learn it makes wiring server and client data objects really easy and save.

Advertisements

23 Responses to Exciting life of Entity Proxies in contexts of RequestFactory

  1. Sonu Mehta says:

    Very very useful article..
    Thanks a lot..

    Need more help from you..
    can you pls share you gmail id???
    my gmail id is: er.sonumehta@gmail.com

  2. jchaganti says:

    Hi,

    Thanks for nicely explaining the various states of the Proxy objects. I have few questions:

    1. Are these states applicable to ValueProxy also? In your example, I guess you are referring to EntityProxy (the StudentProxy object).

    2. In the method changeNameOfSubject, where you are editing selectedSubject, it’s type has been mentioned as “Subject”. I guess the type of selectedSubject should be “SubjectProxy”. am I right or am I missing something grossly?

    Regards,
    jchaganti

    • jchaganti says:

      I realized that your post is specifically related to ‘Entity Proxies’. So I want to know if ValueProxy objects also follow a similar states. If not, what care should one take while editing them.

      Thanks,
      jchaganti

  3. roman says:

    You did exactly the same as you written that is illegal:
    (What’s the point here?)

    req0 = requestFactory.studentRequest();
    req0.findStudent(id).fire(new Receiver() {

    @Override
    public void onSuccess(StudentProxy response) {
    s1 = response;
    }
    });

    StudentRequest req1 = requestFactory.studentRequest();

    StudentProxy s2 = req1.edit(s1);

    ——————-

    If you have retrieved entity from the server or created a new one, and afterwords you are trying to use ANOTHER RequestContext to edit it e.g. in this way:
    StudentRequest req = requestFactory.studentRequest();
    s1 = req.create(StudentProxy.class);
    // s1 is connected with “req” and one context is just enough for it
    StudentRequest reqZZZ = requestFactory.studentRequest();
    reqZZZ.edit(s1); // you cannot do it – here exception will be thrown
    you will surely recieve an exception:
    java.lang.IllegalArgumentException: Attempting to edit an EntityProxy previously edited by another RequestContext

  4. miguialberto says:

    Hi!

    Thank you for the tutorial! I have already started with GWT and it is really usefull.

    Nevertheless, I have found some points that maybe could be changed. In point 2 in “Possible traps along the way” it is said that it is not possible to use a an entity which has already assigned a request. I have tested and it is not always true. When the request from an entity is blocked (it is a property from request that can be saw in debug mode and it is false until request is fired) it can be edit by another request. So, next example should work:

    StudentRequest req = requestFactory.studentRequest();
    s1 = req.create(StudentProxy.class);// Request is not locked. It has not been already fired
    // s1 is connected with “req” and one context is just enough for it
    req.fire();
    StudentRequest reqZZZ = requestFactory.studentRequest();
    reqZZZ.edit(s1); // you can do it – request is locked

    At least, that is what I have tested with GWT 2.4.

    I think that maybe the tutorial could be more clear if the concept of “locked” in a request is also introduced due to affect the live cycle of entities.

    Ciao!

  5. narun009 says:

    Hi I have a class I am getting the error shown below

    caused by: java.lang.IllegalArgumentException: Attempting to edit an EntityProxy previously edited by another RequestContext”

    You have mentioned that we need to initialize how to do it.

  6. robsonexd says:

    Thanks for this good article!

  7. Pingback: Our Experience with GWT’s RequestFactoryJoshIsOnIT

  8. Carlos says:

    thanks for the great article. It was very helpfull.

  9. Hokuto Ide says:

    This post helped me a lot. Thank you.

  10. aleclofabbro says:

    HI!
    I don’t use to leave replies to blogs .. i’m just a “watcher”,
    but this post is one of the most concise and clear view about workflow & traps of this “damned” reaquestFactory.. so i couldn’t leave it “blank”..
    thanks,
    Alex
    (btw may i leave some quests, if i need)

  11. jborlik says:

    Hey, I just wanted to say “thank you”. This post was very helpful tracking down various RequestFactory issues, and actually in just understanding RequestFactory in general. Also: Instead of removing/adding the unfrozen SubjectProxy, you can call something like getSubjects().set(irow, selectedSubject). (This might be useful if you need to preserve the order of the list.)

  12. Mike says:

    Good article.
    For the problem you described in the last, i think maybe we could just send the subjects that need updating, not send the student, what’s your opinion?

  13. Arash says:

    Thank you for your RF caveats especially the picture has helped me a lot. I had a similar scenario as your student / subject case and I wanted to share my solution.
    You can have a Editor<List> that also implements HasRequestContext<List>. In this editor let’s say you use cellTable to render students list.
    You can use EditTextCell to render the column that you wanted to be updated or however else you see fit. Upon updating the field / proxy, as you said edit the given proxy with the requestContext that is handed to you by the parent editor (probably carries student.persist() context). Then let the editor take care of the rest. I have implemented a similar scenario, I can post my implementation if needed.

    Thanks again,
    Arash

  14. imrabti says:

    Great article, now I understand exactly how RequestFactory works.
    Quite complicated compared to other alternatives.

  15. HI Fascynacja,
    Your blog helped me a lot . I am very new to gwt and this requestfactory.
    I have a problem whose solution i am not able to find any where.
    The problem is i am using jpa along with Requestfactory and one entity class object is used as member of anther entity class (for foreign key relation). and from client side i want to persist one proxy object and all inner entities will be saved automatically to their respective tables.

    for ex
    City Entity
    —————
    @Entity
    @Table(name = “city”)
    public class City implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int cityid;

    private String city;

    private String state;

    @Version
    private int version;

    ————————————
    ——————————————-
    }

    User Entity
    —————————
    @Entity
    @Table(name=”ucuser”)
    public class Ucuser implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int userid;

    private String firstname;

    private String lastname;

    @Version
    private int version;

    @ManyToOne
    @JoinColumn(name=”citystateid”)
    private City city;

    ——————————-
    ——————————–
    }

    in my onmoduleload()
    ———————————

    final EventBus eventBus = new SimpleEventBus();
    final UCRequestFactory requestFactory = GWT
    .create(UCRequestFactory.class);
    requestFactory.initialize(eventBus);

    CityRequest cityRequest = requestFactory.cityRequest();
    CityProxy cityProxy = cityRequest.create(CityProxy.class);

    cityProxy.setCity(“Bhadrak”);
    cityProxy.setState(“Orissa”);

    UcuserRequest ucuserRequest = requestFactory.ucuserRequest();
    UcuserProxy ucuserProxy = ucuserRequest.create(UcuserProxy.class);

    ucuserProxy.setCity(cityProxy);
    ucuserProxy.setFirstname(“Niranjan”);
    ucuserProxy.setLastname(“Panigrahi”);

    ucuserRequest.persist(ucuserProxy).fire(new Receiver() {

    @Override
    public void onSuccess(Boolean response) {
    Window.alert(“” + response);
    }
    });

    i am trying do some thing like this .if i will persist the user , along with the user its city will also persist in its respective table. But it is not working .
    any help?

    Thanks in advance

  16. Hi Agata Pysz
    sorry i thought that was your name
    thanks
    Niranjan Panigrahi

  17. Pingback: gwt 2.0.x gilead hibernate tutorial | My Fascinations

  18. Corba says:

    Thank you, Thank you, Thank you, Thank you……..
    This was very helpful in figuring and fixing the problem I had. GBU!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: