Setting ETAG/LastModified on Representation sent b

2019-08-07 03:49发布

问题:

I know I can set the ETAG and LastModified properties on Representation/Repre​sentationInfo. But I have a simple resource implemented like this :

public class AccountServerResource extends ServerResource implements AccountResource {

    private static Logger log = Logger.getLogger(Acc​ountServerResource.c​lass.getName());

    @Override
    public Account retrieve() {
        User user = getClientInfo().getUser();
        AccountDAO dao = new AccountDAO();
        Account ret = dao.getAccountByEmai​l(user.getEm​ail());
        log.info("retrieved " + ret);
        // getResponse().getEntity() == null at this point !!!
        // ---> cannot do this : getResponse().getEntity().setModificationDate(ret.getLastMod​ified());
        return ret;
    }   
}

The representation is not yet attached to the response at this time. When/how do I set the ETAG/LastModified tags ?

What is the recommended practice here ?

---UPDATE---

I tried this approach without luck :

@Override
public Account retrieve() {
        User user = getClientInfo().getUser();
    AccountDAO dao = new AccountDAO(user.getN​amespace());
        AccountDAO dao = new AccountDAO();
        Account ret = dao.getAccountByEmai​l(user.getEm​ail());
    log.info("retrieved " + ret);
    setOnSent(new StrongEtagCallback​<Account>(ret));​
    return ret;
}

And implementation of the StrongEtagCallback like this :

public class StrongEtagCallback<T extends DomainResource> implements Uniform {

    private static SimpleDateFormat df = new SimpleDateFormat("dd​MMyyyyHHmmssSSS");
    private DomainResource d;

    public StrongEtagCallback(T domainResource) {
        d = domainResource;
    }

    @Override
    public void handle(Request request, Response response) {
        String eTag = d.getClass().getSimpleName() + "-" + d.getId() + "-" + df.format(d.getLastModified());
        response.getEntity().setTag(new Tag(eTag, false));
    }
}

Where all my entities implement DomainResource which require them to have an ID and LastModified date.

But it does NOT work. I really expected this to work, it is very elegant !

The StrongEtagCallback is being called though, the ETAG set server-side on the entity. My Wireshark nor my GWT client sees a E-TAG header on the response of the response. Diving deeper now.

回答1:

In researching this issue myself, I noticed a parallel thread started by koma on the Restlet discussion board, in which an alternative and preferable solution was provided by Tim Peierls, namely to override Resource.toRepresentation().

As koma pointed out in that thread, overriding ServerResource.handle() caused conditions matching to fail (I'm not sure why?), so that approach is problematic.

Example override code provided by Tim Peierls:

@Override public Representation toRepresentation(Object source, Variant target) {
    Representation rep = super.toRepresentation(source, target);
    if (source instanceof HasLastModified) {
        HasLastModified hlm = (HasLastModified) source;
        rep.setModificationDate(hlm.getLastModified());
    }
    if (source instanceof HasEtag) {
        HasEtag he = (HasEtag) source;
        rep.setTag(he.gettag());
    }
    return rep;
}


回答2:

The final solution was to make the return domain entity a local variable and override the handle() method of ServerResource. This is safe because the javadoc states that :

one instance of ServerResource is created for each call handled and accessed by only one thread at a time

So the implementation goes like this :

private Account ret = null;

@Override
public Account retrieve() {
    User user = getClientInfo().getUser();
    AccountDAO dao = new AccountDAO();
    ret = dao.getAccountByEmail(UserServiceFactory.getUserService().getCurrentUser().getEmail());
    // getResponse().getEntity().setModificationDate(ret.getLastModified());
    // lastModified = ret.getLastModified();
    log.info("retrieved " + ret);
    //setOnSent(new StrongEtagCallback<Account>(ret));
    return ret;
}

@Override
public Representation handle() {
    Representation representation = super.handle();
    if (ret != null) {
        new StrongEtagCallback<Account>(ret).handle(getRequest(), getResponse());
    }
    return representation;
}

The ETAG header is now sent :

HTTP/1.1 200 OK
Content-Type: application/x-java-serialized-object+gwt; charset=UTF-8
ETag: "Account-104-27012012003721199"
Date: Thu, 26 Jan 2012 23:44:32 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.1rc1
Transfer-Encoding: chunked

PS: my first solution that sets a callback setOnSent is exectued after the response is committed, that's why this solution did not work. I would actually expect a similar hook or some kinda setNext() Restlet to do post-processing. After all, the callback implements the Uniform interface. IMO, this would fit better in the overall architecture of Restlet.