Render a Wicket page to a string for HTML email

Something that\’s very desirable to do in Apache Wicket is create HTML emails using Wicket\’s brilliant component-oriented markup.

I\’ve been working on this problem on and off for ages — it\’s tricky because of teh way that markup rendering is so deeply tied to the requestcycle, which in turn is deeply dependent on the httpservletrequest — with good reason, too. That\’s where Wicket gets its autoconfiguring magic from!

So in order to use Wicket to create HTML emails, we need to fake the request/response cycle. I wrote this convenient method that renders a bookmarkable page (pageclass + pageparameters) to a string:

protected String renderPage(Class<? extends Page> pageClass, PageParameters pageParameters) {

        //get the servlet context
        WebApplication application = (WebApplication) WebApplication.get();

        ServletContext context = application.getServletContext();

        //fake a request/response cycle
        MockHttpSession servletSession = new MockHttpSession(context);
        servletSession.setTemporary(true);

        MockHttpServletRequest servletRequest = new MockHttpServletRequest(
                application, servletSession, context);
        MockHttpServletResponse servletResponse = new MockHttpServletResponse(
                servletRequest);

        //initialize request and response
        servletRequest.initialize();
        servletResponse.initialize();

        WebRequest webRequest = new WebRequest(servletRequest);

        BufferedWebResponse webResponse = new BufferedWebResponse(servletResponse);
        webResponse.setAjax(true);

        WebRequestCycle requestCycle = new WebRequestCycle(
                application, webRequest, webResponse);

        requestCycle.setRequestTarget(new BookmarkablePageRequestTarget(pageClass, pageParameters));

        try {
            requestCycle.request();

            log.warn("Response after request: "+webResponse.toString());

            if (requestCycle.wasHandled() == false) {
                requestCycle.setRequestTarget(new WebErrorCodeResponseTarget(
                        HttpServletResponse.SC_NOT_FOUND));
            }
            requestCycle.detach();

        } finally {
            requestCycle.getResponse().close();
        }

        return webResponse.toString();
    }

One other thing that\’s desirable to do is change all relative links in the email to absolute URLs — something that Wicket makes super-easy, if you know how. That will be the subject of my next post.

13 Replies to “Render a Wicket page to a string for HTML email”

  1. Hi Dan,

    thanks for the post, that seems to be exactly what I’m looking for.

    Copying the code into my app, I got a compiler error on the line where the WebRequest is created. Using the constructor to ServletWebRequest helped.

    Nonetheless, I get only an empty string back, no clue whats going wrong.

    I’m using Wicket 1.3.5.

  2. and by changing
    new WebRequest(servletRequest);
    to
    new ServletWebRequest(servletRequest);

  3. I have a similar problem – something that should be easy to do, and may already be available from the API (I am still usin 1.3.5).

    How can one duplicate rendered strings? In other words, I am trying to render once but copy a number of times for better performance – e.g., putting a page navigator both at the top and bottom of the list (the bottom is simply a label generated from copying the rendered top one). I tried using onRendered to copy getResponse.toString() for later reuse, but renderComponent() throws IllegalStateException: page not found – even though I am adding the component to a panel as instructed by Component.renderComponent() – any ideas? Thx

  4. Hey guys – use wicket-test this is the framework for testing but can render panels or entire pages to a string. It does use a fake web application so you might need to hack some things if you require funky web application setups

  5. I have some BookmarkablePageLink in the page I want to render. They are rendering just a part of the url, for instance

    http://www.miapp.com/asd/asd/asd

    it renders only

    /asd/asd/asd

    which its wrong because users read it in their email program. How can I instruct BookmarkablePageLink to render the whole url?

    thanks in advance

  6. Dan,
    I have to send emails from another servlet. In this case, I need a mock application to get it working. I try to build my own without success. Dummy Application says it has not attached a ServletContext. I got it from WicketTester. Any idea how to build mails from a servlet that is not a WicketFilter or wicket servlet?

  7. Hi,
    is there a possibility to use this code snippet for components (i.e. MarkupContainer) instead of bookmarkable pages? I didn’t find a way to change this. Maybe someone can help me?

  8. Thanks Andrew Williams for mentioning WicketTester! It’s way easier, shorter and you don’t need any web app context at all. Just do this:

    WicketTester tester = new WicketTester();
    tester.startPage(YourPage.class);
    String s = tester.getServletResponse().getDocument();

    regards,
    Tim

  9. for 1.5 version ( seen it on wicket forum )

    BufferedWebResponse bufferedWebResponse = new BufferedWebResponse(null);
    webPage.getRequestCycle().setResponse(bufferedWebResponse);
    webPage.render();
    return bufferedWebResponse.getText().toString();