Skip to content

Recent Articles

2
Apr

How to render a JSP page as a String

Almost every development project I’ve worked on has involved a system that sends email notifications to customers – whether it’s an online shop or a life insurance website, it’s important to keep customers informed about the status of their account or order. Or perhaps you just want to bombard them with spam. In any case, you’re going to need some way of rendering the content of those emails.

There are a few approaches I’ve seen. If your emails are simple, you could create static HTML templates with wildcards that you replace at runtime, like this -

<html>
  <body>
    <p>Dear $FIRST_NAME$,</p>

    <p>We are writing to inform you that your payment has been processed successfully.</p>

    <p>Yours sincerely,
    <b>Betty's Cakes Online Shop</b></p>
  </body>
</html>

Before sending the email, you’d do a string replacement on $FIRST_NAME$, inserting the user’s actual first name. Simple, easy to implement, and should be adequate for most ‘notification’ type email sending requirements.

What if I need to generate more complex emails?

A nice way to do this is to is to design your emails as JSPs – you get all the flexibility you would when putting together any other web page, so you can make your emails as complex as you like.

In order to achieve this you’ll need to create a renderer that can fire off an HTTP request at your JSP, passing it all the contextual information it needs. You can then read the response as a String and use it as the body of your email. The code snippet below should serve as a good starting point.

The render method takes 2 arguments – emailTemplateUrl, a fully qualified url pointing to your JSP template and parameters, a map of the parameters your JSP will require.

  private static final String EQ = "=";
  private static final String AMP = "&amp;";
  private static final String ENCODING_UTF_8 = "UTF-8";
  private static final String CHARSET_ASCII = "ASCII";
  private static final String METHOD_POST = "POST";

  public String render(String emailTemplateUrl, Map<String, String> parameters) {
     String body = null;
     InputStream is = null;

     try {
       URL url = new URL(emailTemplateUrl);
       HttpURLConnection connection = constructRequest(url, parameters);
       body = readResponse(connection.getInputStream());
     } catch (Exception e) {
       // handle exception
     } finally {
       cleanupInputstream(is);
     }
     return body;
   }

  private HttpURLConnection constructRequest(URL url, Map<String, String> parameters) throws IOException {
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setDefaultUseCaches(false);
    connection.setRequestMethod(METHOD_POST);

    OutputStreamWriter writer = null;

    try {
      writer = new OutputStreamWriter(connection.getOutputStream(), CHARSET_ASCII);
      writer.write(getQueryString(parameters));
      writer.flush();
      writer.close();
      writer = null;
    } catch (Exception e) {
      //log error
    } finally {
      if (writer != null) {
        try {
          writer.close();
          writer = null;
        } catch (Exception e) {
          //log error
        }
      }
    }
    return connection;
  }

  private String readResponse(InputStream is) throws IOException {
    StringBuffer buf = new StringBuffer();

    BufferedReader in = new BufferedReader(new InputStreamReader(is));

    String inputLine = null;

    while ((inputLine = in.readLine()) != null) {
      buf.append(inputLine);
    }

    return buf.toString();
  }

  private void cleanupInputstream(InputStream is) {
    if (is != null) {
      try {
        is.close();
      } catch (IOException e) {
        //log error
      }
    }
  }

It’s important when inserting the post parameters that you url encode both the parameter key and the parameter value, while leaving the equals unencoded.

  private String getQueryString(Map<String, String> contentMap) throws UnsupportedEncodingException {
    StringBuffer parameterBuffer = new StringBuffer();

    int index = 0;

    for (String key : contentMap.keySet()) {
      parameterBuffer.append(URLEncoder.encode(key, ENCODING_UTF_8));
      parameterBuffer.append(EQ);

      String content = contentMap.get(key);
      if (content == null) {
        content = "";
      }
      parameterBuffer.append(URLEncoder.encode(content, ENCODING_UTF_8));

      index++;

      if (index < contentMap.size()) {
        parameterBuffer.append(AMP);
      }
    }

    String queryString = parameterBuffer.toString();

    return queryString;
  }

When creating your template JSPs, you can access the POST parameters you passed to the render method using an EL expression. Lets say we populate the parameter map we pass to the render method with 2 key/value pairs:

firstName=John
orderId=1234

We’ll use the customer’s first name as it is, but we’ll also use a tag we wrote earlier to look up the actual order object based on the order id. We can now extend the simple static HTML example above by providing a list of the items that the customer has purchased -

<html>
  <body>
    <p>Dear ${param.firstName},</p>

    <p>We are writing to inform you that your payment has been processed successfully.</p>

    <p>For your information, these are the items you ordered:</p>

    <scribble:orderLookup id="${param.orderId}" var="order" />

    <table>
      <tr>
        <th>
          Item Name
        </th>
        <th>
          Sale Price
        </th>
        <th>
          Quantity
        </th>
      </tr>
      <c:forEach var="orderItem" items="${order.orderItems}">
        <tr>
          <td>
            ${orderItem.productName}
          </td>
          <td>
            ${orderItem.salePrice}
          </td>
          <td>
            ${orderItem.quantity}
          </td>
        </tr>
      </c:forEach>
    </table>

    <p>Yours sincerely,
    <b>Betty's Cakes Online Shop</b></p>
  </body>
</html>

Simple eh?

Follow

Get every new post delivered to your Inbox.