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 © <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>