tags:

views:

776

answers:

5

I'm new to Java EE and I know that something like the following three lines

<%= x+1 ?>
<%= request.getParameter("name") %>
<%! counter++; %>

is an oldschool way of coding and in JSP Version 2 exists a method to avoid Java Code in JSP-Files. Can someone please tell me the alternative JSP2 lines, and how this technique is called.

+3  A: 

JSTL offers tags for conditionals, loops, sets, gets, etc. For example:

<c:if test="${someAttribute == 'something'}">
   ...
</c:if>

JSTL works with request attributes - they are most often set in the request by a Servlet, which forwards to the JSP.

Bozho
+3  A: 

I'm not sure if i get this correct.

You should read something about MVC. Spring MVC & Struts 2 are the two most common solutions.

tzim
A: 

Experience has shown that JSP's have some shortcomings, one of them being hard to avoid mixing markup with actual code.

If you can, then consider using a specialized technology for what you need to do. In Java EE 6 there is JSF 2.0, which provides a lot of nice features including gluing Java beans together with JSF pages through the #{bean.method(argument)} approach.

Thorbjørn Ravn Andersen
+2  A: 

You can use JSTL tags together with EL expressions to avoid intermixing Java and HTML code:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<head>
</head>
<body>

<c:out value="${x + 1}" />
<c:out value="${param.name}" />
// and so on

</body>
</html>
Bytecode Ninja
+47  A: 

The use of scriptlets (those <% %> things) is indeed highly discouraged since the birth of taglibs (like JSTL) and EL (Expression Language, those ${} things) over a decade ago. The major disadvantages of scriptlets are:

  1. Reusability: you can't reuse scriptlets.
  2. Replaceability: you can't make scriptlets abstract.
  3. OO-ability: you can't make use of inheritance/composition.
  4. Debuggability: if scriptlet throws an exception halfway, all you get is a blank page.
  5. Testability: scriptlets are not unit-testable.
  6. Maintainability: per saldo more time is needed to maintain mingled/cluttered/duplicated code logic.

Sun Oracle itself also recommends in the JSP coding conventions to avoid use of scriptlets whenever the same functionality is possible by (tag) classes. Here are several cites of relevance:

From JSP 1.2 Specification, it is highly recommended that the JSP Standard Tag Library (JSTL) be used in your web application to help reduce the need for JSP scriptlets in your pages. Pages that use JSTL are, in general, easier to read and maintain.

...

Where possible, avoid JSP scriptlets whenever tag libraries provide equivalent functionality. This makes pages easier to read and maintain, helps to separate business logic from presentation logic, and will make your pages easier to evolve into JSP 2.0-style pages (JSP 2.0 Specification supports but deemphasizes the use of scriptlets).

...

In the spirit of adopting the model-view-controller (MVC) design pattern to reduce coupling between the presentation tier from the business logic, JSP scriptlets should not be used for writing business logic. Rather, JSP scriptlets are used if necessary to transform data (also called "value objects") returned from processing the client's requests into a proper client-ready format. Even then, this would be better done with a front controller servlet or a custom tag.

