2

I'm using (and new to) RequestFactory in GWT 2.5, with JDO entities with a one-to-many relationship, on AppEngine datastore. I've just started using the GWT RequestFactoryEditorDriver to display/edit my objects.

The Driver traverses my objects fine, and displays them correctly. However, when I try to edit a value on the "related" objects, the change doesn't get persisted to the datastore.

When I change b.name on my UI and click "save", I notice only A's persist() call is called. B's persist() is never called. How do I make the editorDriver fire on both ARequest as well as BRequest request contexts? (since what I want is for B's InstanceRequest<AProxy,Void> persist() to be called when my edits are to B objects only.)

Also, AFAICT, if I have an editor on BProxy, any object b that is being shown by the editor (and following the Editor Contract) should automatically be "context.edit(b)"ed by the Driver to make it mutable. However, in my case "context" is an ARequest, not a BRequest.

Do I have to make a ValueAwareEditor like mentioned here: GWT Editor framework and create a fresh BRequest inside the flush() call and fire it, so that changes to B separately persist in a BRequest before the ARequest is fired?

editorDriver.getPaths() gives me: "bs"

Also, the driver definitely sees the change to B's property, as editorDriver.isChanged() returns true before I fire() the context.

There are no errors on my client-side or server-side logs, and the Annotation Processor runs with no warnings.

Here's how I setup my driver:

editorDriver = GWT.create(Driver.class);
editorDriver.initialize(rf, view.getAEditor());

final ARequest aRequest = rf.ARequest();
        final Request<List<AProxy>> aRequest = aRequest.findAByUser(loginInfo.getUserId());
        String[] paths = editorDriver.getPaths();
        aRequest.with(paths).fire(new Receiver<List<AProxy>>() {

            @Override
            public void onSuccess(List<AProxy> response) {
                AProxy a = response.get(0);
                ARequest aRequest2 = rf.aRequest();
                editorDriver.edit(a, aRequest2);
                aRequest2.persist().using(a);
            }
        });

This is how my entities look:

public abstract class PersistentEntity {
    public Void persist() {
        PersistenceManager pm = getPersistenceManager();
        try {
            pm.makePersistent(this);
        } finally {
          pm.close();
        }
        return null;
    }


    public Void remove() {
        PersistenceManager pm = getPersistenceManager();
        try {
            pm.deletePersistent(this);
        } finally {
          pm.close();
        }
        return null;
    }
}

@PersistenceCapable(identityType = IdentityType.APPLICATION)
@Version(strategy=VersionStrategy.VERSION_NUMBER, column="VERSION",
         extensions={@Extension(vendorName="datanucleus", key="field-name", value="version")})

public class A extends PersistentEntity {

        ... (Id, version omitted for brevity)
    @Persistent
    private String name;

        @Persistent
        private List<B> bs;

    public String getName() {
        return name;
    }

        ...

    public void setName(String name) {
        this.name = name;
    }

    public List<B> getBs() {
        return bs;
    }

    public void setBs(List<B> bs) {
        this.bs = bs;
    }

}

... (same annotations as above omitted for brevity)

public class B extends PersistentEntity {

        ... (Id, version omitted for brevity)
    @Persistent
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Here are the proxies:

@ProxyFor(A.class)
public interface AProxy extends EntityProxy {
    String getName();
    List<BProxy> getBs();
    void setName(String name);
    void setBs(List<BProxy> bs);
}

@ProxyFor(B.class)
public interface BProxy extends EntityProxy {
    String getName();
    void setName(String name);
}

Here are my service stubs:

@Service(A.class)
public interface ARequest extends RequestContext {

    Request<List<A>> findAByUser(String userId);

    InstanceRequest<AProxy, Void> persist();
    InstanceRequest<AProxy, Void> remove();
}

@Service(B.class)
public interface BRequest extends RequestContext {

    Request<List<A>> findB(String key);

    InstanceRequest<BProxy, Void> persist();
    InstanceRequest<BProxy, Void> remove();
}

Edit: I've now changed my ARequest interface and service implementation to support a "saveAndReturn" method, so that I can recursively "persist" "a" on the server side:

Request<UserSandboxProxy> saveAndReturn(AProxy aProxy);

I find now that when I "flush" my RequestFactoryEditorDriver, the client-side context object has my new "b.name" value. However, if I call "context.fire()" and inspect my "saveAndReturn" method on the server side, the resulting server-side object "a", just before I "persist" it, doesn't contain the change to "b.name" on any item of the List.

Why could this be happening? How do I debug why this client-information doesn't go across the wire, to the server?

Options I've considered, tried and ruled out:

1) Ensuring the APT has been run, and there are no warnings/errors on Proxy or Service interfaces

2) Ensuring that my proxies does have a valid setter in AProxy for the List

Community
  • 1
  • 1
yaraju
  • 547
  • 3
  • 18
  • Are you using the OpenSessionInView (aka session-per-request) pattern? With JDO, that means one `PersistenceManager` spanning the whole HTTP request. – Thomas Broyer Dec 28 '12 at 19:23
  • @ThomasBroyer No, I'm not. For now, I have this method: `private static PersistenceManager getPM() { return JDOHelper.getPersistenceManagerFactory("transactions-optional").getPersistenceManager(); }` And I call it for each DAO operation, such as "persist()", etc. and pm.close() it as soon as that op ends. But whether or not "persist()" completes correctly, shouldn't I at least, on debug, see my server-side (pre-persist) obj has my changed value? How is this affected by whether I use OSIV? I tried both setting of "a.name" and "b.name" ops from the freshly loaded app. – yaraju Dec 29 '12 at 10:59

1 Answers1

2

You have to use a session-per-request pattern for RequestFactory to work properly. More details here: https://code.google.com/p/google-web-toolkit/issues/detail?id=7827

Thomas Broyer
  • 64,353
  • 7
  • 91
  • 164
  • Awesome! Thanks a lot, Thomas! I finally used Guice with a RequestScoped instance of PersistenceManager to implement this. – yaraju Jan 02 '13 at 14:06