Grails JIRA

  • Log In Access more options
    • Online Help
    • GreenHopper Help
    • Agile Answers
    • Keyboard Shortcuts
    • About JIRA
    • JIRA Credits
    • What’s New
  • Dashboards Access more options (Alt+d)
  • Projects Access more options (Alt+p)
  • Issues Access more options (Alt+i)
  • Agile
Grails
  • Grails
  • GRAILS-3406 Top level task: Controller Improvements
  • GRAILS-3293

RequestDispatcher.include() doesn't work

  • Log In
  • Views
    • XML
    • Word
    • Printable

Details

  • Type: Sub-task Sub-task
  • Status: Closed Closed
  • Priority: Minor Minor
  • Resolution: Fixed
  • Affects Version/s: 0.6, 1.0.3
  • Fix Version/s: 1.1-beta3
  • Component/s: None
  • Labels:
    None

Description

Trying to use RequestDispatcher to include another controller & action fails, despite the efforts of GRAILS-304. I troubleshooted why this is the case and made things work in my application, with a few changes.

1. The UrlMappings servlet filter needs to execute for this request. There are two reasons why it won't and they need to get resolved:
a. In the web.xml, the filter-mapping element needs a <dispatcher>REQUEST</dispatcher> and a <dispatcher>INCLUDE</dispatcher> Due to the following issue I was forced to modify grails to make this change:
http://www.nabble.com/Deferred-output-of-web.xml-past-doWithWebDescriptor()-makes-some-mutations-impossible.-td18719230.html
b. The UrlMappingsFilter is a Spring OncePerRequestFilter which blocks it from working on the include. So at the point I needed to use the request dispatcher, I removed 'urlMapping.FILTERED' from the request attributes, and add it after the include.

2. The url to get the request dispatcher must start with a '/' and thus be web-app-context relative because for whatever reason if there is no leading slash, the current request looks like "/grails/controller/action" whereas "/grails" is NOT the name of the application or a controller; it appears to be an internal servlet name, however. UrlMappingsFilter gets confused at this. My workaround was simply to make sure my URLs are webapp relative.

3. The requestDispatcher deals directly with the response, even if there might be buffered text in the output stream already ("out"). So I was forced to invoke flush() beforehand.

In the comments on GRAILS-304 I asked it to be re-openned. Since it wasn't, you're looking at another bug report instead of a comment on that one

Issue Links

is related to

Sub-task - The sub-task of the issue GRAILS-979 Re: Servlet includes not handled properly by Grails

  • Major - Major loss of function.
  • Closed - The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.

Activity

Ascending order - Click to sort in descending order
  • All
  • Comments
  • Work Log
  • History
  • Activity
  • Git Commits
Hide
Permalink
Graeme Rocher added a comment - 30/Jul/08 9:16 AM

Patches welcome

Show
Graeme Rocher added a comment - 30/Jul/08 9:16 AM Patches welcome
Hide
Permalink
David Smiley added a comment - 01/Aug/08 3:33 PM

I spent an inordinate amount of time digging deeper into this and I want to add some information. My "description" in JIRA isn't sufficient to get things working. I have a snippet of code in a taglib that looks like this:

      //url looks like '/myyappname/person/dataTable'
      url = url.replaceFirst("^/[^/]+",'')
      //url now looks like: (for example)   '/person/dataTable'

      def dispatcher = request.getRequestDispatcher(url)
      def attrNames = ['urlMapping.FILTERED'] //TODO temp hack until grails fixes
      def oldAttrVals = attrNames.collect { request.getAttribute(it) }
      def webRequest = WebUtils.retrieveGrailsWebRequest()
      def origOut = webRequest.getOut()
      try {
         attrNames.each { request.removeAttribute(it) }
         def newResponse = new RDInclusionWrappedResponse(response,out)
         log.debug("BEFORE INCLUDE to $url")
         dispatcher.include(request,newResponse)
      } catch (Exception e) {
         includeError(out,url,e)
      } finally {
         log.debug("AFTER INCLUDE of $url")
         attrNames.eachWithIndex {name,i -> request.setAttribute(name,oldAttrVals[i])}
         webRequest.setOut(origOut)
      }

And the RDInclusionWrapperResponse looks like this:

public class RDInclusionWrappedResponse extends HttpServletResponseWrapper {

   private PrintWriter out;

   public RDInclusionWrappedResponse(HttpServletResponse httpServletResponse,
                                     PrintWriter out) {
      super(httpServletResponse);
      this.out = new PrintWriter(out) {
         public void close() {
            flush();
            //no close
         }
      };
   }

