You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Micael Padraig Og mac Grene <ca...@harbornet.com> on 2001/10/29 18:26:14 UTC

PageContext include

I have been informed that Tomcat 4.0 is not supporting PageContext includes.  Is that correct?  I am not downloading it and changing my development plans if that is correct and is not merely a temporary aberration.  Could someone give me the true scoop and the thinking?

Re: PageContext include

Posted by Rickard Öberg <ri...@xpedio.com>.
Rickard Öberg wrote:

> Any bright ideas would be most welcome.

Alright, I think have the "most working" code snippet now. The following 
works on Tomcat4, Orion, and Resin. It's not exactly pretty, and it will 
not work on Servlet 2.2 engines (Tomcat3.2 and Jetty for example), but 
ya can't make an omelette without breaking eggs (or something like that).

Here we go:
----
IncludeTag.doEndTag should have this:
String resourcePath = getContextRelativePath(request, result);
RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
rd.include(request,
            new ServletResponseWrapperInclude(response, 
pageContext.getOut()));
---
Add this method to the tag (from the Tomcat4/Jasper code):
private String getContextRelativePath(ServletRequest request,
                                        String relativePath)
{
    if (relativePath.startsWith("/"))
       return (relativePath);
    if (!(request instanceof HttpServletRequest))
       return (relativePath);
    HttpServletRequest hrequest = (HttpServletRequest) request;
    String uri = (String)
    request.getAttribute("javax.servlet.include.servlet_path");
    if (uri == null)
       uri = hrequest.getServletPath();
    return (uri.substring(0, uri.lastIndexOf('/')) + '/' + relativePath);
}

Then include these two static inner classes:
public static class ServletResponseWrapperInclude
    extends HttpServletResponseWrapper
{
    /**
     * The PrintWriter writes all output to the JspWriter of the
     * including page.
     */
    PrintWriter printWriter;
    JspWriter jspWriter;
    ServletResponse response;

    public ServletResponseWrapperInclude(ServletResponse response, 
JspWriter jspWriter)
    {
       super((HttpServletResponse) response);
       this.response = response;
       this.jspWriter = jspWriter;
       this.printWriter = new PrintWriter(jspWriter);
    }

    /**
     * Returns a wrapper around the JspWriter of the including page.
     */
    public java.io.PrintWriter getWriter() throws java.io.IOException
    {
       return printWriter;
    }

    public ServletOutputStream getOutputStream() throws java.io.IOException
    {
       return new ServletOutputStreamWrapper(jspWriter);
    }
}

public static class ServletOutputStreamWrapper
    extends ServletOutputStream
{
    JspWriter jspWriter;

    public ServletOutputStreamWrapper(JspWriter aJspWriter)
    {
       jspWriter = aJspWriter;
    }

    public void write(int b) throws IOException
    {
       jspWriter.write(b);
    }
}
---

Now don't forget to replace all occurances of pageContext.include in 
your tags with these little snippets, or your code will break...

/Rickard

-- 
Rickard Öberg
Software Development Specialist
xlurc - Xpedio Linköping Ubiquitous Research Center
Author of "Mastering RMI"
Email: rickard@xpedio.com


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by Rickard Öberg <ri...@xpedio.com>.
(Sorry for the amount of posts :-)

Rickard Öberg wrote:

> Further testing: Resin 2.0.1 barfs with the same as above, and Jetty is 
> a 2.2/1.1 engine so it doesn't have the wrapper classes...
> 
> Any bright ideas would be most welcome.

I changed SWIR.getServletOutputStream() in the Tomcat4 solution to 
actually return the real servlet output stream. I.e. SWIR.getSOS is 
implemented as follows:
public ServletOutputStream getOutputStream() throws java.io.IOException
{
    // Flush first
    // If the JSP engine uses this call, then you cannot use
    // the include tag from within certain tags that produces
    // BodyContent writers, since you'll get an exception here
    // in that case
    jspWriter.flush();

    return response.getOutputStream();
}

This makes things better, but still weird:
* Orion doesn't crash, but the flush() does nothing, i.e. the includes 
show up at the top of the page and the rest of the page comes next.
* Resin doesn't crash, and the flush() actually flushes, and the weird 
thing is that it allows flush() to be called when it should supposedly 
be done on a BodyContent.

