Resubmitting RequestFactory request when server side logic requires it

199 views
Skip to first unread message

Ryan McFall

unread,
Jun 4, 2012, 4:17:52 PM6/4/12
to Google Web Toolkit
I am having issues when I want to resubmit some requests to the server
when the server-side logic indicates it should happen. In particular,
here's what happens:

1. The user logins in, which causes an object (SurveyResponseHeader)
to be created on the server-side, and sent back to the client. edit
is then called on the client side on that object.

2. The user fills in a set of SurveyResponse objects into a list
contained by the SurveyResponseHeader object (and a corresponding
request is made to save the object on the server).

3. The user submits the survey responses, but the session has timed
out on the server. An error code is sent back to the client, and the
user is asked to re-authenticate.

At this point, I want to resubmit the request that caused the error;
it should succeed on the server now because the session has been
restarted. However, I can't use the same request object because I'll
get the "request is already in progress" error message.

Rather than tell you everything I've tried, I'd like to hear what
people think is the right way to accomplish this. I can provide more
details if needed, but I want to keep the description simple at least
to start.

Thanks,
Ryan

Thomas Broyer

unread,
Jun 5, 2012, 5:21:00 AM6/5/12
to google-we...@googlegroups.com
You have to make sure you convey the session-expiry-error as a "transport error" or "general failure", and not as a "service method return value".
Using the DefaultRequestTransport, any non-200 response from the server will be a "transport error", so handling session expiration from a servlet filter on the server and responding with a non-200 status code (let's say, for instance, a 401, or 400; make sure you don't redirect to your login page though!) should Just Work(tm).
To tell the difference between a real server failure and session expiration though, you'll have to make your own RequestTransport on the client (possibly extending DefaultRequestTransport) and define some kind of "protocol" (shared knowledge) with the server. Have a look at how it's done in the MobileWebApp sample in the GWT SDK: http://code.google.com/p/google-web-toolkit/source/browse/trunk/samples/mobilewebapp/src/main/java/com/google/gwt/sample/gaerequest/ It's specific to Google AppEngine but is really easy to adapt to whatever you use. The trick is to make sure you don't set a <security-constraint> on the RequestFactoryServlet in your web.xml, and instead handle the absence of authenticated user from code (in a servlet filter is the easiest)

Ryan McFall

unread,
Jun 5, 2012, 6:38:51 AM6/5/12
to Google Web Toolkit
Thanks for the ideas, Thomas.

Two questions on the response:
1. I was hoping to detect this within my service method, rather than
at a higher level like a servlet filter. Is it possible to return a
non-200 error code from within the service method? I tried getting
the thread local HttpResponse object and changing its status code, but
this had no effect.

2. If I do write a servlet filter, there are only certain service
methods that I want this code to apply to (for example, I use a
service method, rather than the built in servlet authentication to
authenticate the user, and I certainly don't want to require a valid
session during this call). From what I see in the sample code you
posted, it looks like that application is doing this on every
request. What is the best way to differentiate which service
method(s) are being called as part of the request? Are there classes
available that will help me parse the request payload on the server
and identify the methods being called?

Ryan
> in the MobileWebApp sample in the GWT SDK:http://code.google.com/p/google-web-toolkit/source/browse/trunk/sampl...It's

ashwin....@gmail.com

unread,
Jun 5, 2012, 6:55:15 AM6/5/12
to google-we...@googlegroups.com

Ryan,

You can create your own custom annotation (for example : LoggedIn). Apply this on all methods you want to authenticate the user/ session to be validated. 

Define your own ApplicationRequestFactoryServlet and ApplicationServiceLayerDecorators. Override the invoke method under ServiceLayerDecorator and validate for the presence of @LoggedIn annotation. If present, check if the user session is valid.

regards
Ashwin



--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To post to this group, send email to google-we...@googlegroups.com.
To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.


Jens

unread,
Jun 5, 2012, 8:22:32 AM6/5/12
to google-we...@googlegroups.com
You can create your own custom annotation (for example : LoggedIn). Apply this on all methods you want to authenticate the user/ session to be validated. 

Better do it the opposite way if you only have few methods that are allowed for unauthenticated users (I think thats more typically), e.g. create a @AllowUnauthenticated annotation and if its absent assume the user must be logged in. That way you have few "opt-out" methods instead of many "opt-in" methods.

-- J.

Ryan McFall