   public PrintWriter getWriter() throws IOException {
      return out;
   }

   public ServletOutputStream getOutputStream() throws IOException { //this is actually needed, though you won't see the exception
      throw new IllegalStateException("Expected to get the writer");
   }
}

And finally to activate UrlMappingsFilter for includes, I basically copied the code at the end of this:
http://amorproximi.blogspot.com/2008/07/more-on-grails-declarative-error.html
into an Events.groovy in the scripts/ but change the code to apply to the "urlMapping" named filter.

However, doing all this isn't quite perfect but it was good enough for my purposes. I also think a more thorough solution would also activate GrailsWebRequestFilter for includes, and there is code in that filter and also in UrlMappingsFilter that manipulate thread-locals and/or attributes in the request that are in-effect like a global (and globals are of course evil). Although it wasn't necessary to get my app working, I also manipulated those to grails classes to save off the old globals and then restore them when the filter is done.

Show
David Smiley added a comment - 01/Aug/08 3:33 PM I spent an inordinate amount of time digging deeper into this and I want to add some information. My "description" in JIRA isn't sufficient to get things working. I have a snippet of code in a taglib that looks like this: //url looks like '/myyappname/person/dataTable' url = url.replaceFirst( "^/[^/]+" ,'') //url now looks like: ( for example) '/person/dataTable' def dispatcher = request.getRequestDispatcher(url) def attrNames = ['urlMapping.FILTERED'] //TODO temp hack until grails fixes def oldAttrVals = attrNames.collect { request.getAttribute(it) } def webRequest = WebUtils.retrieveGrailsWebRequest() def origOut = webRequest.getOut() try { attrNames.each { request.removeAttribute(it) } def newResponse = new RDInclusionWrappedResponse(response,out) log.debug( "BEFORE INCLUDE to $url" ) dispatcher.include(request,newResponse) } catch (Exception e) { includeError(out,url,e) } finally { log.debug( "AFTER INCLUDE of $url" ) attrNames.eachWithIndex {name,i -> request.setAttribute(name,oldAttrVals[i])} webRequest.setOut(origOut) } And the RDInclusionWrapperResponse looks like this: public class RDInclusionWrappedResponse extends HttpServletResponseWrapper { private PrintWriter out; public RDInclusionWrappedResponse(HttpServletResponse httpServletResponse, PrintWriter out) { super (httpServletResponse); this .out = new PrintWriter(out) { public void close() { flush(); //no close } }; } public PrintWriter getWriter() throws IOException { return out; } public ServletOutputStream getOutputStream() throws IOException { // this is actually needed, though you won't see the exception throw new IllegalStateException( "Expected to get the writer" ); } } And finally to activate UrlMappingsFilter for includes, I basically copied the code at the end of this: http://amorproximi.blogspot.com/2008/07/more-on-grails-declarative-error.html into an Events.groovy in the scripts/ but change the code to apply to the "urlMapping" named filter. However, doing all this isn't quite perfect but it was good enough for my purposes. I also think a more thorough solution would also activate GrailsWebRequestFilter for includes, and there is code in that filter and also in UrlMappingsFilter that manipulate thread-locals and/or attributes in the request that are in-effect like a global (and globals are of course evil). Although it wasn't necessary to get my app working, I also manipulated those to grails classes to save off the old globals and then restore them when the filter is done.
Hide
Permalink
Graeme Rocher added a comment - 19/Sep/08 4:50 AM

Reduced priority of non critical issues which have current workarounds

Show
Graeme Rocher added a comment - 19/Sep/08 4:50 AM Reduced priority of non critical issues which have current workarounds
Hide
Permalink
Graeme Rocher added a comment - 27/Jan/09 11:07 AM

You can now use the new include tag to do this from a view:

<g:include controller="foo" action="test"></g:include>

Or from a controller:

def content = include(controller:"foo", action:"bar")
Show
Graeme Rocher added a comment - 27/Jan/09 11:07 AM You can now use the new include tag to do this from a view: <g:include controller= "foo" action= "test" ></g:include> Or from a controller: def content = include(controller: "foo" , action: "bar" )

People

  • Assignee:
    Graeme Rocher
    Reporter:
    David Smiley
Vote (0)
Watch (1)

Dates

  • Created:
    29/Jul/08 5:39 PM
    Updated:
    18/May/11 3:18 PM
    Resolved:
    27/Jan/09 11:07 AM

Agile

  • View on Board
  • Atlassian JIRA (v5.2.1#813-sha1:277a546)
  • Report a problem
  • Powered by a free Atlassian JIRA open source license for Grails project. Try JIRA - bug tracking software for your team.