Both these are wrong:
* Orion doesn't work as it should, since flush() doesn't throw an exception
* Resin doesn't work as it should, since it actually flushes the 
BodyContent to the servlet stream, yet this should not be allowed

But, the Resin behaviour is what I really want, i.e. a flush sends the 
current JspWriter content to the underlying stream, and the subsequent 
RD.include code then works as desired.

Argh... weirdness all 'round...

/Rickard

-- 
Rickard Öberg
Software Development Specialist
xlurc - Xpedio Linköping Ubiquitous Research Center
Author of "Mastering RMI"
Email: rickard@xpedio.com


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by Rickard Öberg <ri...@xpedio.com>.
Rickard Öberg wrote:

>>> It's not like you cannot create custom tags that do this kind of 
>>> thing --
>>> it's just that you need to use a mechanism other than
>>> PageContext.include() to implement it.
>>
> 
> 
> FYI, I've just tested this, and it does indeed work on Tomcat4 as 
> intended. However, running it on Orion makes it crash since RD.include() 
> in Orion calls response.getServletOutputStream() instead of 
> response.getWriter(). And the code for the above SRWI class only 
> replaces getWriter() and not getServletOutputStream().

Further testing: Resin 2.0.1 barfs with the same as above, and Jetty is 
a 2.2/1.1 engine so it doesn't have the wrapper classes...

Any bright ideas would be most welcome.

(no Jon, don't, I know what you're thinking..."YMTD"... yeahyeah...;-)

/Rickard

-- 
Rickard Öberg
Software Development Specialist
xlurc - Xpedio Linköping Ubiquitous Research Center
Author of "Mastering RMI"
Email: rickard@xpedio.com


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by Rickard Öberg <ri...@xpedio.com>.
Rickard Öberg wrote:

>> The guts of the method are pretty straightforward:
>>
>>   public static void include(HttpServletRequest request,
>>                              HttpServletResponse response,
>>                              String relativePath,
>>                              JspWriter out,
>>                              boolean flush)
>>       throws IOException, ServletException {
>>
>>       if (flush)
>>           out.flush();
>>       String resourcePath = getContextRelativePath(request, 
>> relativePath);
>>       RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
>>       rd.include(request,
>>                  new ServletResponseWrapperInclude(response, out));
>>
>>   }
>>
>> It's not like you cannot create custom tags that do this kind of thing --
>> it's just that you need to use a mechanism other than
>> PageContext.include() to implement it.


FYI, I've just tested this, and it does indeed work on Tomcat4 as 
intended. However, running it on Orion makes it crash since RD.include() 
in Orion calls response.getServletOutputStream() instead of 
response.getWriter(). And the code for the above SRWI class only 
replaces getWriter() and not getServletOutputStream().

How should I implement SRWI.getSOS() to make it work properly?

/Rickard

-- 
Rickard Öberg
Software Development Specialist
xlurc - Xpedio Linköping Ubiquitous Research Center
Author of "Mastering RMI"
Email: rickard@xpedio.com


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by Paul Speed <ps...@progeeks.com>.
Hello,

If this is the same kind of problem that appears in JSP 1.1 then we
used a different "work around" than the one mentioned.  Basically,
our include tag iterated through the pending body contents and
manually flushed any buffered output by writing the body content as
a string (and then clearing them out).  This worked with our tag
libraries, but I imagine that it may screw up some tags that are
expecting the body content data to hang around for whatever reason.

Anyway... just thought I'd mention it.  I could probably dig up the
code if needed.

-Paul