How to replace scriptlets entirely depends on the sole purpose of the code/logic. More than often this code is to be placed in a fullworthy Java class.

  • If you want to invoke the same Java code on every request, less-or-more regardless of the requested page, e.g. checking if an user is logged in, then implement a Filter and write code accordingly in doFilter() method. E.g.:

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
            ((HttpServletResponse) response).sendRedirect("login"); // Not logged in, redirect to login page.
        } else {
            chain.doFilter(request, response); // Logged in, just continue request.
        }
    }
    

    When mapped on an appropriate url-pattern covering the JSP pages of interest, then you don't need to copypaste the same piece of code over all JSP pages.

  • If you want to invoke some Java code to preprocess a request, e.g. preloading some list from a database to display in some table, then implement HttpServlet and write code accordingly in doGet() method. E.g.:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Product> products = productDAO.list(); // Obtain all products.
        request.setAttribute("products", products); // Store products in request scope.
        request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); // Forward to JSP page to display them in a HTML table.
    }
    

    This way dealing with exceptions is easier. The DB is not accessed in the midst of JSP rendering, but far before the JSP is been displayed. You still have the possibility to change the response whenever the DB access throws an exception.

  • If you want to invoke some Java code to postprocess a request, e.g. processing a form submit, then implement HttpServlet and write code accordingly in doPost() method. E.g.:

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = userDAO.find(username, password);
        if (user != null) {
            request.getSession().setAttribute("user", user); // Login user.
            response.sendRedirect("home"); // Redirect to home page.
        } else {
            request.setAttribute("message", "Unknown username/password. Please retry."); // Store error message in request scope.
            request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); // Forward to JSP page to redisplay login form with error.
        }
    }
    

    This way dealing with different result page destinations is easier: redisplaying the form with errors in case of an error (in this particular example you can redisplay it using ${message} in EL), or just taking to the desired target page in case of success.

  • If you want to invoke some Java code to control the execution plan and/or the destination of the request and the response, then implement HttpServlet according the MVC's Front Controller Pattern. E.g.:

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        View view = new View(request, response);
        Action action = ActionFactory.getAction(request);
        action.execute(view);
        view.navigate();
    }
    

    Or just adopt a MVC framework like JSF so that you end up with just a JSP/Facelets page and a Javabean class without the need for a HttpServlet.

  • If you want to invoke some Java code to control the flow inside a JSP page, then you need to grab an (existing) flow control taglib like JSTL core. E.g. displaying List<Product> in a table:

    <%@ 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>
    
  • If you want to invoke some Java code to access and display "backend" data inside a JSP page, then you need to use EL (Expression Language), those ${} things. E.g. redisplaying submitted input values:

    <input type="text" name="foo" value="${param.foo}" />
    

    The ${param.foo} displays the outcome of request.getParameter("foo").

  • If you want to invoke some utility Java code directly in the JSP page (typically public static methods), then you need to define them as EL functions. There's a standard functions taglib in JSTL, but you can also easily create functions yourself. Here's an example how JSTL fn:escapeXml is useful 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)}" />
    

    Note that the XSS sensitivity is in no way specifically related to Java/JSP/JSTL/EL/whatever, this problem needs to be taken into account in every webapplication you develop. By the way, scriptlets provides no way of builtin preventions, at least not using the standard Java API.

See also:

BalusC
Really nice exhaustive answer. +1
Pascal Thivent
Answers like these deserve more upvotes. Too bad the JSP population on SO is pretty low.
Lotus Notes
+1 Great answer. But don't go dogmatic, sometime using scriptlets IS ok, but that should be the exception that proves the rule.
svachon
@svachon: Scriptlets are useful for quick prototyping/testing. As far as I know, there's only one "legitimate" production use of a scriptlet, namely `<% response.getWriter().flush(); %>` between the `</head>` and the `<body>` to improve webpage parsing performance in the webbrowser. But this use is in turn completely negligible when the output buffer size at the server side is low (1~2KB). [See also this article](http://balusc.blogspot.com/2009/09/webapplication-performance-tips-and.html#FlushTheBufferEarly).
BalusC
@BalusC A few times I've been stuck with java classes that didn't follow the getter/setter pattern. IMHO that is a case where a scripltet does the job.
svachon
@svachon: I'd wrap those classes with own javabean classes and use them instead.
BalusC
@BalusC I agree with you that this is the best practice approach, especially with the question beign "howto avoid".
svachon
+1 : Thanks for the great answer, just started using JSTL where I work.
Zachary
Purely unintentional touchpad effect during scrolling. Please make a little edit so that I can recast it. And no, I rarely remove votes, especially if there is no reason.
Pascal Thivent
Happily casted back.
Pascal Thivent