• Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

forward() vs include()

 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
We have discussed in this forum whether or not you can call forward() multiple times and how it behaives in terms of returning control to the calling method.
Here's the thing. forward() is no different from include(), in fact it's a flavor of include() with one defining characteristic -- it throws an exception if called after output has been committed (and does so by choice, not necessity).
So, code like

is perffectly legal as long doas "as_some_calc" commits no output. Not that I condone this.
I would never think of coding like that but I have actually seen production code made in this manner and a recent discussion brought it up in memory. Just thought it's worth mentioning.
 
Madhav Lakkapragada
Ranch Hand
Posts: 5040
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The earlier discussion happened in this thread.

is perffectly legal as long doas "as_some_calc" commits no output.

I respectfully beg to differ. IMO, this is NOT something a Servlet/JSP developer controls. The App Server/Container must control this.
I would never think of coding like that but I have actually seen production code made in this manner
Whatever the web-app was, I would say it is NOT portable. It probably works on the particular app server which it was tested, but there is NO guarentee that it would run on a different app server.
For what its worth.....
- satya
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
1. Of course developer controls the output...by not writing anything to client... or at least checking it with isCommitted().
2. Well, I think it's perfectly portable. I mean why wouldn't it be? This forwarding style does not contradict the spec in any way, can you think of a reason why this shouldn't work? Just because the verbalized description of forward() is "to forward the request" it doesn't mean that after forward() has returned you cannot continue with your processing including calling other forward()s.
3. Again, if you think of forward() as include() that throws IllegalStateException everything falls into places quite nicely.
BTW, i tested it on Tomcat, which is supposed to be the closest spec implementation and it worked like a charm.
Although, I must note, JSP spec mandates that any java code following jsp:forward be removed from generated servlet code. This is done intentionally to emphasize the usage of forward(), reduce error-proness, enhance clarity of jsp programming, blah blah blah, but again it's not to say that multiple forward()s would not work had they decided not to enforce this JSP constraint.
 
Scott Ramsey
Greenhorn
Posts: 16
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Gennady Shapiro:

Here's the thing. forward() is no different from include(), in fact it's a flavor of include() with one defining characteristic -- it throws an exception if called after output has been committed (and does so by choice, not necessity).
So, code like

is perffectly legal as long doas "as_some_calc" commits no output.

