Monday, June 15, 2009

Executing A FreeMarker Template

In our front page servlet, we need to execute the FreeMarker template we created.

We start by creating a Configuration instance. This only needs to be done once per application, so we can use a singleton pattern (remember the PMF class?) Only this time we don't have a utility class provided to us, so you'll have to write your own. Follow the PMF pattern and create a singleton for getting Configuration instances.

You'll end up with something like this (I've left off the package and import statements to save space):


public class ConfigFactory
{
  private static Configuration configInstance = null;
  private ConfigFactory() {}
  public static Configuration get(ServletContext context)
  {
   if (configInstance == null)
   {
    configInstance = new Configuration();
    configInstance.setServletContextForTemplateLoading(context, "WEB-INF/templates");
    configInstance.setObjectWrapper(new DefaultObjectWrapper());
   }

   return configInstance;
  }
}


Note that I have it looking for templates in WEB-INF/templates. That gets templates out of the way, which is generally a good idea to keep things tidy. Now would be a good time to right click on WEB-INF and create a new folder named templates, and to drag Front.ftl into that new directory.

In our front page servlet, we can now use the singleton class to get a Configuration instance.


Configuration config = ConfigFactory.get(getServletContext ());


Now we need to create the data model for the template. In the case of our simplified front page, the data model just has a boolean named loggedIn. Before we can do that we need to know if they're logged in or not.

If they are logged in, we'll have their user id in the session. So let's get the session and check.


HttpSession session = req.getSession(false);


I pass false into getSession this time because if there isn't already a session, I don't want to create one. If no session already existed, getSession(false) returns null.


boolean loggedIn = false;

if (session != null)
  loggedIn = true;


We could very well have used getAttribute to get the actual user id and looked up the User record in the datastore, but this will do for the moment. Now that we have our loggedIn variable, let's create the data model:


Map root = new HashMap();
root.put("loggedIn", loggedIn);


That's it. A data model is nothing more than a map of values. You can nest maps inside the map to create objects with fields for the template to use.

Now we need to get the template:


Template temp = config.getTemplate("Front.ftl");


If you named your template something other than Front, you'll want to modify that. Now that we have the template, let's execute it:


PrintWriter out = resp.getWriter();

try
{
  temp.process(root, out);
  out.flush();
} catch (TemplateException e)
{
  e.printStackTrace(out);
}


Note that printing the stack trace to the user's web browser isn't the best way to handle errors. We'll want to do something better than that later, but for now it'll serve.

We have one last piece before this will ever show "Sign Out". That's to cause the RPXResults servlet to redirect the user's browser back to the front page after the session has been set.

At the bottom of doPost in RPXResults, this should do it:


resp.sendRedirect("/front");


Assuming that /front is what you put in for the url mapping for your front page servlet.

You should now be able to run the application, click Sign In, go through the RPX process and end up back at the front page with the Sign Out link showing. This shows a very simple example of using templates to display pages with dynamic content. If our content is going to be updated according to user action, we'll use GWT widgets, but for simple content that stays the same once it's generated on the page, templates are a great option.

2 comments:

  1. I like this blog - tutorial. On this last part, I had to make the web.xml's welcome-file be an actual filesystem file - not a virtual path (eg. servlet name). So, I merely created a front.htm as my welcome-file, and have it META-REFRESH to the /front servlet. Also in web.xml, I needed to ensure that I had a servlet-mapping (with servlet-name and url-pattern) for the /front servlet as well - otherwise the URL of /front wouldn't go anywhere. I am new to JDO on GAE, so this was extremely helpful. Nice job!

    ReplyDelete