unread,
Jun 5, 2012, 9:58:22 AM6/5/12
to Google Web Toolkit
After I wrote my original follow-up to Thomas' message, I thought of
having different services - one for methods that require
authentication, and one for those that don't. Then I can map my
ServletFilter to the URL for the service that requires authentication,
and not map it to those that don't.

That seems easier to me than the annotation route. Anyone have any
reasons to think otherwise?

Ryan

Thomas Broyer

unread,
Jun 5, 2012, 10:08:59 AM6/5/12
to google-we...@googlegroups.com


On Tuesday, June 5, 2012 3:58:22 PM UTC+2, Ryan McFall wrote:
After I wrote my original follow-up to Thomas' message, I thought of
having different services - one for methods that require
authentication, and one for those that don't.  Then I can map my
ServletFilter to the URL for the service that requires authentication,
and not map it to those that don't.

That seems easier to me than the annotation route.  Anyone have any
reasons to think otherwise?

It won't work. RequestFactoryServlet loads from the classpath, so unless you somehow constrain the classpath of each servlet to only contain the classes you want to expose, the unauthenticated servlet would be able to load the "services requiring authentication", therefore allowing unauthenticated access to them.
In other words, that's not how RF has been designed. 

Ashwin Desikan

unread,
Jun 5, 2012, 11:13:52 AM6/5/12
to google-we...@googlegroups.com
Thomas is correct. I had tried the route of creating two separate RF's. But that involves more work in comparison to annotations. In fact I changed my approach post Thomas suggestion in this forum quite sometime bac 

~Ashwin

Sent from my iPhone
--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.

Ryan McFall

unread,
Jun 7, 2012, 2:30:35 PM6/7/12
to Google Web Toolkit
I'm well on my way to getting this to work. I can determine whether
the method to be invoked requires authentication, and whether or not
the user is currently authenticated.

Currently, what I've done is defined a new runtime exception and
thrown that exception in my ServiceLayerDecorator if an
unauthenticated user tries to invoke a method requiring
authentication. I've then overridden doPost in my sub-class of
RequestFactoryServlet to catch this exception and set the status code
of the response to be 401.

Unfortunately, this doesn't quite work. I get the following in my
server console when this exception is thrown by my code:

SEVERE: Unexpected error
edu.hope.cs.surveys.editor.server.SessionExpiredException: Session
expired
at
edu.hope.cs.surveys.editor.server.ActiveSessionDecorator.invoke(ActiveSessionDecorator.java:
17)
at
com.google.web.bindery.requestfactory.server.ServiceLayerDecorator.invoke(ServiceLayerDecorator.java:
111)
at
com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.processInvocationMessages(SimpleRequestProcessor.java:
455)
at
com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.process(SimpleRequestProcessor.java:
225)
at
com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.process(SimpleRequestProcessor.java:
127)
at
com.google.web.bindery.requestfactory.server.RequestFactoryServlet.doPost(RequestFactoryServlet.java:
133)
at
edu.hope.cs.surveys.editor.server.JandyRequestFactoryServlet.doPost(JandyRequestFactoryServlet.java:
28)

I see that SimpleRequestProcessor has a setExceptionHandler method
which is responsible for creating a ServerFailure object. Do I need
to provide a custom exception handler to the RequestProcessor? Or is
there some other way of catching the failure caused by the invoke
method of my ServiceLayerDecorator?

Thanks,
Ryan

On Jun 5, 11:13 am, Ashwin Desikan <ashwin.desi...@gmail.com> wrote:
> Thomas is correct. I had tried the route of creating two separate RF's. But that involves more work in comparison to annotations. In fact I changed my approach post Thomas suggestion in this forum quite sometime bac
>
> ~Ashwin
>
> Sent from my iPhone
>
> On Jun 5, 2012, at 7:38 PM, Thomas Broyer <t.bro...@gmail.com> wrote:
>
>
>
>
>
> > On Tuesday, June 5, 2012 3:58:22 PM UTC+2, Ryan McFall wrote:
> > After I wrote my original follow-up to Thomas' message, I thought of
> > having different services - one for methods that require
> > authentication, and one for those that don't.  Then I can map my
> > ServletFilter to the URL for the service that requires authentication,
> > and not map it to those that don't.
>
> > That seems easier to me than the annotation route.  Anyone have any
> > reasons to think otherwise?
>
> > It won't work. RequestFactoryServlet loads from the classpath, so unless you somehow constrain the classpath of each servlet to only contain the classes you want to expose, the unauthenticated servlet would be able to load the "services requiring authentication", therefore allowing unauthenticated access to them.
> > In other words, that's not how RF has been designed.
> > --
> > You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
> > To view this discussion on the web visithttps://groups.google.com/d/msg/google-web-toolkit/-/fl0PLdvGKHUJ.

