In this tutorial, we will use Spring 3 to render a JSP and capture the output to a String.  Why might you want to do this?  Specifically this need comes up in two instances:

  1. You want to use parameterized JSP pages to send emails to end-users.  You may want to do this in a separate Thread that doesn’t have access to the original user request. Yes, you could use Freemarker or Velocity to build a dynamic email system.  But you have an investment in JSP technology, and would like to use it to produce emails on the fly.
  2. You want to conditionally include a JSP, but you want to render it in the background for speed reasons.  Yes, you could use AJAX for this, but for some reason you can’t or don’t want to.

The goals of this tutorial are:

  • To render an arbitrary JSP, and return the rendered page as a String.
  • To render the JSP without access to any end-user HttpServletRequest, as it might not be available when we need it.
  • To be able to render any normal JSP.  This includes: using normal taglibs in the JSP such as JSTL and Spring tags, being able to render a full HTML page or just a fragment JSP, and being able to include other JSP pages inside our JSP if we need to (that is, use jsp:include), and the use of message bundles (that is, spring:message).
  • The JSP that we render should be Locale-aware, for both messages and numeric formats.

Full source code for this tutorial is available on GitHub.

Continue to the tutorial….

A few notes:

  • A full discussion on how to set up a web application on Eclipse (or other development tool) is beyond the scope of this post.  If you need help with this, see my post “Hello World App”.  I’m assuming you know the basics of web applications and how to create, start, and stop an app in your favorite tool.  You should also have a basic understanding of Servlets and JSP.  I’m also assuming that you are somewhat familiar with Maven and what it does.
  • My style is to show full code and review it.  This is not everyone’s favorite style, but I try to be as thorough as possible.
  • I use Maven for dependency management, so I don’t normally pay much attention to the full set of dependencies needed (because Maven handles this).  I have added a list of all dependencies (as reported by Eclipse) at the end of this post.  If you are using Maven, you don’t need this list, but people often ask for it.  I assume these dependencies are correct, but your mileage my vary.
  • I’m using Tomcat 7.0.8 for my application server. Although this should work with any app server, you mileage may vary. I also use the Sysdeo plugin for Eclipse so that I can hot swap easily, but this is not required. You can see how to set up the plugin at my post “Setting up the Sysdeo Plugin”.
  • To save wear and tear on my fingers, I will often abbreviate the Java package name “com.technologicaloddity” to “c.t.”.

Enough preamble, let’s look at some code!  Let’s start off with the easy infrastructure files.

/src/main/webapp/WEB-INF/web.xml

<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Capture JSP</display-name>

    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>capturejsp.root</param-value>
    </context-param>

    <listener><listener-class>org.springframework.web.util.Log4jConfigListener</listener-class></listener>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>    

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:/spring/dispatch-servlet.xml
        </param-value>
    </context-param>
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/log4j.properties</param-value>
    </context-param>

    <servlet>
        <servlet-name>dispatch</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath:/spring/dispatch-servlet.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatch</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>        

    <servlet-mapping>
        <servlet-name>dispatch</servlet-name>
        <url-pattern>/index.html</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>        

</web-app>

This is a standard web.xml file.  We are loading up the Spring context from the classpath (/spring/dispatch-servlet.xml), and mapping it to handle URLs that end in .html.  In this tutorial, I’m using Servlets version 2.4 (as you can see from the webapp tag), but I have also used this method with Servlets 3.0 on Tomcat 7.

/src/main/webapp/WEB-INF/log4j.properties

# For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
log4j.rootLogger=ALL, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyMMdd HH:mm:ss}|%-5p|%40.40c|%X{user}|%m%n

log4j.logger.org.apache=INFO
log4j.logger.org.springframework=INFO
log4j.logger.com.technologicaloddity=INFO
log4j.logger.org.directwebremoting=INFO

Again, this is a normal Log4J configuration file.  Nothing fancy.

This is the POM file I use with Maven.

