Keeping JAVA code out of .jsps

Lets face it, scriptlets were a great way to make a page dynamic back when javascript was still a baby. However, anyone who has experienced jsp code in the past few years, especially within an enterprise, has seen some pretty horrible atrocities. So to clean things up a bit, here are some suggestions to avoid putting all the code into one jsp, if you are still unfortunate to be working with them.

Firstly, the disadvantages of scriptlets:

  1. Reusability: You cannot reference scriptlets
  2. Replaceability: How can you abstract scriptlets
  3. OO-ability: Try inheriting a scriptlet lately?
  4. Debuggability: Did you find the compiled java jsp source file?
  5. Testability: Screw any type of TDD
  6. Maintainability: Same problem with embedding all your javascript into an html page.

Secondly, some suggestions for avoiding too much embedded code:

public void doFilter(ServletRequest request, 
  ServletResponse response, FilterChain chain) 
    throws ServletException, IOException 
{
  if (((HttpServletRequest) request).getSession().getAttribute("user") == null) 
  {
    // Not logged in, redirect to login page.
    ((HttpServletResponse) response).sendRedirect("login"); 
  } 
  else 
  {
    // Logged in, just continue request.
    chain.doFilter(request, response); 
  }
}
  • Code that needs to execute before a specific page load should be routed through a servlet doGet()
protected void doGet(HttpServletRequest request, 
  HttpServletResponse response) 
    throws ServletException, IOException 
{
  try 
  {
    // Obtain all products.
    List<Product> products = productService.list(); 
    // Store products in request scope.
    request.setAttribute("products", products); 
    // Forward to JSP page to display them in a HTML table.
    request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); 
  } 
  catch (SQLException e) 
  {
    throw new ServletException("Retrieving products failed!", e);
  }
}
  • Code that needs to be processed after the request should be handled by a servlet doPost()
protected void doPost(HttpServletRequest request, 
  HttpServletResponse response) 
    throws ServletException, IOException 
{
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  User user = userService.find(username, password);

  if (user != null) 
  {
    request.getSession().setAttribute("user", user); // Login user.
    response.sendRedirect("home"); // Redirect to home page.
  } 
  else 
  {
    // Store error message in request scope.
    request.setAttribute("message", "Unknown username/password. Please retry."); 
    // Forward to JSP page to redisplay login form with error.
    request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); 
  }
}
  • Code that controls page navigation and actions should make use of some MVC framework (e.g. JSF, SpringMVC, Wicket)
protected void service(HttpServletRequest request, 
  HttpServletResponse response) 
    throws ServletException, IOException 
{
  try 
  {
    Action action = ActionFactory.getAction(request);
    String view = action.execute(request, response);

    if (view.equals(request.getPathInfo().substring(1)) 
    {
      request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
    } 
    else 
    {
      response.sendRedirect(view);
    }
  } 
  catch (Exception e) 
  {
    throw new ServletException("Executing action failed.", e);
  }
}
  • Controlling flow within a page should make use of the taglibs
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
...
<table>
  <c:forEach items="${products}" var="product">
    <tr>
      <td>${product.name}</td>
      <td>${product.description}</td>
      <td>${product.price}</td>
    </tr>
  </c:forEach>
</table>
...
  • Utility methods should be referenced via the taglib standard to prevent XSS attacks
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
...
<input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />
...
  • Retrieving any data set in the above methods, would be a simple expression language query
<input type="text" name="foo" value="${param.foo}" />

Admittingly, most of the information above was neatly summarised on stack-overflow. Thought I would share it here though.