CQ Resource Inclusion from a Servlet

CQ Resource Inclusion from a Servlet

Including resources from a JSP is super simple due to the many facilities within Sling and CQ.  But what if you wanted to include a CQ resource from a custom Sling Servlet?  

All of this nice JSP based inclusion is built on top of the JavaEE API.  Take a look at the class: javax.servlet.jsp.PageContext and two of it's methods: public abstract void include(String relativeUrlPath) and public JspWriter pushBody(Writer writer). These two method will form the basis for all of this inclusion business.  With a little bit of extra code, we can capture the included resource before it gets written to the current Servlets OutputStream.

Here is the benefit of doing this within a CQ/Sling context: you can request other CQ / Sling resources (Servlet, JSP, Component, etc) without re-authenticating when using an API like Apache HTTP Commons.  If you use the already existing SlingHttpServletRequest and SlingHttpServletResponse Objects, the Request already contains the authentication info.  Dispatched inclusion is likely faster that the HTTP Commons approach, and is more "natural".

To do a quick and dirty POC, use CRXDE Lite.  I do not recommend using this for day to day development, but it does have it's purposes.  This example is one case where CRXDE Lite is the best tool for the job.  But, I digress...

Use the Geometrixx Homepage template, specifically the content JSP: /apps/geometrixx/components/homepage/content.jsp.  Plop the following chunk of JSP Scriptlet into your JSP.  Let me digress one more time.  Sorry...  I do not recommend this approach for development either.  I exclusively use JSTL / EL for my CQ JSP's unless I cannot accomplish what I need without a Scriptlet.  Again, this is a POC, and we are not really concerned with best practise.  I would likely put this into either a stateless OSGi service, or a plain old Java utility class.  Point is:  I would put it into a Java class somewhere.   

One more thing to note:  this example was meant to be used within a Servlet.  The original "requirement" was that a Servlet being developed needed to emit a secondary request to a CQ resource on the same host.  I developed this as a JSP because I did not want to have to create an OSGi bundle using CRXDE Lite.  Keep in mind that a JSP is really just a fancy Servlet.  I could have created a real Servlet using a CRXDE Lite based "POC" OSGi bundle I created for this very purpose.  But using good a old Geometrixx JSP was quicker.  OK, back to our story...

Here is the code, put it after the last closing div in .../homepage/content.jsp :

<h1>Below Here is our POC Code</h1><%
if (true) {//** Line 01
   out.flush();//** Line 02
   try {//** Line 03
      java.io.StringWriter sw = new java.io.StringWriter(1024);//** Line 04
      javax.servlet.jsp.JspWriter jspwriter = pageContext.pushBody(sw);//** Line 05
      pageContext.include("/content/geometrixx/en.timezones.json");//** Line 06
      jspwriter.close();//** Line 07
      pageContext.setAttribute("theresult", sw.toString().trim());//** Line 08
   } catch (Exception e) {//** Line 09
      out.print("Dispatcher ERROR");//** Line 10
      log.error("Error with include", e);//** Line 11
   }//** Line 12
   pageContext.popBody();//** Line 13
}%>${ theresult }//** Line 14

The "if" switch is there for me to easily turn this "on" or "off" to help with some troubleshooting.

Line 02 is needed because the response content type on the "current" OutputStream is not set immediately, even though we are outputting markup.  What was happening was the response type was getting set to "application/json" when I was writing out the included resource as a String.  Yes, strange.  It must have forced a flush, and thus setting the response content type.  Invoking Line 02 forced the content type to be set to "html" so that it doesn't get set incorrectly when writing out our captured resource.  This is just a detail of the example I chose, and does not necessarily have much to do with including other resources.  Just be aware of it.

Line 05 causes the current OutputStream to be "saved" and create a new one for writing markup to.  This is how we capture our included resource.  We use a StringWriter to do the capturing for us.  As you can see, that is the real purpose of the method "pushBody" on Line 05.

Line 06 does the inclusion, writing all markup out to the newly created JspWriter created from Line 04 - 05.  Line 07 is simply cleanup, making sure the OutputStream is flushed so we get ALL of the captured resource.

Line 08 we are simply fetching the captured resource as a String and stuffing it into a Page scoped variable we can use later with an EL statement.

Line 13 is clean up too.  We need to make sure we restore the pageContext to its proper OutputStream state, or we could cause strange errors elsewhere.

Thats it.

Keep in mind there is a method on the HttpServletResuest Object to fetch a RequestDispatcher.  Using that approach won't work with what I have shown you above.  Calling pageContext.pushBody has NO effect on the existing SlingHttpServletResponse Object.  It will work if you write a custom SlingHttpServletResponseWrapper that internally defines a JspWriter wrapped with a StrignWriter to capture the included resource.  But this was extra code I did not want to have to write.

Enjoy.

Share this post

0 Comments

comments powered by Disqus