Thomas Broyer

unread,
Jun 7, 2012, 3:06:32 PM6/7/12
to google-we...@googlegroups.com
If you go the ServiceLayerDecorator route, you can no longer use an HTTP status detected by a custom RequestTransport, you're pluggin *into* the RF protocol, not *around* it.
Try using the die() or report() methods from ServiceLayerDecorator and see how they behave.
> > To post to this group, send email to google-web-toolkit@googlegroups.com.
> > To unsubscribe from this group, send email to google-web-toolkit+unsub...@googlegroups.com.

Ryan McFall

unread,
Jun 7, 2012, 4:40:23 PM6/7/12
to Google Web Toolkit
Thanks, Thomas, I searched through the methods in
ServiceLayerDecorator for something like this, but somehow missed
these.

Using report() from with the ServiceLayerDecorator doesn't allow me to
re-fire the RequestFactory request, giving me the "A request is
already in progress" error. If I call die() then the exception is
still printed out, but the onFailure method of the Receiver is in fact
called, and I can re-fire the request.

Ryan

On Jun 7, 3:06 pm, Thomas Broyer <t.bro...@gmail.com> wrote:
> If you go the ServiceLayerDecorator route, you can no longer use an HTTP
> status detected by a custom RequestTransport, you're pluggin *into* the RF
> protocol, not *around* it.
> Try using the die() or report() methods from ServiceLayerDecorator and see
> how they behave.
>
>
>
> On Thursday, June 7, 2012 8:30:35 PM UTC+2, Ryan McFall wrote:
>
> > I'm well on my way to getting this to work.  I can determine whether
> > the method to be invoked requires authentication, and whether or not
> > the user is currently authenticated.
>
> > Currently, what I've done is defined a new runtime exception and
> > thrown that exception in my ServiceLayerDecorator if an
> > unauthenticated user tries to invoke a method requiring
> > authentication.  I've then overridden doPost in my sub-class of
> > RequestFactoryServlet to catch this exception and set the status code
> > of the response to be 401.
>
> > Unfortunately, this doesn't quite work.  I get the following in my
> > server console when this exception is thrown by my code:
>
> > SEVERE: Unexpected error
> > edu.hope.cs.surveys.editor.server.SessionExpiredException: Session
> > expired
> >         at
> > edu.hope.cs.surveys.editor.server.ActiveSessionDecorator.invoke(ActiveSessi­onDecorator.java:
>
> > 17)
> >         at
> > com.google.web.bindery.requestfactory.server.ServiceLayerDecorator.invoke(S­erviceLayerDecorator.java:
>
> > 111)
> >         at
> > com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.process­InvocationMessages(SimpleRequestProcessor.java:
>
> > 455)
> >         at
> > com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.process­(SimpleRequestProcessor.java:
>
> > 225)
> >         at
> > com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.process­(SimpleRequestProcessor.java:
>
> > 127)
> >         at
> > com.google.web.bindery.requestfactory.server.RequestFactoryServlet.doPost(R­equestFactoryServlet.java:
>
> > 133)
> >         at
> > edu.hope.cs.surveys.editor.server.JandyRequestFactoryServlet.doPost(JandyRe­questFactoryServlet.java:
> > google-we...@googlegroups.com.
> > > > To unsubscribe from this group, send email to
> > google-web-tool...@googlegroups.com.

Thomas Broyer

unread,
Jun 8, 2012, 3:47:17 AM6/8/12
to google-we...@googlegroups.com


On Thursday, June 7, 2012 10:40:23 PM UTC+2, Ryan McFall wrote:
Thanks, Thomas, I searched through the methods in
ServiceLayerDecorator for something like this, but somehow missed
these.

Using report() from with the ServiceLayerDecorator doesn't allow me to
re-fire the RequestFactory request, giving me the "A request is
already in progress" error.  If I call die() then the exception is
still printed out, but the onFailure method of the Receiver is in fact
called, and I can re-fire the request.

Be careful though: if you enqueued two invocations in your context, and the second one generated the die() exception (meaning the first one has been processed), when sending the request a second time, the first invocation will be processed a second time too, so it'd better be idempotent! 
Reply all
Reply to author
Forward
0 new messages