Rickard Öberg wrote:
> 
> Craig R. McClanahan wrote:
> 
> > You can use PageContext.include() just fine, if all you want to do is pass
> > the included text on to the response page.  This issue comes up only when
> > you want to simulate the behavior of:
> >
> >   <jsp:include page="/..." include="false"/>
> >
> > which, as you point out, is common in templating environments.
> 
> Which is the only reasonable case (i.e. to not flush) if you are doing
> an include tag that is to be *generally used*. The problem, which was
> one of the big fixes in JSP 1.2 (or so we assumed), is that you cannot
> use pageContext.include to implement an include-tag, if you want to be
> able to use that tag *within the scope of other tags*. I.e. if the
> include-tag below is implemented with pageContext.include the following
> snippet will always crash:
> <x:iterate list="foo">
>    <x:include page="bar.jsp">
> </x:iterate>
> 
> The iterator tag will push a BodyContent onto the writer stack, and thus
> the include tag when doing pageContext.include("bar.jsp") will do a
> flush() on that BodyContent, which will result in an exception.
> 
> So, the result is: "you can't use pageContext.include to implement
> include-like-tags if you want to use that tag from within other tags in
> JSP pages". And IMnsHO that makes pageContext.include useless for any
> real include-tag, since the above limitation is just too severe.
> 
> The ability to do this was one of the major reasons our company switched
> from Tomcat 3.2 to Tomcat 4.0, and now that doesn't work....I guess you
> can see how extremely frustrating this is for us.
> 
> > The simplest thing to do would be to base your implementation on the code
> > that Jasper uses for the above idiom.  In
> > org.apache.jasper.runtime.JspRuntimeLibrary there is a public static
> > include() method that the generated code for a JSP page calls, with some
> > appropriate supporting methods and a response wrapper available as well.
> >
> > The guts of the method are pretty straightforward:
> >
> >   public static void include(HttpServletRequest request,
> >                              HttpServletResponse response,
> >                              String relativePath,
> >                              JspWriter out,
> >                              boolean flush)
> >       throws IOException, ServletException {
> >
> >       if (flush)
> >           out.flush();
> >       String resourcePath = getContextRelativePath(request, relativePath);
> >       RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
> >       rd.include(request,
> >                  new ServletResponseWrapperInclude(response, out));
> >
> >   }
> >
> > It's not like you cannot create custom tags that do this kind of thing --
> > it's just that you need to use a mechanism other than
> > PageContext.include() to implement it.
> 
> Note that some servlet 2.2 engines will crash with the above, since they
> don't allow the response object to be a custom one (i.e. you'll get
> classcast exceptions).
> 
> I guess that at least the issue is understood now. From your comments it
> seems like we have no option but to change include-tags to use the
> wrapper-solution.
> 
> /Rickard
> 
> --
> Rickard Öberg
> Software Development Specialist
> xlurc - Xpedio Linköping Ubiquitous Research Center
> Author of "Mastering RMI"
> Email: rickard@xpedio.com
> 
> --
> To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
> For additional commands, e-mail: <ma...@jakarta.apache.org>

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by "Craig R. McClanahan" <cr...@apache.org>.

On Tue, 30 Oct 2001, Rickard Öberg wrote:

> Date: Tue, 30 Oct 2001 09:11:46 +0100
> From: Rickard Öberg <ri...@xpedio.com>
> Reply-To: Tomcat Developers List <to...@jakarta.apache.org>
> To: Tomcat Developers List <to...@jakarta.apache.org>
> Cc: xdev@allt.nu
> Subject: Re: PageContext include
>
> Craig R. McClanahan wrote:
>
> > You can use PageContext.include() just fine, if all you want to do is pass
> > the included text on to the response page.  This issue comes up only when
> > you want to simulate the behavior of:
> >
> >   <jsp:include page="/..." include="false"/>
> >
> > which, as you point out, is common in templating environments.
>
>
> Which is the only reasonable case (i.e. to not flush) if you are doing
> an include tag that is to be *generally used*. The problem, which was
> one of the big fixes in JSP 1.2 (or so we assumed) ...

For the record, the goal was to be able to use <jsp:include page="/..."
include="false"/> in a JSP page, which is not quite the same thing as
being able to use PageContext.include() in a custom tag that buffers.

Craig



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by Rickard Öberg <ri...@xpedio.com>.
Craig R. McClanahan wrote:

> You can use PageContext.include() just fine, if all you want to do is pass
> the included text on to the response page.  This issue comes up only when
> you want to simulate the behavior of:
> 
>   <jsp:include page="/..." include="false"/>
> 
> which, as you point out, is common in templating environments.


Which is the only reasonable case (i.e. to not flush) if you are doing 
an include tag that is to be *generally used*. The problem, which was 
one of the big fixes in JSP 1.2 (or so we assumed), is that you cannot 
use pageContext.include to implement an include-tag, if you want to be 
able to use that tag *within the scope of other tags*. I.e. if the 
include-tag below is implemented with pageContext.include the following 
snippet will always crash:
<x:iterate list="foo">
   <x:include page="bar.jsp">
</x:iterate>

The iterator tag will push a BodyContent onto the writer stack, and thus 
the include tag when doing pageContext.include("bar.jsp") will do a 
flush() on that BodyContent, which will result in an exception.

