Wednesday, January 27, 2010

Hidden features of JSP/Servlet

This is a copy of my answer on stackoverflow.com (which has been deleted with the reason "Not constructive" and is therefore only visible for 10K users).


Hide JSP pages from direct access

By placing JSP files in /WEB-INF folder you effectively hide them from direct access by for example http://example.com/contextname/WEB-INF/page.jsp. This will result in a 404. You can then only access them by a RequestDispatcher in Servlet or using jsp:include.


Preprocess request for JSP

Most are aware about Servlet's doPost() to post-process a request (a form submit), but most don't know that you can use Servlet's doGet() method to pre-process a request for a JSP. For example:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    List<Item> items = itemDAO.list();
    request.setAttribute("items", items);
    request.getRequestDispatcher("/WEB-INF/page.jsp").forward(request, response);
}

which is used to preload some tabular data which is to be displayed with help of JSTL's c:forEach:

<table>
    <c:forEach items="${items}" var="item">
        <tr><td>${item.id}</td><td>${item.name}</td></tr>
    </c:forEach>
</table>

Map such a servlet on an url-pattern of /page (or /page/*) and just invoke http://example.com/contextname/page by browser address bar or a plain vanilla link to run it.


Dynamic includes

You can use EL in jsp:include:

<jsp:include page="/WEB-INF/${bean.page}.jsp" />

The bean.getPage() can just return a valid pagename.


EL can access any getter

EL does not per-se require the object-to-be-accessed to be a fullworthy Javabean. The presence of a no-arg method which is prefixed with get or is is more than sufficient to access it in EL. E.g.:

${bean.class.name}

This returns the value of bean.getClass().getName() where the getClass() method is actually inherited from Object#getClass().

${pageContext.session.id}

This returns the value of pageContext.getSession().getId().


EL can access Maps as well

The following EL notation

${bean.map.foo}

resolves to bean.getMap().get("foo"). If the Map key contains a dot, you can use the "brace notation" with a quoted key:

${bean.map['foo.bar']}

which resolves to bean.getMap().get("foo.bar"). If you want a dynamic key, use brace notation as well, but then unquoted:

${bean.map[otherbean.key]}

which resolves to bean.getMap().get(otherbean.getKey()).


Iterate over Map with JSTL

You can use c:forEach as well to iterate over a Map. Each iteration gives a Map.Entry which in turn has getKey() and getValue() methods (so that you can just access it in EL by ${entry.key} and ${entry.value}). Example:

<c:forEach items="${bean.map}" var="entry">
    Key: ${entry.key}, Value: ${entry.value} <br>
</c:forEach>

Get current date in JSP

You can get the current's date with jsp:useBean and format it with help of JSTL fmt:formatDate

<jsp:useBean id="date" class="java.util.Date" />
...
<p>Copyright &copy; <fmt:formatDate value="${date}" pattern="yyyy" /></p>

This prints (as of now) like follows: "Copyright © 2010".


Easy friendly URL's

An easy way to have friendly URL's is to make use of HttpServletRequest#getPathInfo() and JSP's hidden in /WEB-INF:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF" + request.getPathInfo() + ".jsp").forward(request, response);
}

If you map this servlet on for example /pages/*, then a request on http://example.com/contextname/pages/foo/bar will effectively display /WEB-INF/foo/bar.jsp. You can get a step further by splitting the pathinfo on / and only take the first part as JSP page URL and the remnant as "business actions" (let the servlet act as a page controller).


Redisplay user input using ${param}

The implicit EL object ${param} which refers to the HttpServletRequest#getParameterMap() can be used to redisplay user input after a form submit in JSP:

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

This basically does the same as request.getParameterMap().get("foo").
Don't forget to prevent from XSS! See following chapter.


JSTL to prevent XSS

To prevent your site from XSS, all you need to do is to (re)display user-controlled data using JSTL fn:escapeXml or c:out.

<p><input type="text" name="foo" value="${fn:escapeXml(param.foo)}">
<p><c:out value="${bean.userdata}" />

Alternating <table> rows with LoopTagStatus

The varStatus attribute of JSTL c:forEach gives you a LoopTagStatus back which in turn has several getter methods (which can be used in EL!). So, to check for even rows, just check if loop.getIndex() % 2 == 0:

<table>
    <c:forEach items="${items}" var="item" varStatus="loop">
        <tr class="${loop.index % 2 == 0 ? 'even' : 'odd'}">...</tr>
    <c:forEach>
</table>

which will effectively end up in

<table>
    <tr class="even">...</tr>
    <tr class="odd">...</tr>
    <tr class="even">...</tr>
    <tr class="odd">...</tr>
    ...
</table>

Use CSS to give them a different background color.

tr.even { background: #eee; }
tr.odd { background: #ddd; }

Populate commasepared string from List/Array with LoopTagStatus:

Another useful LoopTagStatus method is the isLast():

<c:forEach items="${items}" var="item" varStatus="loop">
    ${item}${!loop.last ? ', ' : ''}
<c:forEach>

Which results in something like item1, item2, item3.


EL functions

You can declare public static utility methods as EL functions (like as JSTL functions) so that you can use them in EL. E.g.

package com.example;

public final class Functions {
     private Functions() {}

     public static boolean matches(String string, String pattern) {
         return string.matches(pattern);
     }
}

with /WEB-INF/functions.tld which look like follows:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">

    <display-name>Custom Functions</display-name>    
    <tlib-version>1.0</tlib-version>
    <uri>http://example.com/functions</uri>

    <function>
        <name>matches</name>
        <function-class>com.example.Functions</function-class>
        <function-signature>boolean matches(java.lang.String, java.lang.String)</function-signature>
    </function>
</taglib>

which can be used as

<%@taglib uri="http://example.com/functions" prefix="f" %>

<c:if test="${f:matches(bean.value, '^foo.*')}">
    ...
</c:if>

Tuesday, January 19, 2010

What is the difference between JSF, JSP and Servlet?

This is a copy of my answer on stackoverflow.


JSP (JavaServer Pages)

JSP is a Java view technology running on the server machine which allows you to write template text in (the client side languages like HTML, CSS, JavaScript and so on). JSP supports the so-called taglibs which are backed by pieces of Java code with which you can control the page flow and/or output dynamically (programmatically). A well known taglib is JSTL. JSP also supports Expression Language which can be used to access backend data (actually, the attributes which are available in page, request, session and application scopes), mostly in combination with taglibs.

When a JSP is requested for the first time or when the webapp starts up, the servlet container will compile it into a class extending HttpServlet and use it during the webapp's lifetime. You can find the generated source code in the server's work directory. In for example Tomcat, it's the /work directory.

Servlets

Servlet is an Java application programming interface (API) running on the server machine which can intercept on the requests made by the client and can generate/send a response accordingly. A well known example is the HttpServlet which provides methods to hook on HTTP requests using the popular HTTP methods such as GET and POST. You can configure HttpServlets to listen on a certain HTTP URL pattern, which is configureable in web.xml, or more recently with Java EE 6, with @WebServlet annotation.

When a Servlet is requested for the first time or when the webapp starts up, the servlet container will create an instance of it and keep it in memory during webapp's lifetime. The same instance will be reused for every incoming request whose URL matches the servlet's URL pattern. You can access the request data by HttpServletRequest and handle the response by HttpServletResponse. Both objects are available as method arguments inside any of the overridden methods of HttpServlet, such as doGet() and doPost().

JSF (JavaServer Faces)

JSF is a component based MVC framework which is built on top of the Servlet API and provides components in flavor of taglibs which can be used in JSP or any other Java based view technology such as Facelets. Facelets is much more suited to JSF than JSP. It namely provides great templating capabilities such as composite components, while JSP basically only offers the <jsp:include> for templating, so that you're forced to create custom components with raw Java code (which is a bit opaque and a lot of tedious work in JSF) when you want to replace a repeated group of components by a single component. If you can, I recommend to drop JSP and go for Facelets when you want to develop with JSF.

As being a MVC (Model-View-Controller) framework, JSF provides the FacesServlet as the sole request-response Controller. It takes all the standard and tedious HTTP request/response work from your hands, such as gathering user input, validating/converting them, putting them in model objects, invoking actions and rendering the response. This way you end up with basically a JSP or Facelets (XHTML) page for View and a Javabean class as Model. The JSF components are been used to bind the view with the model (such as your ASP.NET web control does) and the FacesServlet uses the JSF component tree to do all the work.

Related questions