Maybe I'm not understanding what you're saying, but, according to section 8.4 of the 2.3 Servlet spec the second line of your example code will never execute (on any implementation of the servlet container) since, "Before the forward method of the RequestDispatcher interface returns, the response content must be sent and committed, and closed by the servlet container."
This means that while the code may be technically legal (i.e. it won't cause a compile error), it certainly won't behave as intended.
Thus, I would also have to disagree that a forward() is merely and include() with only an extra exception to throw. A servlet can have multiple includes to build a response, but only the first forward will be executed. (I suppose one could have a circular set of forward() calls among a group of servlets, but such programming technique is probably pretty suspect design in most cases.)
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Scott, you make very valid argument.
The spec does dictate that forwarded resource must write and commit, however, there is no way to validate this behaivior of the resource since RequestDispatcher (on that resource) can be obtained at run-time. Therefore, write and commit by 'forwardee' cannot be enforced at compile time. And this is what makes multiple forwards legal and portable.
I am not saying this is a good practice, quite the opposite, but multiple forward()s do not violate any Servlet protocols and contracts, except mentioned above, which accidentally cannot be enforced.
The second point is that you always can aggregate include()s but not forward()s. Again, true -- in theory, in practice you can call as many forward()s as you want as long as they dont commit output.
Imagine a forward call that does not throw exceptions and permits previously commited output. How is it different from include()? I see no difference.
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I just looked at Tomcat's implementation of RequestDispatcher (RequestDispatcherImpl.java). The implementations of forward and include are really similar: they both do
context.getContextManager().processRequest(realRequest);
but before that forward does this:
if (realResponse.isBufferCommitted())
throw new IllegalStateException(sm.getString("rdi.forward.ise"));
.
forward() is intentionally made that way to enforce the concept of "forwarding" as opposed to "including". But in fact these to methods lagely execute the same code.
 
Madhav Lakkapragada
Ranch Hand
Posts: 5040
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
1. Of course developer controls the output...by not writing anything to client... or at least checking it with isCommitted().
I tried this. When you don't write anything to the client, the server throws an exception to indicate "Document containes no data." and then removes the empty servlet from the Service.
Hence, the response is always committed, as the server returns the error-code to the client.
And regarding your other point about multiple forwards , I prefer not to comment. Much has already been said earlier.....
I just looked at Tomcat's implementation
Personally, I don't think this is a good approach of deciding wether forward and include are same or different. Afterall how many servers give you the source code...and why would they. I prefer sticking to the spec and follow that.
My two cents.....
- satya
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
1. How is that even possible? Server removes a servlet from service if it contains no data? what if this servlet is meant to produce no data? What server are you running it on?
2. Looking at server code never hurts. You'll always better undertand how it works and why if you do. As is in this case, now you know that forward() is a constrained include() which is the purpose of this exercise anyway.
 
jason adam
Chicken Farmer ()
Ranch Hand
Posts: 1932
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I ran two test servlets on two different servers (Orion and Tomcat), and got two completely different results. Here's the jist:
ServletTest prints out a statement
ServletTest forwards to Servlet2Test
Servlet2Test prints out a statement
Servlet2Test forwards to ServletTest
ServletTest prints out a statement
On Orion, this actually worked. I ended up with 3 statements in the order that you would expect.
On Tomcat, I got an IllegalStateException error at runtime.
So yes, multiple forwards are doable (I think we've beaten that horse enough). However, this code is not in any way portable. It relies on the server implementation, as is obvious by the example. One server it "works", the other it doesn't.
If you want the code, gohere
[ March 05, 2002: Message edited by: jason adam ]
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jason, you are missing an important point.
You test with response.getWriter (or such) and by doing so you commit the output which must cause IllegalStateException. If Orion passes this test it's going against the spec.
My whole point is doing multiple forward()s without writing to client. This does not violate the spec. So replace your response.getWrter().print with System.out.print() and Tomcat will pass this test -- I know I tested Tomcat 4.0.
P.S. This wouldnt be the first time Orion went against the spec. It synchronizes SerletContext as well as sessions in cluster mode, which is also against the spec -- no to say that this is such a bad idea.
 
jason adam
Chicken Farmer ()
Ranch Hand
Posts: 1932
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You mean there was a point to all of this?
I agree with forwarding multiple requests if there is no writing to the response, that's logical (and not mentioned at all in the spec's, correct?). I thought this whole discussion was related to output, my bad!
And thanks for the extra info on Orion. I think I might bring that up to the higher powers in regards to the Cattle Drive. Using a server that disregards the specs doesn't seem very wise, even if the assignments don't come close to touching on this subject.
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You are welcome.
And to finalize this all : forward() is the same god damned include() only superficially throws IllegalStateException if prior output has been commited.
 
Peter den Haan
author
Ranch Hand
Posts: 3252
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Gennady Shapiro:
And to finalize this all : forward() is the same god damned include() only superficially throws IllegalStateException if prior output has been commited.
Language, please
Anyway, there are more differences than you seem to imply here. Sure, both "forward" and "include" are just fancy ways of performing a Java method call to another servlet, but
  • As you note, you can add to the response after an include - the response has been committed and closed after a forward, any attempt to write to it results in an IllegalStateException.
  • Along similar lines, an include is always possible - a forward is possible only if output has not been committed yet, otherwise an IllegalStateException is thrown.
  • An included resource cannot set response headers - a forwarded resource can.
  • The HttpServletRequest path information for an include reflects the original request - the information for a forward reflects the forwarded request.
  • As a consequence two consecutive forwards do not work in any container that conforms to the spec. The first forward commits (and closes) the response, which must cause the second forward to throw an IllegalStateException (Spec 2.3, SRV8.4). The code discussed above is not legal, not portable, and any servlet container which actually executes the second forward does so in clear violation of the spec.
    - Peter
    P.S. Upon reflection the code ought to be portable, as the specification clearly defines what the container's behaviour must be. In practice, it isn't, though.
    [ March 06, 2002: Message edited by: Peter den Haan ]
     
    Gennady Shapiro
    Ranch Hand
    Posts: 196
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Ok. Can you explain to me how the container enforces Before the forward method of the RequestDispatcher interface returns, the
    response content must be sent and committed, and closed by the servlet container.
    if there was no output??
    How can it possibly ? There is no way. The container cannot stop a servlet from forwarding requests to a no-output-servlet. The container cannot "send and commit" response if there is none, unless it sees no output and decides to be creative and sends to the browser "<HTML>WooHooo! This is forward speaking. Your programmer sucks, he aint gave you no output so I'm gonna give you some!</HTML>" and commit it so the following forward()s wouldnt even think of executing.
    You are correct about include() and forward() having some minor differences but they fall under the "commited output" category.
    The point is if your forward() does not produce ouput the container cannot commit, the spec says "response must be sent and commited" -- no output no commitment.
    Therefore you MUST be able to call consequtive forward()s because they may not throw IllegalStateException because no output had been commited. I mean, come on, there is no spec violation here...bad practice maybe but not spec violation. Therefore it is legal and very portable, even though against suggestion.
    I tested this code on Tomcat 3.2, 4.0 and Weblogic 6.1, the code works lovely as it should. I am reasonably sure it'll work on others too.
    P.S. You know, I challenge anyone to find a server that will NOT execute this code...if you on vacation, afraid to fly and itching to do sotmething.
    [ March 06, 2002: Message edited by: Gennady Shapiro ]
     
    Peter den Haan
    author
    Ranch Hand
    Posts: 3252
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Originally posted by Gennady Shapiro:
    Ok. Can you explain to me how the container enforces Before the forward method of the RequestDispatcher interface returns, the
    response content must be sent and committed, and closed by the servlet container.
    if there was no output??
    Well, just close it, of course. The response would consist of a response header (with at least an HTTP response code) and no body. There's no ambiguity there.
    In fact with a number of possible response codes that would be a perfectly sensible and valid format!
    This rather undermines your line of reasoning... but quite apart from the sense or nonsense of the generated response, the specification is really unequivocal here. The second forward must fail with an IllegalStateException.
    I tested this code on Tomcat 3.2, 4.0 and Weblogic 6.1, the code works lovely as it should.
    That's all well and good, but if they do then they seem to do so in violation of the specification. I would appreciate it if you would point out where I am misreading it.
    - Peter
    [ March 06, 2002: Message edited by: Peter den Haan ]
     
    Peter den Haan
    author
    Ranch Hand
    Posts: 3252
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Postscriptum: I would be very curious as well to learn how you conducted your tests. Had a brief go at Tomcat 3.2 to see if it was as non-compliant as you suggest, but it does fine: the first forward() is executed and returns a response with an empty body, the second forward() fails withAnd to answer the obvious question: no, my code retrieves neither OutputStream nor Writer. Code available to any interested party on request.
    Sanity has been restored. Can we put this matter to rest now?
    - Peter
    [ March 07, 2002: Message edited by: Peter den Haan ]
     
    Gennady Shapiro
    Ranch Hand
    Posts: 196
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Not quite yet.
    You are correct, Tomcat 3.2 fails the test. I wasnt diligent enough to check Tomcat 3.2 logs for exeptions. I believe I missed it because I was expecting an exception appear in console out. However, Tomcat 4.0 does pass the test -- I examined the logs, hell, I even ran it in a debugger to make sure I dont miss any exceptions.
    This exception indicates that Illegal State occured due to previous out obtaining not previous out commiting. It does not really say that response has been commited (as the spec insists). But I also tried reset() after forward() and it failed on Tomcat 3.2 saying cannot reset after writing to client, which implies that output has been commited even though no body and no headers were explicitelly written. I guess empty http response is sent to the client by Tomcat 3.2's forward().
    Ok. I give up. (Even though Tomcat 4 does allow this)
     
    Peter den Haan
    author
    Ranch Hand
    Posts: 3252
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Dropped the same servlets on JRun 3.02a:Pass, with full marks for the error message. The same code on Orion 1.5.2, though, actually performs both forwards - a fail in my book.
    We should be able at least to agree that the code you present is emphatically non-portable.
    I should really reiterate that committing the response without having written any output does not send an empty response! It merely sends an HTTP response with an empty body. There is nothing strange or invalid about this; section 4.3 of the HTTP 1.1 spec states that For response messages, whether or not a message-body is included with a message is dependent on both the request method and the response status code [... snipped a discussion of responses that MUST NOT have a body ...] other responses do include a message-body, although it may be of zero length. Any server that refuses to commit a response with an empty body prevents you from sending some perfectly valid HTTP responses, which might well be considered a bug.
    - Peter
    PS. Regarding the Tomcat 3.2 exception, it is perfectly fine with the specification but I agree that there's something fishy about the message. It seems to imply that it is impossible to forward after retrieving a response stream or writer. If so, this could be seen as a violation of the Servlet specification, which merely states that any uncommitted response content must be cleared.
    [ March 08, 2002: Message edited by: Peter den Haan ]
     
    jason adam
    Chicken Farmer ()
    Ranch Hand
    Posts: 1932
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Poor horsey just can't get enough beatin's...
    Anyways, I was playing around with this again (finally being able to sit down and really delve into the specs), and came up with something that bothers me.
    I have a servlet that calls res.getWriter().println( "Some stuff" );
    I do a flush, and I test for isCommitted(). I then grab a RequestDispatcher from the request, send it to another servlet that has one line (another res.getWriter().println( "Some other stuff" )) , and call forward().
    Now, I would expect to get an IllegalStateException, since according to the print out my response is committed. I don't, I get everything printing out on the first servlet, don't get anything from the second servlet. It is only until I put another println() statement after the forward() that I get the IllegalStateException.
    Shouldn't having a committed response throw this error? I'm using the latest Tomcat.
    [ March 13, 2002: Message edited by: jason adam ]
     
    jason adam
    Chicken Farmer ()
    Ranch Hand
    Posts: 1932
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Well, looking at the logs, I do get an error. Out of curiosity, why does it not get thrown up on the page in this instance, where other times it does?
     
    Peter den Haan
    author
    Ranch Hand
    Posts: 3252
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    That's somewhat bizarre behaviour, I agree.
    The normal response to exceptions is to clear the response buffer and forward the request to the error page, but that is no longer possible if (part of) the response has already been committed. I've looked in the spec (SRV9.9) but it doesn't really cover what should happen in that case.
    - Peter
     
    • Post Reply
    • Bookmark Topic Watch Topic
    • New Topic