/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.technologicaloddity</groupId>
  <artifactId>capturejsp</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>Capture JSP</name>
  <url>http://technologicaloddity.com</url>
  <dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.14</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.directwebremoting</groupId>
        <artifactId>dwr</artifactId>
        <version>2.0.3</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>capturejsp</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Let’s go over the POM file a bit to see what libraries we are using.

  • JSTL – The standard Java taglibs for JSPs.
  • Log4J – No surprise here, we will use it for logging.
  • Spring-WebMVC – We are using Spring’s Web Application framework.  Maven will fill in the rest of the dependencies that WebMVC needs (like spring-core, spring-context, etc.)
  • Spring-Test – This is normally used only in testing and isn’t included by WebMVC, but we need access to some “fake” requests and responses (MockHttpServletRequest/Response).  We’ll use the Mock objects to help capture the output of the JSP.
  • DWR – Also known as “easy AJAX”, although we won’t be using any of the AJAX functions.  We need DWR for one class: SwallowingHttpServletResponse.  This class captures the output of a response and places it into String.

We also need to configure Spring with the file we told it about in web.xml, lines 24 and 38.

/src/main/resources/spring/dispatch-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans    xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:util="http://www.springframework.org/schema/util"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        ">

    <context:annotation-config />

    <context:component-scan base-package="com.technologicaloddity.capturejsp"/>        

</beans>

This is a minimal Spring configuration file.  We’ll use annotations to set up most things, as we will see later in the Java code.  Basically we activate annotation configuration (line 11) and then tell Spring to scan our application package for annotated classes (line 13).

We also have a series of message files which we will use to localize the captured JSP page.  Each file defines one piece of text called “welcome.message”, each in a different language.  The default is English:

/src/main/resources/messages/messages.properties

welcome.message=Welcome!

For comparison, here is the Spanish one:

/src/main/resources/messages/messages_es.properties

welcome.message=¡Bienvenidos!

There are also files for French (fr) and German (de).

Pages: 1 2 3

