Dan WalmsleyCoding so you don't have to

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.

This entry was posted in Articles, Programming and tagged , , , , , . Bookmark the permalink.

11 Responses 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. Ro says:

    We are also using Wicket in combination with Spring. Wouldn’t it be easier to use Spring + Velocity to generate HTML Emails?

    http://www.theserverside.com/tt/blogs/showblog.tss?id=SpringVelocityEmail

    Although I would prefer a solution that is more tied to Wicket, the Spring solution seems to be easier to grab and thus more maintainable IMHO.

    I never tried it any of the above, but will have to in the next couple of months.

  3. Got it working by replacing requestCycle.request() with requestCycle.getProcessor().respond(requestCycle);

    Thanks for sharing!

  4. rodrigob says:

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

  5. JF says:

    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

  6. 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

  7. Fernando says:

    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

  8. Fernando says:

    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?

  9. Torben says:

    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?

  10. Tim Büthe says:

    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

  11. Pingback: Retrieving html from wicket components

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

Browse by Topic