So, the result is: "you can't use pageContext.include to implement 
include-like-tags if you want to use that tag from within other tags in 
JSP pages". And IMnsHO that makes pageContext.include useless for any 
real include-tag, since the above limitation is just too severe.

The ability to do this was one of the major reasons our company switched 
from Tomcat 3.2 to Tomcat 4.0, and now that doesn't work....I guess you 
can see how extremely frustrating this is for us.

> The simplest thing to do would be to base your implementation on the code
> that Jasper uses for the above idiom.  In
> org.apache.jasper.runtime.JspRuntimeLibrary there is a public static
> include() method that the generated code for a JSP page calls, with some
> appropriate supporting methods and a response wrapper available as well.
> 
> The guts of the method are pretty straightforward:
> 
>   public static void include(HttpServletRequest request,
>                              HttpServletResponse response,
>                              String relativePath,
>                              JspWriter out,
>                              boolean flush)
>       throws IOException, ServletException {
> 
>       if (flush)
>           out.flush();
>       String resourcePath = getContextRelativePath(request, relativePath);
>       RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
>       rd.include(request,
>                  new ServletResponseWrapperInclude(response, out));
> 
>   }
> 
> It's not like you cannot create custom tags that do this kind of thing --
> it's just that you need to use a mechanism other than
> PageContext.include() to implement it.


Note that some servlet 2.2 engines will crash with the above, since they 
don't allow the response object to be a custom one (i.e. you'll get 
classcast exceptions).

I guess that at least the issue is understood now. From your comments it 
seems like we have no option but to change include-tags to use the 
wrapper-solution.

/Rickard

-- 
Rickard Öberg
Software Development Specialist
xlurc - Xpedio Linköping Ubiquitous Research Center
Author of "Mastering RMI"
Email: rickard@xpedio.com


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by "Craig R. McClanahan" <cr...@apache.org>.

On Mon, 29 Oct 2001, Rickard Öberg wrote:

> Date: Mon, 29 Oct 2001 19:33:03 +0100
> From: Rickard Öberg <ri...@xpedio.com>
> Reply-To: Tomcat Developers List <to...@jakarta.apache.org>
> To: Tomcat Developers List <to...@jakarta.apache.org>
> Subject: Re: PageContext include
>
> Craig R. McClanahan wrote:
>
> > Tomcat 4 is enforcing the JSP spec requirements that say calling
> > PageContext.include() causes a flush.  This is only an issue if you are
> > trying to buffer the output for further processing (for example, trying to
> > simulate what <jsp:incluce page="..." include="false"/> does).  This
> > behavior was fixed very late in the 4.0 development cycle, due to a spec
> > compliance test failing.
>
>
> I think the million dollar question, regardless of the above (which I
> don't fully understand), is: can we use pageContext.include to implement
> tags that include other pages? It seems like the answer is no (since
> BodyContent.flush() makes flushing illegal in tags).
>
> Right?

You can use PageContext.include() just fine, if all you want to do is pass
the included text on to the response page.  This issue comes up only when
you want to simulate the behavior of:

  <jsp:include page="/..." include="false"/>

which, as you point out, is common in templating environments.

>
> If so, and I believe this is what the fuzz is about, it makes writing
> tag libraries that implement include-like functionality much harder.
> Especially since it would have to rely on the response object
> replacement that I've outlined earlier, which is kind of "hacky".
>
> The key question is: what is the best snippet of code to include other
> pages/servlets in a JSP tag lib implementation?
>

The simplest thing to do would be to base your implementation on the code
that Jasper uses for the above idiom.  In
org.apache.jasper.runtime.JspRuntimeLibrary there is a public static
include() method that the generated code for a JSP page calls, with some
appropriate supporting methods and a response wrapper available as well.

The guts of the method are pretty straightforward:

  public static void include(HttpServletRequest request,
                             HttpServletResponse response,
                             String relativePath,
                             JspWriter out,
                             boolean flush)
      throws IOException, ServletException {

      if (flush)
          out.flush();
      String resourcePath = getContextRelativePath(request, relativePath);
      RequestDispatcher rd = request.getRequestDispatcher(resourcePath);
      rd.include(request,
                 new ServletResponseWrapperInclude(response, out));

  }

It's not like you cannot create custom tags that do this kind of thing --
it's just that you need to use a mechanism other than
PageContext.include() to implement it.


> > Incidentally, if Tomcat 3.x doesn't do a flush here (haven't tried it), it
> > would also not spec compliant, and any program depending on this behavior
> > is unlikely to run as desired in compliant containers.
> >
> > This is a specification compliance issue, and has been forwarded to the
> > spec lead for comment.  It is not appropriate to selectively enforce spec
> > requirements -- that is a very slippery slope and destroys any hope for
> > application portability.
>
> I thought this was a problem stemming from an erroneous Javadoc comment.
> If that is indeed the case, then keeping it as it was and making a
> bug-fix release of JSP (1.2.1 or something) would be preferred.
>

