SlingFilter for injecting additional markup into response: IllegalStateException | Community
Skip to main content
Level 4
October 16, 2015
Solved

SlingFilter for injecting additional markup into response: IllegalStateException

  • October 16, 2015
  • 6 replies
  • 1968 views

Hi,

I'm trying to write a custom Sling Filter for injecting additional HTML markup into the response.


Original response:

<html> <head> ... </head> <body> ... </body> </html>

Response after custom filter:

<html> <head> ... <custom-tag> ... </custom-tag> </head> <body> ... </body> </html>

This is the filter I wrote using Sling Filters:

import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.sling.SlingFilter; import org.apache.felix.scr.annotations.sling.SlingFilterScope; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.wcm.api.Page; @SlingFilter(generateComponent = false, generateService = true, order = -50001, scope = SlingFilterScope.REQUEST) @Component(immediate = true, metatype = false) public class TealiumFilter implements Filter { private Logger logger = LoggerFactory.getLogger(TealiumFilter.class); public void init(FilterConfig filterConfig) throws ServletException { } @Activate protected void activate(final Map<String, Object> props) { logger.warn("***** TEALIUM FILTER *****"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; Resource resource = slingRequest.getResource(); if(resource != null && !resource.getPath().contains("qfusion/")) { chain.doFilter(request, response); return; } Page page = resource.adaptTo(Page.class); CharResponseWrapper responseWrapper = new CharResponseWrapper((HttpServletResponse) response); chain.doFilter(request, responseWrapper); // Get writer must be called after chain.doFilter // otherwise request for non html resources (images, css, js, etc.) // results in an IllegalStateException PrintWriter out = response.getWriter(); String servletResponse = new String(responseWrapper.toString()); if(servletResponse != null && servletResponse.length() > 0 && // Servlet response must be non empty responseWrapper.getContentType().contains("text/html") && // Servlet response must be html page != null && // The resource the request points to must resolve to a CQ Page servletResponse.contains("<head>")) { // Servlet response must contain head tag logger.warn("**** APPLYING TEALIUM FILTER ****"); CharArrayWriter caw = new CharArrayWriter(); caw.write(servletResponse.substring(0, servletResponse.indexOf("</head>")-1)); //logger.warn("before manipulation: " + caw.toString()); caw.write("<meta name=\"og:title\" content=\"og:"+ page.getTitle() +"\" />"); caw.write(servletResponse.substring(servletResponse.indexOf("</head>")-1, servletResponse.length())); //logger.warn("after manipulation: " + caw.toString()); response.setContentLength(caw.toString().length()); out.write(caw.toString()); } else { out.write(servletResponse); } out.close(); } public void destroy() { } }

The problem here is the line of code for getting the PrintWriter from the response. If I move this line of code *before* the call to chain.doFilter then I have an IllegalStateException for all the requests that do not point to an HTML resource (css, js, images, ...).

chain.doFilter(request, responseWrapper); // Get writer must be called after chain.doFilter // otherwise request for non html resources (images, css, js, etc.) // results in an IllegalStateException PrintWriter out = response.getWriter();

Why I have this exception only for non-html resources? Do you have any real example of implementation of Sling Filter that modifies the HTML response sent to the client?

Thank you in advance!

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by JustinEd3

Hi,

This is defined in the servlet specification. If getWriter() is called after getOutputStream(), it throws an IllegalStateException. See http://docs.oracle.com/javaee/5/api/javax/servlet/ServletResponse.html#getWriter()

Regards,

Justin

6 replies

JustinEd3Adobe EmployeeAccepted solution
Adobe Employee
October 16, 2015

Hi,

This is defined in the servlet specification. If getWriter() is called after getOutputStream(), it throws an IllegalStateException. See http://docs.oracle.com/javaee/5/api/javax/servlet/ServletResponse.html#getWriter()

Regards,

Justin

boser87Author
Level 4
October 16, 2015

Hi Justin,

thank you. The reason of the exception was clear to me but the solution for solving was not! :-)

However, in case it can help someone I found out how to solve my issue: I dont call response.getWriter() anymore for request that has content-type != text/html. For such requests, getWriter() has already been called by another servlet in the chain. This caused the IllegalStateException.

If anyone can confirm this assumption it would be really appreciated!

I attach the final code below for reference:

import java.io.CharArrayWriter; import java.io.IOException; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.sling.SlingFilter; import org.apache.felix.scr.annotations.sling.SlingFilterScope; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.wcm.api.Page; @SlingFilter(generateComponent = false, generateService = true, order = -50001, scope = SlingFilterScope.REQUEST) @Component(immediate = true, metatype = false) public class TealiumFilter implements Filter { private Logger logger = LoggerFactory.getLogger(TealiumFilter.class); public void init(FilterConfig filterConfig) throws ServletException { } @Activate protected void activate(final Map<String, Object> props) { logger.warn("***** TEALIUM FILTER *****"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; Resource resource = slingRequest.getResource(); if((resource != null && !resource.getPath().contains("mysite/"))) { chain.doFilter(request, response); return; } Page page = resource.adaptTo(Page.class); CharResponseWrapper responseWrapper = new CharResponseWrapper((HttpServletResponse) response); chain.doFilter(request, responseWrapper); String servletResponse = new String(responseWrapper.toString()); if(servletResponse != null && servletResponse.length() > 0 && // Servlet response must be non empty responseWrapper.getContentType().contains("text/html") && // Servlet response must be html page != null && // The resource the request points to must resolve to a CQ Page servletResponse.contains("</head>")) { // Servlet response must contain head tag logger.warn("**** APPLYING TEALIUM FILTER ****"); CharArrayWriter caw = new CharArrayWriter(); caw.write(servletResponse.substring(0, servletResponse.indexOf("</head>")-1)); //logger.warn("before manipulation: " + caw.toString()); caw.write("<meta name=\"og:title\" content=\"og:"+ page.getTitle() +"\" />"); caw.write(servletResponse.substring(servletResponse.indexOf("</head>")-1, servletResponse.length())); //logger.warn("after manipulation: " + caw.toString()); response.setContentLength(caw.toString().length()); response.getWriter().write(caw.toString()); } } public void destroy() { } }

Thank you!

boser87Author
Level 4
October 16, 2015

Thank you Sham. That could be another good example of modifying the response sent to the client with a Sling Filter.

However this still does not anwser to my doubt. Why do I get an IllegalStateException if a call getWriter after chain.doFilter call only for non-html resources?

Thank you!

Adobe Employee
October 16, 2015

Hi,

That looks correct to me. Although honestly a better approach for this would be to use the Sling Rewriter.

Justin

August 3, 2018

Old Thread, but for some reason Sling Documentation mentions that SlingFilters can just be used to manipulate request entities, which kind of is misleading.