Saving request parameters before redirect

T

Terry

Hello,

I've been unable to solve a servlet problem. I hope this group can
offer some ideas.

I have several servlets that accepts only post data. Depending on
business logic, a servlet may need to redirect to another page on
another site with only the address of the servlet appended to the query
string so that the outside page can post back to the servlet. This
seems like an unusual design - it's not mine, but I'm stuck with it.

Here's where it gets tricky. If a servlet does need to redirect to an
outside site, it must first store the original request parameters. When
the outside site posts back, it can discard completely anything sent
from that site, but it needs to reinstate the original request
parameters in the current request object and resume processing.

I can use session variables and cookies, but cannot write to a file or
to a database. The original request parameters will usually be less
than 1kb in size, but could be as large as 8 Kb in some rare cases.

I've approached the problem like this: I have a utility object with one
method. The method is called as the first line of doPost() by each
servlet that needs to implement this functionality. This method takes
two parameters: the request and response passed to doPost(). If the
method determines the servlet does not need to redirect, it simply
returns and the servlet resumes processing. If the method determines
the servlet must redirect, it first calls getParameterMap() on the
request, then stores the returned map in a session variable. It then
calls the response object's sendRedirect() method to send the browser to
the outside page. When the page posts back (usually within seconds),
the servlet again calls the utility object's single method as the first
line of its code, again passing it the request and response objects of
the doPost method. This time, the method looks for and finds the
original request parameters stored as a map in a session variable. It
retrieves the map, then deletes the session variable.

Now, what I want to do is to have the request contain the original
parameters so that when the method returns, the servlet can continue
processing as though it never left. I can set *attributes* in the
request via setAttribute(), but it doesn't seem possible to set
*parameters*. There's nothing valuable in the post back from the
outside site, so I can change it all I want, so long as at the end, the
servlet can continue processing with the original request parameters.
I've thought about storing the whole request object in a session and
replacing the request from the outside site with the original, but since
Java passes object references by value, swapping the request object
inside the method has no effect once the method returns to doPost(),
which is where I need the request to have the original parameters.

I suspect Java has a far more eloquent mechanism for accomplishing what
I want to do, I'm just not aware of it.

Any ideas? Thanks in advance for your feedback.

--Terry.
 
A

Andy Fish

I assume that "normal processing" of a request involves passing the
HTTPServletRequest object down to some method.

Rather than trying to fudge the request that comes back from the second
server to make it look like it had different parameters, you could have your
own object that implements HTTPServlet request. If the original request does
not need redirection you just pass it through. If it needs redirection, you
build a fake request and store it away; when the response comes back from
the other server you dig out the fake request and pass it down to the worker
routines.

if this sounds like too much work, you could consider abstracting the
concept of "request" so that the routines that process the request just
accept a simpler object as a parameter (maybe a hashmap of names and
values). you can construct this parameter from the current request, a stored
request, or any combination thereof

Andy
 
J

John C. Bollinger

Followups to comp.lang.java.programmer.

Andy said:
I assume that "normal processing" of a request involves passing the
HTTPServletRequest object down to some method.

Rather than trying to fudge the request that comes back from the second
server to make it look like it had different parameters, you could have your
own object that implements HTTPServlet request.

Depending on how closely you need to make the second request simulate
the first one, you may find that the HttpServletRequestWrapper class
provides a convenient avenue of implementation for that plan. This
might be the "far more eloquent mechanism" you hoped to find.
If the original request does
not need redirection you just pass it through. If it needs redirection, you
build a fake request and store it away; when the response comes back from
the other server you dig out the fake request and pass it down to the worker
routines.

With that general strategy it's up to you to determine at what point you
actually construct your stand-in request object. You certainly need to
store some information before redirecting, but depending on your exact
requirements, it might make more sense to delay constructing the actual
request until the second time around.

NOTE WELL: you *must not* try to store a reference to the original
request object as part of this scheme. That object's state is not
guaranteed to be preserved after the servlet container is done
processing the associated request.
if this sounds like too much work, you could consider abstracting the
concept of "request" so that the routines that process the request just
accept a simpler object as a parameter (maybe a hashmap of names and
values). you can construct this parameter from the current request, a stored
request, or any combination thereof

That's an approach that has some distinct advantages, not least that it
decouples the remaining processing from the servlet API. Such a
decoupling may not be feasible, however, or might be more work than it's
worth. If you can make it work, though, some variation on this approach
would be my recommendation.


John Bollinger
(e-mail address removed)
 
T

Terry

Thanks for your reply!

I like the suggestion about storing data in a simpler data structure
such as a Hashtable, but it will take a lot of work to make those
changes. I prefer to store the entire request if possible.

I'm confused about the following statement:
NOTE WELL: you *must not* try to store a reference to the original
request object as part of this scheme.