Well, the Javadoc comment was there in JSP 1.1 as well, so it's not like
this is new ... but regardless, it's a spec issue first and foremost.

> Especially since I doubt that many other JSP engines will implement
> this. They do not really have to worry about spec compliance (unless you
> want to wave the "J2EE certification flag"), but rather worry more about
> usability.
>

The test in question is part of CTS for J2EE 1.3, so anyone who *does*
pass the certification will have to implement it :-).

> /Rickard
>

Craig



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by Rickard Öberg <ri...@xpedio.com>.
Craig R. McClanahan wrote:

> Tomcat 4 is enforcing the JSP spec requirements that say calling
> PageContext.include() causes a flush.  This is only an issue if you are
> trying to buffer the output for further processing (for example, trying to
> simulate what <jsp:incluce page="..." include="false"/> does).  This
> behavior was fixed very late in the 4.0 development cycle, due to a spec
> compliance test failing.


I think the million dollar question, regardless of the above (which I 
don't fully understand), is: can we use pageContext.include to implement 
tags that include other pages? It seems like the answer is no (since 
BodyContent.flush() makes flushing illegal in tags).

Right?

If so, and I believe this is what the fuzz is about, it makes writing 
tag libraries that implement include-like functionality much harder. 
Especially since it would have to rely on the response object 
replacement that I've outlined earlier, which is kind of "hacky".

The key question is: what is the best snippet of code to include other 
pages/servlets in a JSP tag lib implementation?


> Incidentally, if Tomcat 3.x doesn't do a flush here (haven't tried it), it
> would also not spec compliant, and any program depending on this behavior
> is unlikely to run as desired in compliant containers.
> 
> This is a specification compliance issue, and has been forwarded to the
> spec lead for comment.  It is not appropriate to selectively enforce spec
> requirements -- that is a very slippery slope and destroys any hope for
> application portability.

I thought this was a problem stemming from an erroneous Javadoc comment. 
If that is indeed the case, then keeping it as it was and making a 
bug-fix release of JSP (1.2.1 or something) would be preferred.

Especially since I doubt that many other JSP engines will implement 
this. They do not really have to worry about spec compliance (unless you 
want to wave the "J2EE certification flag"), but rather worry more about 
usability.

/Rickard



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: PageContext include

Posted by "Craig R. McClanahan" <cr...@apache.org>.

On Mon, 29 Oct 2001, Micael Padraig Og mac Grene wrote:

> Date: Mon, 29 Oct 2001 09:26:14 -0800
> From: Micael Padraig Og mac Grene <ca...@harbornet.com>
> Reply-To: Tomcat Developers List <to...@jakarta.apache.org>
> To: Tom Cat <to...@jakarta.apache.org>
> Subject: PageContext include
>
> I have been informed that Tomcat 4.0 is not supporting PageContext
> includes.  Is that correct?

No, that is not correct.

> I am not downloading it and changing my
> development plans if that is correct and is not merely a temporary
> aberration.  Could someone give me the true scoop and the thinking?
>

Tomcat 4 is enforcing the JSP spec requirements that say calling
PageContext.include() causes a flush.  This is only an issue if you are
trying to buffer the output for further processing (for example, trying to
simulate what <jsp:incluce page="..." include="false"/> does).  This
behavior was fixed very late in the 4.0 development cycle, due to a spec
compliance test failing.

Incidentally, if Tomcat 3.x doesn't do a flush here (haven't tried it), it
would also not spec compliant, and any program depending on this behavior
is unlikely to run as desired in compliant containers.

This is a specification compliance issue, and has been forwarded to the
spec lead for comment.  It is not appropriate to selectively enforce spec
requirements -- that is a very slippery slope and destroys any hope for
application portability.

Craig



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>