20 Responses to “Render and capture the output of a JSP as a String”

  1. Hi, i like the tutorial. I almost gave up and tried an other way.
    With this i can use the same jsp for the “real” page by including it and i can return the page-content encoded as a part of an json object called by ajax.
    I would like to use it and create a kind of library to have a tested maven artifact. That artifact i would like to use as a dependency for (an) other project(s).
    The only thing is, in the git-reposetory is no License included. Would you add a apache, mit or lgpl license?

  2. Apache license added. Have fun. Shoot me a copy when you get your library working.

  3. Thanks for sharing, using mockresponse is a neat idea.

  4. Bob, first of all, thanks for writing this tutorial. Bonus points for being so thorough. I came here looking to do the exact same thing as your first poster, David. Anyway, I’ve encountered one big problem. The render(…) method kicks off the servlet container’s own include(…) method which ultimately causes a MalformedURLException. Here’s a snippet of the stack trace:

    ——————————————————-
    java.net.MalformedURLException: Path does not start with a “/” character
    com.zeroturnaround.javarebel.vS.a(JRebel:50)
    com.zeroturnaround.javarebel.pY.getResource(JRebel:108)
    org.eclipse.jetty.webapp.WebAppContext$Context.getResource(WebAppContext.java)
    org.apache.jasper.servlet.JspServlet._serviceJspFile(JspServlet.java:447)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:380)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:538)
    org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:478)
    org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
    org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:517)
    org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:225)
    org.eclipse.jetty.server.handler.ContextHandler.__doHandle(ContextHandler.java:937)
    org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java)
    org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:406)
    org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
    org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:871)
    org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
    org.eclipse.jetty.server.Dispatcher.include(Dispatcher.java:195)
    … [the rest omitted] …
    ——————————————————-

    (FYI: JRebel isn’t at fault; I get the same whether it’s enabled or not)

    I’ve debugged my custom ViewResolver’s getUrlForView(…) method, and it always returns a path starting with a “/” character (i.e.: starts with “/WEB-INF/”). So I have no idea how to go forward from here. Any idea what the problem might be?

    Michael

    • Hmm.. that’s a tough one. My first guess would be that the context in the web.xml file doesn’t start (or possibly end) with a “/”, so you may want to look there.

      I also seem to remember that in some early versions of Tomcat that there was a bug getting to internal resources unless they originated within the container itself (as a security measure). Not sure about Jetty though, as I don’t normally use it.

      Check out the web.xml file’s context setting and see if that works. In the meantime, I’ll try to set up a Jetty instance (I don’t have JRebel, but you said it happened even without, so that’s obviously not it).

      Are you using Eclipse’s WTP there? Or just building a war and running it outside?

  5. Hello bob, i’ve use your SwallowingJspRenderer in Tomcat 7.0 and it’s work great. But, when i try to using JBOSS EAP 6.0 or JBOSS AS 7.x.x as a web server, i’ve got error below :

    javax.servlet.ServletException: Original SevletRequest or wrapped original ServletRequest not passed to RequestDispatcher in violation of SRV.8.2 and SRV.14.2.5.1

    What should i do to solve this problem? Because i’ll use JBOSS EAP 6.0 on production’s environment. Thanks 🙂

  6. Hi Apri,

    I haven’t used JBoss in a while, so I haven’t seen this error. One of the JBoss forums suggests this can fixed by turning off strict server compliance with “-Dorg.apache.catalina.STRICT_SERVLET_COMPLIANCE=false” in the startup script though.

    (Yeah, I know, looks weird with org.apache.catalina in it)

    Bob

    • Thank you for your response bob, i’ve add -Dorg.apache.catalina.STRICT_SERVLET_COMPLIANCE=false in standalone.conf but i’ve got another error :

      java.lang.ClassCastException: com.bankbjb.itcore.cashportal.common.MockIncludedHttpServletRequest cannot be cast to javax.servlet.ServletRequestWrapper

      Any idea to solve this error? thanks again bob 🙂

      • @Apri,

        I’m not sure why it would be casting MockIncludedHttpServletRequest (MIHSR for short) to ServletRequestWrapper. MIHSR doesn’t implement wrapper, but is (ultimately) derived directly from SerlvetRequest.

        In theory, you could modify MIHSR to implement ServletWrapper as well, though. Can you tell where this is happening? Is there more to the stack trace?

        Bob

        • helo rob,
          sorry for late response, i’ve got stack trace from jboss log :

          09:56:33,696 INFO [stdout] (http-/127.0.0.1:8089-5) SwallowingJspRenderer: com.bankbjb.itcore.cashportal.common.MockIncludedHttpServletRequest cannot be cast to javax.servlet.ServletRequestWrapper
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) java.lang.ClassCastException: com.bankbjb.itcore.cashportal.common.MockIncludedHttpServletRequest cannot be cast to javax.servlet.ServletRequestWrapper
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterFactory.createFilterChain(ApplicationFilterFactory.java:164)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:827)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:720)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:657)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.common.SwallowingJspRenderer.render(SwallowingJspRenderer.java:91)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.renderView(CredentialControler.java:341)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.showView(CredentialControler.java:296)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.showView(CredentialControler.java:290)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.add(CredentialControler.java:133)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at java.lang.reflect.Method.invoke(Method.java:601)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:212)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:679)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:931)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at java.lang.Thread.run(Thread.java:722)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) java.io.IOException: java.lang.ClassCastException: com.bankbjb.itcore.cashportal.common.MockIncludedHttpServletRequest cannot be cast to javax.servlet.ServletRequestWrapper
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.common.SwallowingJspRenderer.render(SwallowingJspRenderer.java:98)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.renderView(CredentialControler.java:341)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.showView(CredentialControler.java:296)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.showView(CredentialControler.java:290)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.controller.CredentialControler.add(CredentialControler.java:133)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at java.lang.reflect.Method.invoke(Method.java:601)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:212)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
          09:56:33,696 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:679)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:931)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at java.lang.Thread.run(Thread.java:722)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) Caused by: java.lang.ClassCastException: com.bankbjb.itcore.cashportal.common.MockIncludedHttpServletRequest cannot be cast to javax.servlet.ServletRequestWrapper
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationFilterFactory.createFilterChain(ApplicationFilterFactory.java:164)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:827)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:720)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:657)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) at com.bankbjb.itcore.cashportal.common.SwallowingJspRenderer.render(SwallowingJspRenderer.java:91)
          09:56:33,711 ERROR [stderr] (http-/127.0.0.1:8089-5) … 37 more

          thanks again rob 🙂

          • @Apri,

            I’ve been looking around, and to be honest I am stumped. RequestDispatcher.include (which is where the exception starts) should accept any ServletRequest, without it being a wrapper. I’m not sure why JBoss would require such. Maybe somebody on here that uses JBoss can answer this?

            Bob

  7. Taimo Kolsar says:

    I had the following problem:
    In a Spring MVC app I had some SVG-s generated by JSP-s. Now I was asked to add the SVG-s to a PDF, which was generated in a controller.

    So I needed exactly the solution. But I tried a simpler one, and it seems to work. Here is my solution:

    1. I implemented javax.servlet.http.HttpServletResponse wrapping PrintWriter, and created content only for getWriter() method:

    public class HttpServletResponseOutput implements HttpServletResponse {

    private PrintWriter printWriter;

    public HttpServletResponseOutput(StringWriter stringWriter){
    this.printWriter = new PrintWriter(stringWriter);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
    return this.printWriter;
    }

    }

    2. I autowired another controller to the main controller:

    @Controller(value = “anotherController”)
    public class AnotherController {…

    @Controller
    public class MainController {
    @Autowired
    private VastController vastController;

    3. Inside the controller method:

    RequestDispatcher dispatcher = request.getRequestDispatcher(“/WEB-INF/views/svg/graph.jsp”); // Absolute path here. It’s not very nice, but it works.
    StringWriter stringWriter = new StringWriter();
    HttpServletResponse intermediateResponse = new HttpServletResponseOutput(stringWriter);
    anotherController.getGraph(model, request, intermediateResponse);
    try{
    dispatcher.include(request, intermediateResponse);
    } catch(Exception e){
    log.error(e.getMessage());
    }
    String graph = stringWriter.toString();
    // This String contains the SVG now

  8. Taimo Kolsar says:

    Sorry for a typo,
    In the 2. point it should be:
    @Autowired
    private AnotherController anotherController;

  9. Hi Bob,

    My project wants the static html rendered out of jsp. Will this source code helps in capturing HTML.

    Thanks in advance.
    Sudha.

    • It should. The “string” that comes out of the JSP renderer is simply the HTML that the JSP would have produced. You could leave out the model stuff if you don’t need it.

  10. Thanks for the tutorial, but messageSource doesn’t work for me (configured it as bean in my java config).

    It works when i use the jsps like usual, but it does not when i want to render jsps to a String.

    Any ideas?

  11. Hi Bob,

    I deployed your code and when I request http://localhost:8080/capturejsp/
    Browser only shows content of index.jsp. The text below line “The capture JSP page follows:” is empty.

    I checked Console and saw this warning:
    |WARN |oting.util.SwallowingHttpServletResponse||Ignoring call to sendError(405, JSPs only permit GET POST or HEAD)

    I think that the mock request object does not have any HTTP method. Then, I change file MockIncludedHttpServletRequest.java as follows:

    public class MockIncludedHttpServletRequest extends MockHttpServletRequest {

    public MockIncludedHttpServletRequest() {
    super();
    this.setMethod(“GET”);
    }

    Now, JSP files are renderred.

  12. Steven Wright says:

    Hi, thanks for this tutorial, it works like a charm. I have one issue I could use your help with please. I am using this a secondary way of rendering content in a Spring App. Basically in some cases we use the regular Spring renderer and in others we have to use this.

    What I’d like to know is if its possible that the bean annotation I use for formatting can be applied to this as well?

    I have a custom formatter and annotation for phone number. The formatter is added to Springs registry like this:

    @Override
    public void addFormatters(FormatterRegistry registry)
    {
    registry.addFormatterForFieldAnnotation(new PhoneNumberFormatAnnotationFormatterFactory());
    registry.addFormatterForFieldAnnotation(new BinaryIntFormatterAnnotationFormatterFactory());
    }

    How do I accomplish this in the context of the swallowing renderer?

    Thanks for any advice you can offer.

    • You know, I’ve looked at this several times over the last couple of months, and I can’t find where the FormatterRegistry is attached. There must be one somewhere that could be modified for the Swallowing renderer, I just can’t find it very easily. If you breakpoint your normal addFormatterFor… calls, you might be able to find it. Sorry I couldn’t be more helpful there.

Leave a Reply

Your email address will not be published. Required fields are marked *