I think I understand the meaning - that the contents of the same memory
location is undefined between servlet requests - but I don't understand
the implementation (sorry, I'm a relative newcomer to Java). I thought
*everything* in Java is a reference. For example isn't the following
also storing a reference?

String foo = "bar";
request.getSession( ).setAttribute( "myString" , foo );

Isn't that storing a reference to the String object foo in the session?
Doesn't the memory occupied by foo risk being deallocated during
a garbage collection once the servlet has finished with the request? If
not, what is the difference between that and:

request.getSession( ).setAttribute( "myRequest" , request );

Is it because the servlet container explicitly deallocates the request
object after the servlet has run? Or does it have something to do with
serialization? I read through the javadoc, and setAttribute( ) seems
only to require an Object to be stored, and makes no mention of
serializable (except in the case of a distributed web app, which this is
not). In addition, HttpServletRequest( ) doesn't implement the
Serializable interface.

My question then is this, if the above only stores a reference to some
inconsistent memory location, how can I store an HttpServletRequest in
the session and retrieve it later?

Thank you so much for your help.

-Terry.
 
J

John C. Bollinger

Terry wrote:

Your newsreader did not honor my followup-to instruction, or you
willfully overrode it. Fix your newsreader or your attitude, whichever
applies. Followups to (again) comp.lang.java.programmer.
I'm confused about the following statement:




I think I understand the meaning - that the contents of the same memory
location is undefined between servlet requests - but I don't understand
the implementation (sorry, I'm a relative newcomer to Java).

No, the memory location is not undefined. You cannot have references to
undefined memory in Java. The object referred to can, however, be
modified through a different copy of the reference. In this particular
case, the servlet spec defines that the request reference it hands you
is only valid for you to use during the process of servicing one
request. What happens afterward is an application question, not a Java
question, but many servlet containers reuse request objects to service
other requests.
I thought
*everything* in Java is a reference.

That's a strange way to put it. I more often hear the idea that
"everything in Java is an Object", which makes more sense but is still
wrong. Java has primitive data types as well as reference types, the
latter being handles on objects.
For example isn't the following
also storing a reference?

String foo = "bar";

foo is a variable that can contain a reference to a String object.
"bar" is a String literal, representing an object of type String, and
the line directs that a reference to the String object be stored in foo.
request.getSession( ).setAttribute( "myString" , foo );

The getSession() method is invoked on the object referred to by the
reference stored in variable request, returning a reference. The
setAttribute method is invoked on the referrent of the second reference,
with two arguments, a reference to a String object containing the
characters "myString" and the reference stored in variable foo. (Aren't
you glad you don't have to write programs in English?)
Isn't that storing a reference to the String object foo in the session?

That's a fairly succinct way of describing it.
Doesn't the memory occupied by foo risk being deallocated during
a garbage collection once the servlet has finished with the request? If

(1) If foo is a local variable then the memory it occupies is available
for reuse as soon as the method containing it exits.

(2) The object last referred to by the reference stored in foo may or
may not become eligible for GC at that time, depending on whether there
are other live references to it. In this particular case, there is at
least one other live reference, the one just stored in the session.
not, what is the difference between that and:

request.getSession( ).setAttribute( "myRequest" , request );

There is no fundamental difference in the operation of the methods, but
that's irrelevant.
Is it because the servlet container explicitly deallocates the request
object after the servlet has run? Or does it have something to do with
serialization?

No and no. Java does not have explicit deallocation, and serialization
is completely beside the point. The issue is that the servlet container
does not guarantee that it will not change the state of the request
after you finish processing it (as defined by doGet or doPost returning
to the servlet container or throwing an exception). This can be a
difficult to find bug, because you may find that sometimes, maybe even
most of the time, you can get away with storing a reference to the
request. But in most servlet containers it will sometimes fail.

You seem to be willing to and capable of a bit of research. Instead of
guessing, then, go get the Servlet Specification v 2.3 from Sun's web
site, and read it. Among other things, it explains fairly clearly the
contracts that servlets are expected to abide by and the scope of
various objects that are tossed around the inside of a servlet container.
My question then is this, if the above only stores a reference to some
inconsistent memory location, how can I store an HttpServletRequest in
the session and retrieve it later?

You cannot safely do so. Period.


John Bollinger
(e-mail address removed)
 
L

lixin chu

This
seems like an unusual design - it's not mine, but I'm stuck with it.

might not be unusual. I think a lot of single sign-on products use the
similar way. you can check open source Yale CAS as an example.
 
A

Arvind

Terry,

Just to rephrase what John points out - technically nothing prevents
you from doing session.setAttribute(x,request)

However - the object that is being referenced by the variable request
- is *NOT* guaranteed to be integral - beyond the scope of that http
request call.

It might work out that the reference is still valid - but that is
*purely* co-incidental and not definitive.

You might want to adopt the 'simpler' data structure to for elegant
solution.... finally all the communication contracts between
controller & views and controllers & models are via value objects
anyways !

Arvind
 
T

Terry

Hi John.

I've taken your advice and read the servlet spec. It looks like I've
got a whole lot of retrofitting to do, but I now understand why it
wasn't working before and what I have to do to make it work.

Many thanks for your time and patience.

-Terry
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top