Java EE tricks

Content:

  1. Introduction
  2. JSP structure
  3. Page directive
  4. JSP and Java
    1. Java calling JSP
    2. JSP calling JAVA
  5. JSTL
    1. Core
    2. Functions
    3. Formatting
    4. XML access
    5. Database access
  6. Custom taglib:
  7. Global variables and scope:
  8. Managed security
    1. Basic autentication
    2. Form based authentication
    3. More
  9. Filter
  10. Session listener
  11. Using Quartz scheduler
  12. Other languages
    1. Python
    2. Groovy
    3. PHP

Introduction:

The article Modern Java EE describe many of the main features of Java EE. The features that everybody learns.

This article describe some more odd features and some interesting possibilities, that not everyone may be aware of.

This may not be features that are frequently needed, but it can still be very convenient to know about them if the need should arise.

Especially if using an established framework like:

then one would avoid the need for a lot of this. But sometimes it is useful to be able to do the basic stuff without the help of a big framework.

Everything will be demonstrated via examples, but explanations will be very short. Everyone that know Java and know web technology should not have any problems understanding the examples.

And even if you are a beginner then by actually trying the examples, look at the output and try modifying the code should quickly bring you up to speed.

All pages should display valid but very primitive HTML. That is on purpose. The topic is Java EE technology. Advanced HTML, CSS and JavaScript is a separate topic.

All URL's shown wil assume the name of the web app to be test and the server to be localhost listening on port 8080.

JSP structure

JSP has two syntaxes:

And JSP has several elements:

Example using traditional syntax:

<%-- Directive controlling overall page: --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP structure - traditional syntax</title>
<%-- Declaration of class, field and method: --%>
<%!
public class X {
    private String s;
    public X(String s) {
        this.s = s;
    }
    public String getS() {
        return s;
    }
}
private X[] x = new X[2];
private void setTwo(String s1, String s2) {
    x[0] = new X(s1);
    x[1] = new X(s2);
}
%>
</head>
<body>
<h1>JSP structure - traditional syntax</h1>
<h2>Demo:</h2>
<%-- Scriptlet calling method: --%>
<%
setTwo("ABC", "DEF");
%>
<%-- Expressions outputting: --%>
<%=x[0].getS()%>
<br>
<%=x[1].getS()%>
</body>
</html>

Example using XML syntax:

<%-- Directive controlling overall page: --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP structure - XML syntax</title>
<%-- Declaration of class, field and method: --%>
<jsp:declaration>
public class X {
    private String s;
    public X(String s) {
        this.s = s;
    }
    public String getS() {
        return s;
    }
}
private X[] x = new X[2];
private void setTwo(String s1, String s2) {
    x[0] = new X(s1);
    x[1] = new X(s2);
}
</jsp:declaration>
</head>
<body>
<h1>JSP structure - XML syntax</h1>
<h2>Demo:</h2>
<%-- Scriptlet calling method: --%>
<jsp:scriptlet>
setTwo("ABC", "DEF");
</jsp:scriptlet>
<%-- Expressions outputting: --%>
<jsp:expression>x[0].getS()</jsp:expression>
<br>
<jsp:expression>x[1].getS()</jsp:expression>
</body>
</html>

Actions will be explained in the JSP and Java section.

Page directive

The page directive allows for controlling the pages behavior:

Attribute Example Semantics
language <%@ page language="java" %> Language for embedded code. Default value is Java. Only valid value is Java.
contentType <%@ page contentType="text/html; charset=UTF-8" %> Content-Type HTTP header to be sent in response. Default value is text/html.
pageEncoding <%@ page pageEncoding="UTF-8" %> Encoding for JSP page source. Default value is ISO-8859-1.
trimDirectiveWhitespaces <%@ page trimDirectiveWhitespaces="true" %> Whether to avoid useless whitespace in output. Default value is false.
buffer <%@ page buffer="16kb" %> Size of output buffer. Default value is 8kb. To disable buffering use the value none. Note that when buffer is full and output is sent to browser then it is no longer possible to add or modify HTTP headers for response
errorPage <%@ page errorPage="error.jsp" %> Page to display in case of an uncaugth exception. Default is no such page.
import <%@ page import="java.util.*,java.sql.*" %> Classes to import to allow usage without package name. Default is no imports. But java.lang.*, javax.servlet.*, javax.servlet.jsp.* and javax.servlet.http.* are always implicit imported.
isThreadSafe <%@ page isThreadSafe="false" %> Declare whether the JSP page is thread safe - if it is not then the servlet container will only execute on request at a time. Default value is true.
session <%@ page session="false" %> Specify whether JSP page automatically create session. Default value is true.

JSP and Java

No surprise that JSP pages and Java code integrate nicely.

The examples will use the following dummy classes for data. Real implementations would interact with a database, a business logic layer or some web services.

Data.java:

package test;

public class Data {
    private int iv;
    private String sv;
    public Data() {
        this(0, "");
    }
    public Data(int iv, String sv) {
        this.iv = iv;
        this.sv = sv;
    }
    public int getIv() {
        return iv;
    }
    public void setIv(int iv) {
        this.iv = iv;
    }
    public String getSv() {
        return sv;
    }
    public void setSv(String sv) {
        this.sv = sv;
    }
}

DataManager.java:

package test;

import java.util.List;
import java.util.ArrayList;

public class DataManager {
    private static List<Data> db = new ArrayList<Data>();
    public List<Data> getAll() {
        return db;
    }
    public void add(Data o) {
        synchronized(db) {
            db.add(o);
        }
    }
}

Java calling JSP:

HTTP requests hits a Java servlet that produces some data and forward to JSP page.

DataServlet.java:

package test;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns={"/data"})
public class DataServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        DataManager mgr = new DataManager();
        List<Data> data = mgr.getAll();
        request.setAttribute("data", data);
        request.getRequestDispatcher("data.jsp").forward(request, response);
    }
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        DataManager mgr = new DataManager();
        Data o = new Data(Integer.parseInt(request.getParameter("iv")), request.getParameter("sv"));
        mgr.add(o);
        response.sendRedirect("data");
    }
}
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

is the method called for HTTP(S) GET.

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

is the method called for HTTP(S) POST.

        request.getRequestDispatcher("data.jsp").forward(request, response);

forward (transfers control) to a JSP page. Note this happens internally on server and i invisible to browser.

        response.sendRedirect("data");

instruct browser to request the specified URL via GET.

data.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Data</title>
</head>
<body>
<h1>Data</h1>
<h2>Current:</h2>
<table border="yes">
<tr>
<th>iv</th>
<th>sv</th>
</tr>
<c:forEach items="${data}" var="o">
<tr>
<td>${o.iv}</td>
<td>${o.sv}</td>
</tr>
</c:forEach>
</table>
<h2>Add:</h2>
<form method="POST">
iv : <input type="text" name="iv">
<br>
sv : <input type="text" name="sv">
<br>
<input type="submit" value="Add">
</form>
</body>
</html>

http://localhost:8080/test/data is the URL.

JSP calling Java:

HTTP request hits a JSP page that uses various Java classes.

data2.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ page import="test.*" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Data</title>
<%
DataManager mgr = new DataManager();
if(request.getMethod().equals("GET")) {
    List<Data> data = mgr.getAll();
    request.setAttribute("data", data);
}
if(request.getMethod().equals("POST")) {
    Data o = new Data(Integer.parseInt(request.getParameter("iv")), request.getParameter("sv"));
    mgr.add(o);
    response.sendRedirect("data2.jsp");
}
%>
</head>
<body>
<h1>Data</h1>
<h2>Current:</h2>
<table border="yes">
<tr>
<th>iv</th>
<th>sv</th>
</tr>
<c:forEach items="${data}" var="o">
<tr>
<td>${o.iv}</td>
<td>${o.sv}</td>
</tr>
</c:forEach>
</table>
<h2>Add:</h2>
<form method="POST">
iv : <input type="text" name="iv">
<br>
sv : <input type="text" name="sv">
<br>
<input type="submit" value="Add">
</form>
</body>
</html>

http://localhost:8080/test/data2.jsp is the URL.

This JSP page can actually be simplified by using JSP actions.

data3.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ page import="test.*" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Data</title>
<jsp:useBean id="mgr" class="test.DataManager"/>
<c:set var="data" value="${mgr.getAll()}"/>
<jsp:useBean id="o" class="test.Data"/>
<jsp:setProperty name="o" property="*"/>
<%
if(request.getMethod().equals("POST")) {
    mgr.add(o);
    response.sendRedirect("data3.jsp");
}
%>
</head>
<body>
<h1>Data</h1>
<h2>Current:</h2>
<table border="yes">
<tr>
<th>iv</th>
<th>sv</th>
</tr>
<c:forEach items="${data}" var="o">
<tr>
<td>${o.iv}</td>
<td>${o.sv}</td>
</tr>
</c:forEach>
</table>
<h2>Add:</h2>
<form method="POST">
iv : <input type="text" name="iv">
<br>
sv : <input type="text" name="sv">
<br>
<input type="submit" value="Add">
</form>
</body>
</html>

http://localhost:8080/test/data3.jsp is the URL.

The magic JSP actions are the following:

<jsp:useBean id="mgr" class="test.DataManager"/>
...
<jsp:useBean id="o" class="test.Data"/>

that create instances of the specified class and make it available and:

<jsp:setProperty name="o" property="*"/>

that load all properties in the specified object from request (form data).

JSTL:

JSTL is the standard tag libraries.

JSTL contains multiple tag libraries:

If your server does not have JSTL librray installed then you can download Apache Standard Taglib open source implementation and put the jar files into WEB-INF/lib directory. Additionally to get XML working you may need to put Xalan's xalan.jar and serializer.jar in WE-INF/lib.

Core:

Core provides basic flow and output functionality.

This is the tag library that almost everybody knows. And it is frequently used even with standard frameworks.

Common tags are:

c:set
assign to variable
c:forEach
iterate over collection/array
c:out
output variable

Example:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Core test page</title>
<%
// demo data
List<Integer> lst = new ArrayList<Integer>();
lst.add(1);
lst.add(3);
lst.add(5);
lst.add(7);
lst.add(9);
request.setAttribute("lst", lst);
String str = "A,B,C";
request.setAttribute("str", str);
%>
</head>
<body>
<h1>Core test page</h1>
<h2>Iterate:</h2>
<c:set var="n" value="0"/>
<c:forEach items="${lst}" var="v">
<c:out value="${n}"/> : <c:out value="${v}"/>,
<c:set var="n" value="${n+1}"/>
<c:if test="${n % 2 == 0}">
<br>
</c:if>
</c:forEach>
<h2>Tokenize:</h2>
<c:forTokens items="${str}" delims="," var="v">
<c:out value="${v}"/>
<c:choose>
<c:when test="${v=='A'}"> : A</c:when>
<c:when test="${v=='B'}"> : B</c:when>
<c:otherwise> : neither A nor B</c:otherwise>
</c:choose>
<br>
</c:forTokens>
<h2>Import:</h2>
<c:import url="fragment.jsp" var="fragment"/>
<c:out value="${fragment}"/>
</body>
</html>

fragment.jsp:

It works!

Note that:

<c:out value="${v}"/>

Can be written simpler as just:

${v}

Functions:

Functions provides access to some basic functions. EL support basic operators out of the box.

Example:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Functions test page</title>
<%
// demo data
String str1 = "A,B,C";
request.setAttribute("str1", str1);
String str2 = "Test";
request.setAttribute("str2", str2);
%>
</head>
<body>
<h1>Functions test page</h1>
<h2>String functions:</h2>
<c:set var="str1m" value="${fn:split(str1, ',')}"/>
<c:forEach items="${str1m}" var="itm">
<c:out value="${itm}"/><br>
</c:forEach>
<c:set var="str1m1" value="${fn:join(str1m, '#')}"/>
<c:out value="${str1m1}"/><br>
<c:set var="str1rpl" value="${fn:replace(str1, ',', ';')}"/>
<c:out value="${str1rpl}"/><br>
<c:set var="str2uc" value="${fn:toUpperCase(str2)}"/>
<c:out value="${str2uc}"/><br>
<c:set var="str2lc" value="${fn:toLowerCase(str2)}"/>
<c:out value="${str2lc}"/><br>
<c:set var="str2len" value="${fn:length(str2)}"/>
<c:out value="${str2len}"/><br>
<c:set var="str2sub" value="${fn:substring(str2, 1, 3)}"/>
<c:out value="${str2sub}"/><br>
<c:set var="str2con" value="${fn:contains(str2, 'es')}"/>
<c:out value="${str2con}"/><br>
</body>
</html>

Formatting:

Formatting provides internationalization support.

Common tags are:

fmt:setLocale
to set locale
fmt:bundle
to specify bundle filenames
fmt:message
to output text in selected locale
fmt:formatNumber
to output numeric vale
fmt:formatDate
to output date and/or time

Example:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Formatting test page</title>
<%
double xv = 1234.5678;
request.setAttribute("xv", xv);
Date d = new Date();
request.setAttribute("d", d);
%>
</head>
<body>
<h1>Formatting test page</h1>
<h2>Bundles:</h2>
<fmt:setLocale value="da_DK"/>
<fmt:bundle basename="test">
<fmt:message key="good"/><br>
<fmt:message key="bad"/><br>
</fmt:bundle>
<fmt:setLocale value="en_US"/>
<fmt:bundle basename="test">
<fmt:message key="good"/><br>
<fmt:message key="bad"/><br>
</fmt:bundle>
<h2>Numbers and dates:</h2>
<fmt:setLocale value="da_DK"/>
<fmt:formatNumber value="${xv}"/><br>
<fmt:formatNumber value="${xv}" type="number"/><br>
<fmt:formatNumber value="${xv}" type="currency"/><br>
<fmt:formatNumber value="${xv}" type="percent"/><br>
<fmt:formatDate value="${d}"/><br>
<fmt:formatDate value="${d}" type="date"/><br>
<fmt:formatDate value="${d}" type="time"/><br>
<fmt:formatDate value="${d}" type="both"/><br>
<fmt:setLocale value="en_US"/>
<fmt:formatNumber value="${xv}"/><br>
<fmt:formatNumber value="${xv}" type="number"/><br>
<fmt:formatNumber value="${xv}" type="currency"/><br>
<fmt:formatNumber value="${xv}" type="percent"/><br>
<fmt:formatDate value="${d}"/><br>
<fmt:formatDate value="${d}" type="date"/><br>
<fmt:formatDate value="${d}" type="time"/><br>
<fmt:formatDate value="${d}" type="both"/><br>
</body>
</html>

WEB-INF/classes/test.properties:

# default to English
good = Good
bad = Bad

WEB-INF/classes/test_da_DK.properties:

good = Godt
bad = Skidt

WEB-INF/classes/test_en_US.properties:

good = Good
bad = Bad

XML access:

XML provides access to XML, XPath and XSLT.

Common tags are:

x:parse
to parse XML to DOM model
x:transform
to do XSLT transformation

Note that DOM model is navigated via XPath expression.

Example:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>XML test page</title>
<%
// demo data
String xmlstr = "<all><one id='1'>A</one><one id='2'>BB</one><one id='3'>CCC</one></all>";
request.setAttribute("xmlstr", xmlstr);
String xslstr = "<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'><xsl:output method='html' indent='yes'/><xsl:template match='all'><ul><xsl:apply-templates/></ul></xsl:template><xsl:template match='one'><li><xsl:value-of select='text()'/></li></xsl:template></xsl:stylesheet>";
request.setAttribute("xslstr", xslstr);
%>
</head>
<body>
<h1>XML test page</h1>
<h2>Parse and iterate:</h2>
<x:parse doc="${xmlstr}" varDom="doc"/>
<x:forEach select="$doc/all/one" var="one">
<x:out select="$one/@id"/> : <x:out select="$one/text()"/><br>
</x:forEach>
<h2>Transform:</h2>
<x:transform doc="${xmlstr}" xslt="${xslstr}"/>
${res}
</body>
</html>

Database access:

Database access provides access to databases via JDBC.

Common tags are:

sql:setDataSource
specific database connection/data source
sql:query
execute query (SELECT)
sql:update
execute update (INSERT/UPDATE/DELETE)
sql:param
specify value for parameter

Example:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Database access test page</title>
</head>
<body>
<h1>Database access test page</h1>
<h2>Query without params:</h2>
<sql:setDataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/Test" user="root" password="" var="con1"/>
<sql:query dataSource="${con1}" sql="SELECT f1,f2 FROM t1" var="res"/>
<c:forEach items="${res.rows}" var="row">
<c:out value="${row.f1}"/> : <c:out value="${row.f2}"/><br>
</c:forEach>
<h2>Query with params:</h2>
<sql:setDataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/Test" user="root" password="" var="con2"/>
<sql:query dataSource="${con2}" sql="SELECT f1,f2 FROM t1 WHERE f1 > ?" var="res">
<sql:param value="2"/>
</sql:query>
<c:forEach items="${res.rows}" var="row">
<c:out value="${row.f1}"/> : <c:out value="${row.f2}"/><br>
</c:forEach>
<h2>Update with params:</h2>
<sql:setDataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/Test" user="root" password=""  var="con3"/>
<sql:update dataSource="${con3}" sql="DELETE FROM t1 WHERE f1 > ?" var="res">
<sql:param value="10"/>
</sql:update>
<c:out value="${res}"/>
</body>
</html>

Obviously this taglib is more relevant for proto typing than for production code. SQL statements in UI should never happen for production code.

To use a database connection pool simply replace:

<sql:setDataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/Test" user="root" password="" var="con1"/>

with:

<sql:setDataSource dataSource="jdbc/MySqlTest" var="con1"/>

where the data source is defined in the servers configuration.

Custom taglib:

It is also possible to create ones own taglib. Such a taglib can be considered a library of reusable stuff that make it faster and more consistent to write JSP pages.

Let us define two tags: one to show data in table and one to show input form.

TableTag.java:

package test;

import java.io.IOException;
import java.util.List;

import java.beans.Expression;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

public class TableTag extends TagSupport {
    private List data;
    private String cols;
    private boolean border = true;
    private static String capitalize(String s) {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
    @Override
    public int doStartTag() {
        String[] props = cols.split(",");
        try {
            JspWriter out = pageContext.getOut();
            out.println("<table " + (border ? "border='yes'" : "") + ">");
            out.println("<tr>");
            for(int j = 0; j < props.length; j++) {
                out.println("<th>" + capitalize(props[j]) + "</th>");
            }
            out.println("</tr>");
            for(int i = 0; i < data.size(); i++) {
                out.println("<tr>");
                for(int j = 0; j < props.length; j++) {
                    Object o;
                    try {
                        Expression expr = new Expression(data.get(i), "get" + capitalize(props[j]), new Object[0]);
                        expr.execute();
                        o = expr.getValue();
                    } catch(Exception ex) {
                        o = "";
                    }
                    out.println("<td>" + o + "</td>");
                }
                out.println("</tr>");
            }
            out.println("</table>");
        } catch (IOException e) {
        }
        return SKIP_BODY;
    }
    @Override
    public int doEndTag() {
        return EVAL_PAGE;
    }
    public List getData() {
        return data;
    }
    public void setData(List data) {
        this.data = data;
    }
    public String getCols() {
        return cols;
    }
    public void setCols(String cols) {
        this.cols = cols;
    }
    public boolean isBorder() {
        return border;
    }
    public void setBorder(boolean border) {
        this.border = border;
    }
}

FormTag.java:

package test;

import java.io.IOException;

import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

public class FormTag extends TagSupport {
    private String fields;
    private String btn;
    private String method = "POST";
    private static String capitalize(String s) {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }
    @Override
    public int doStartTag() {
        String[] names = fields.split(",");
        try {
            JspWriter out = pageContext.getOut();
            out.println("<form method='" + method + "'>");
            for(int i = 0; i < names.length; i++) {
                out.println(capitalize(names[i]) + " : <input type='text' name='" + names[i] + "'>");
                out.println("<br>");
            }
            out.println("<input type='submit' value='" + btn + "'>");
            out.println("</form>");
        } catch (IOException e) {
        }
        return SKIP_BODY;
    }
    @Override
    public int doEndTag() {
        return EVAL_PAGE;
    }
    public String getFields() {
        return fields;
    }
    public void setFields(String fields) {
        this.fields = fields;
    }
    public String getBtn() {
        return btn;
    }
    public void setBtn(String btn) {
        this.btn = btn;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
}

WEB-INF/data.tld:

<?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/web-jsptaglibrary_2_1.xsd" version="2.1">
    <tlib-version>1.0</tlib-version>
    <jsp-version>2.0</jsp-version>
    <short-name>Data tags</short-name>
    <uri>http://test.vajhoej.dk/data</uri>
    <display-name>Data tags</display-name>
    <tag>
        <name>table</name>
        <tag-class>test.TableTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>data</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <type>java.util.List</type>
        </attribute>
        <attribute>
            <name>cols</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <name>border</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.Boolean</type>
        </attribute>
    </tag>
    <tag>
        <name>form</name>
        <tag-class>test.FormTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>fields</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <name>btn</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <name>method</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
    </tag>
</taglib>

data4.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ page import="test.*" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="data" uri="http://test.vajhoej.dk/data" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Data</title>
<jsp:useBean id="mgr" class="test.DataManager"/>
<c:set var="data" value="${mgr.getAll()}"/>
<jsp:useBean id="o" class="test.Data"/>
<jsp:setProperty name="o" property="*"/>
<%
if(request.getMethod().equals("POST")) {
    mgr.add(o);
    response.sendRedirect("data4.jsp");
}
%>
</head>
<body>
<h1>Data</h1>
<h2>Current:</h2>
<data:table data="${data}" cols="iv,sv"/>
<h2>Add:</h2>
<data:form fields="iv,sv" btn="Add"/>
</body>
</html>

http://localhost:8080/test/data4.jsp is the URL.

Global variables and scope:

JSP has 4 global variables and scopes:

global variable/scope start life cycle end life cycle usage
page before page execution start after page excution is done normal variable
request before request execution start after request execution is done normal variable that is kept in case of a forward
session session start = first JSP page requestes or explicit creation in servlet session stop = timeout or explicit destruction share data between multiple requests from same user
application application start = server start or application deployment application stop = server shutdown or application redeployment share data between multiple users

EL automatically search all scopes for variables.

Most tags that creates a new variable allow scope to be spcified explicit. If not specified then default is page scope.

In Java you explicit chose scope by chosing the global variable you call getAttribute or setAttribute on.

Managed security:

It is possible to protect an entire web application or part of an web application declative. No changes to any pages or any code - just a change to the web applications configuration (WEB-INF/web.xml).

Both basic authentication and form based authentication are supported.

Note that the actual authentication mechanism is defined in the servers configuration.

Basic authentication:

WEB-INF/web.xml fragment:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd"
         version="3.0">
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Secure part</web-resource-name>
            <url-pattern>/secure/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <!-- NONE allows both HTTP and HTTPS, INTEGRAL and CONFIDENTIAL requires HTTPS -->
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-role>
        <role-name>user</role-name>
    </security-role>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Test login</realm-name> 
  </login-config>
</web-app>

Form based authentication:

WEB-INF/web.xml fragment:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd"
         version="3.0">
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Secure part</web-resource-name>
            <url-pattern>/secure/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>user</role-name>
        </auth-constraint>
        <user-data-constraint>
            <!-- NONE allows both HTTP and HTTPS, INTEGRAL and CONFIDENTIAL requires HTTPS -->
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-role>
        <role-name>user</role-name>
    </security-role>
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/login.jsp</form-login-page>
            <form-error-page>/error.jsp</form-error-page>
        </form-login-config>
  </login-config>
</web-app>

login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login page</title>
</head>
<body>
<h1>Login page</h1>
<h2>Login:</h2>
<form action="j_security_check" method="POST">
Username: <input type="text" name="j_username">
<br>
Password: <input type="password" name="j_password">
<br>
<input type="submit" value="Login">
</form>
</body>
</html>

The names j_security_check, j_username and j_password are magic.

error.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Error page</title>
</head>
<body>
<h1>Error page</h1>
<h2>Error:</h2>
<p>
Login failed !
</p>
</body>
</html>

More:

It is of course possible to get the username and check for roles programmatic.

secure/test.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Secure page</title>
</head>
<body>
<h1>Secure page</h1>
<h2>Info:</h2>
<table border="1">
<tr>
<th>Information</th>
<th>Value</th>
</tr>
<tr>
<td>Auth type</td>
<td><%=request.getAuthType()%></td>
</tr>
<tr>
<td>Remote user</td>
<td><%=request.getRemoteUser()%></td>
</tr>
<tr>
<td>User principal</td>
<td><%=request.getUserPrincipal()%></td>
</tr>
<tr>
<td>Is user</td>
<td><%=request.isUserInRole("user")%></td>
</tr>
</table>
</body>
</html>

http://localhost:8080/test/secure/test.jsp is the URL.

Note that for security reasons then HTTPS should be required for login *and* access to all login-protected pages.

This means:

<transport-guarantee>CONFIDENTIAL</transport-guarantee>

should be used.

Filter:

Sometime it is desirable to add some pre or post processing to all or a group of servlets/JSP pages.

This can be done using a filter. A filter is some code that get called for all URL's matching a given pattern.

This can be illustrated with a couple of examples.

First example is blocking some specific IP address from accessing the URL's.

IPAddressFilter.java:

package test;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebFilter(filterName="IPAddressFilter",urlPatterns={"*"},initParams={@WebInitParam(name="block",value="192.168.1.108,192.168.1.110")})
public class IPAddressFilter implements Filter {
    private List<String> blockaddr;
    @Override
    public void init(FilterConfig conf) throws ServletException {
        String allipaddr = conf.getInitParameter("block");
        blockaddr = Arrays.asList(allipaddr.split(","));
    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpreq = (HttpServletRequest)req;
        HttpServletResponse httpresp = (HttpServletResponse)resp;
        String thisaddr = req.getRemoteAddr();
        if(blockaddr.contains(thisaddr)) {
            httpresp.setStatus(HttpServletResponse.SC_FORBIDDEN);
        } else {
            chain.doFilter(httpreq, httpresp);
        }
    }
    @Override
    public void destroy() {
    }
}

Most of this code should be self-explanatory.

@WebFilter(filterName="IPAddressFilter",urlPatterns={"*"},initParams={@WebInitParam(name="block",value="192.168.1.108,192.168.1.110")})

defines that this is a filter for URL's matching * and send a parameter over to the init code.

            httpresp.setStatus(HttpServletResponse.SC_FORBIDDEN);

aborts processing and return a HTTP error status.

            chain.doFilter(httpreq, httpresp);

continue with normal processing (possible including other filters).

The parameter probably make more sense if the filter is defined in web.xml instead of via annotations in the code.

WEB-INF/web.xml fragment:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd"
         version="3.0">
    <filter>
        <filter-name>IPAddressFilter</filter-name>
        <filter-class>test.IPAddressFilter</filter-class>
        <init-param>
            <param-name>block</param-name>
            <param-value>192.168.1.108,192.168.1.110</param-value>
        </init-param>    
    </filter>
    <filter-mapping>
        <filter-name>IPAddressFilter</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>
</web-app>

Second example is to log information from request and response message.

LogFilter.java:

package test;

import java.io.FilterOutputStream;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.HashMap;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

@WebFilter(filterName="LogFilter",urlPatterns={"*"})
public class LogFilter implements Filter {
    public static class LogOutputStream extends FilterOutputStream {
        public LogOutputStream(OutputStream real) {
            super(real);
        }
        @Override
        public void write(byte[] b) throws IOException {
            System.out.print(new String(b));
            super.write(b);
        }
        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            System.out.print(new String(b, off, len));
            super.write(b, off, len);
        }
        @Override
        public void write(int b) throws IOException {
            System.out.print((char)b);
            super.write(b);
        }
    }
    public static class LogWriter extends FilterWriter {
        protected LogWriter(Writer real) {
            super(real);
        }
        @Override
        public void write(char[] cbuf, int off, int len) throws IOException {
            System.out.print(new String(cbuf, off, len));
            super.write(cbuf, off, len);
        }
        @Override
        public void write(int c) throws IOException {
            System.out.print((char)c);
            super.write(c);
        }
        @Override
        public void write(String str, int off, int len) throws IOException {
            System.out.println(str.substring(off, off + len));
            super.write(str, off, len);
        }
    }
    public static class LogServletOutputStream extends ServletOutputStream {
        private OutputStream real;
        public LogServletOutputStream(OutputStream real) {
            this.real = real;
        }
        @Override
        public boolean isReady() {
            return true;
        }
        @Override
        public void setWriteListener(WriteListener wrtlist) {
        }
        @Override
        public void write(int b) throws IOException {
            real.write(b);
        }
    }
    public static class LogHttpServletResponse extends HttpServletResponseWrapper {
        private HttpServletResponse real;
        public LogHttpServletResponse(HttpServletResponse real) {
            super(real);
            this.real = real;
        }
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            return new LogServletOutputStream(new LogOutputStream(real.getOutputStream()));
        }
        @Override
        public PrintWriter getWriter() throws IOException {
            return new PrintWriter(new LogWriter(real.getWriter()));
        }
    }
    @Override
    public void init(FilterConfig conf) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpreq = (HttpServletRequest)req;
        HttpServletResponse httpresp = (HttpServletResponse)resp;
        System.out.println(httpreq.getMethod() + " " + httpreq.getRequestURI() + " " + httpreq.getProtocol());
        Enumeration<String> ehdr = httpreq.getHeaderNames();
        while(ehdr.hasMoreElements()) {
            String hdr = ehdr.nextElement();
            System.out.println(hdr + ": " + httpreq.getHeader(hdr));
        }
        System.out.println("Parameters = " + new HashMap<String, String[]>(httpreq.getParameterMap()));
        chain.doFilter(httpreq, new LogHttpServletResponse(httpresp));
    }
    @Override
    public void destroy() {
    }
}

All the wrapper classes require some lines but the logic should still be easy to follow.

Session listener:

Another interesting capability is the possibility to track what is going on with sessions.

This can be done with session listeners and session attribute listeners.

Example of session listener.

MySessionListener.java:

package test;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class MySessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession ses = se.getSession();
        System.out.printf("Session created: %s\n", ses.getId());
    }
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession ses = se.getSession();
        long dt = System.currentTimeMillis() - ses.getLastAccessedTime();
        System.out.printf("Session destroyed: %s (after %d ms)\n", ses.getId(), dt);
    }
}

Example of session attribute listener.

MySessionAttributeListener.java:

package test;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

@WebListener
public class MySessionAttributeListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        String id = event.getSession().getId();
        String name = event.getName();
        Object value = event.getValue();
        System.out.printf("Session %s added %s = %s\n", id, name, value.toString());
    }
    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {
        String id = event.getSession().getId();
        String name = event.getName();
        Object value = event.getValue();
        System.out.printf("Session %s removed %s = %s\n", id, name, value.toString());
    }
    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {
        String id = event.getSession().getId();
        String name = event.getName();
        Object value = event.getValue();
        System.out.printf("Session %s replaced %s = %s\n", id, name, value.toString());
    }
}

The code is the methods should be self-explanatory.

@WebListener

tells the servlet container that this is a listener that need to be registered. The container figure what type of listener be looking at the implemented interfaces.

If you want to control session timeout then define it in WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd"
         version="3.0">
    <session-config>
        <session-timeout>3</session-timeout>
    </session-config>
</web-app>

It is specified in minutes. 10, 20 or 30 minutes are common values.

Using Quartz scheduler:

It is a common problem for web applications to have some task that need to be executed at specific intervals. With many web technologies that is a problem to implement inside the web application and instead an external scheduler is used. Java EE containers allow the usage of scheduled jobs inside the web application.

The de facto standard product for this is an open source product Quartz Scheduler.

Note that this type of scheduler is a completely different thing than using a simple timer task. When configured with a database, then Quartz jobs will survive restart of server and will only run once in a multi-node cluster. Quartz also has capabilities to manage jobs.

To get Quartz up and running you need 3 things:

  1. copy Quartz jar files to WEB-INF/lib directory
  2. create quartz.properties in WEB-INF/classes directory
  3. modify WEB-INF/web.xml

quartz.properties:

# threadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.makeThreadsDaemons = true
org.quartz.threadPool.threadCount = 5
# jobStore
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = mysqlds
org.quartz.jobStore.isClustered = true
# dataSource
org.quartz.dataSource.mysqlds.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.mysqlds.URL = jdbc:mysql://localhost/Test
org.quartz.dataSource.mysqlds.user = root
org.quartz.dataSource.mysqlds.password =

WEB-INF/web.xml fragment:

    <context-param>
         <param-name>quartz:config-file</param-name>
         <param-value>quartz.properties</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:shutdown-on-unload</param-name>
         <param-value>true</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:wait-on-shutdown</param-name>
         <param-value>false</param-value>
     </context-param>
     <context-param>
         <param-name>quartz:start-scheduler-on-load</param-name>
         <param-value>true</param-value>
     </context-param>
     <listener>
         <listener-class>
             org.quartz.ee.servlet.QuartzInitializerListener
         </listener-class>
     </listener>

For convenience I have created a small utility class and a management JSP page.

QuartzUtil.java:

package test;

import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.JobBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.TriggerBuilder.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;

public class QuartzUtil {
    private static final String GROUP = "stdgroup";
    private static final String JOB_SUFFIX = "_job";
    private static final String TRIGGER_SUFFIX = "_trigger";
    private static Scheduler getScheduler() throws SchedulerException {
        SchedulerFactory schedFact = new StdSchedulerFactory();
        Scheduler sched = schedFact.getScheduler();
        return sched;  
    }
    private static void run(String name, Class<? extends Job> jobclz, Trigger trigger) throws SchedulerException {
        Scheduler sched = getScheduler();
        JobDetail job = newJob(jobclz).withIdentity(name + JOB_SUFFIX, GROUP).build();
        sched.scheduleJob(job, trigger);
    }
    public static void runNow(String name, Class<? extends Job> jobclz) throws SchedulerException {
        Trigger trigger = newTrigger().withIdentity(name + TRIGGER_SUFFIX, GROUP).startNow().build();
        run(name, jobclz, trigger);
    }
    public static void runEveryDay(String name, Class<? extends Job> jobclz, int h, int m) throws SchedulerException {
        Trigger trigger = newTrigger().withIdentity(name + TRIGGER_SUFFIX, GROUP).startNow().withSchedule(dailyAtHourAndMinute(h, m)).build();
        run(name, jobclz, trigger);
    }
    public static void runInterval(String name, Class<? extends Job> jobclz, int m) throws SchedulerException {
        Trigger trigger = newTrigger().withIdentity(name + TRIGGER_SUFFIX, GROUP).startNow().withSchedule(simpleSchedule().withIntervalInMinutes(m).repeatForever()).build();
        run(name, jobclz, trigger);
    }
    public static class JobInfo {
        private String name;
        private String clznam;
        private Date lastrun;
        private Date nextrun;
        public JobInfo(String name, String clznam, Date lastrun, Date nextrun) {
            super();
            this.name = name;
            this.clznam = clznam;
            this.lastrun = lastrun;
            this.nextrun = nextrun;
        }
        public String getName() {
            return name;
        }
        public String getClznam() {
            return clznam;
        }
        public Date getLastrun() {
            return lastrun;
        }
        public Date getNextrun() {
            return nextrun;
        }
    }
    public static List<JobInfo> getList() throws SchedulerException {
        List<JobInfo> res = new ArrayList<JobInfo>();
        Scheduler sched = getScheduler();
        for(JobKey jk : sched.getJobKeys(GroupMatcher.jobGroupEquals(GROUP))) {
            for(Trigger tg : sched.getTriggersOfJob(jk)) {
                res.add(new JobInfo(jk.getName(), sched.getJobDetail(jk).getJobClass().getName(), tg.getPreviousFireTime(), tg.getNextFireTime()));
            }
        }
        return res;
    }
}

joblist.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="java.util.*" %> 
<%@ page import="test.*" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="data" uri="http://test.vajhoej.dk/data" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Jobs</title>
<%
List<QuartzUtil.JobInfo> jobs = QuartzUtil.getList();
request.setAttribute("jobs", jobs);
%>
</head>
<body>
<h1>Jobs</h1>
<h2>Job list:</h2>
<data:table data="${jobs}" cols="name,clznam,lastrun,nextrun"/>
</body>
</html>

And a demo.

OneMinutePing.java:

package test;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class OneMinutePing implements Job {
    @Override
    public void execute(JobExecutionContext ctx) throws JobExecutionException {
        System.out.println("1 minute ping");
    }
}

jobstart.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ page import="test.*" %> 
<%
QuartzUtil.runInterval("OneMinute", OneMinutePing.class, 1);
%>

Other languages:

It is not surprising that the main programming language in Java EE is Java. But it is actually possible to use languages.

Java is a great language for many purposes and in many contexts. But sometimes it is more convenient to use a different language.

We will explore a few possibilities here. It will be very introductory - not much deeper than hello world.

Python:

Python is a very popular language. Especially in scientific environments and among sysadmin types.

Python has a JVM implementation called Jython. And Jython does support Java EE web apps. You can write servlets in Jython (sometimes called jylets).

To make it work you need to:

  1. copy jython.jar to WEB-INF/lib directory
  2. modify WEB-INF/web.xml

WEB-INF/web.xml fragment:

     <servlet>
        <servlet-name>PyServlet</servlet-name>
        <servlet-class>org.python.util.PyServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>python.home</param-name>
            <param-value>C:\DivJava\jython2.7.0</param-value>
        </init-param>
        <init-param>
            <param-name>python.path</param-name>
            <param-value>C:\Apache\apache-tomcat-8.5.12\webapps\test\WEB-INF\classes</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>PyServlet</servlet-name>
        <url-pattern>*.py</url-pattern>
    </servlet-mapping>

Note: I could not get python.home to work so as a workaround I had to put all Jython Lib/*.class into a jar file in WEB-INF/lib.

Let us first see the classic hello world.

test.py:

from javax.servlet.http import HttpServlet

class test(HttpServlet):
    def doGet(self, request, response):
        writer = response.getWriter()
        response.setContentType ("text/plain")
        writer.println ("Hello world!")

http://localhost:8080/test/test.py is the URL.

Let us now implement the TestServlet we used previously.

data.py:

from javax.servlet.http import HttpServlet

from test import Data
from test import DataManager

class data(HttpServlet):
    def doGet(self, request, response):
        request.setAttribute("data", DataManager().getAll())
        request.getRequestDispatcher("data.jsp").forward(request, response)
    def doPost(self, request, response):
        mgr = DataManager()
        o = Data(request.getParameter("iv"), request.getParameter("sv"))
        mgr.add(o)
        response.sendRedirect("data.py")

http://localhost:8080/test/data.py is the URL.

Groovy:

Groovy is a JVM based scripting language.

I consider Groovy to be Java with a very relaxed syntax. Minimal boilerplate code.

Web applications with Groovy is usually done using the Grails framework, which I will not cover here. But it is also possible to write servlets called groovlets in Groovy.

To make it work you need to:

  1. copy a bunch of Groovy jar files to WEB-INF/lib directory
  2. modify WEB-INF/web.xml

WEB-INF/web.xml fragment:

    <servlet>
        <servlet-name>GroovyServlet</servlet-name>
        <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>GroovyServlet</servlet-name>
        <url-pattern>*.groovy</url-pattern>
    </servlet-mapping>

Let us first see the classic hello world.

test.groovy:

response.setContentType ("text/plain")
println "Hello world!"

http://localhost:8080/test/test.groovy is the URL.

Let us now implement the TestServlet we used previously.

data.groovy:

import test.*

mgr = new DataManager()
if (request.method == "GET") {
    request.data = mgr.getAll()
    request.getRequestDispatcher("data.jsp").forward(request, response)
}
if (request.method == "POST") {
    o = new Data(Integer.parseInt(request.getParameter("iv")), request.getParameter("sv"))
    mgr.add(o)
    response.sendRedirect("data.groovy")
}

http://localhost:8080/test/fsts.groovy is the URL.

I told you that Groovy had a relaxed syntax! :-)

But Groovy also has some builtin capabilities for HTML generation that may make it possible to avoid JSP completely.

One capability is Groovys multi line string literals with variable substitution.

multiline.groovy:

import test.*

mgr = new DataManager()
if (request.method == 'GET') {
    data = mgr.getAll()
    println """<html>
<head>
<title>Data</title>
</head>
<body>
<h1>Data</h1>
<h2>Current:</h2>
<table border='yes'>
<tr>
<th>iv</th>
<th>sv</th>
</tr>"""
    data.each { 
        println """<tr>
<td>${it.iv}</td>
<td>${it.sv}</td>
</tr>"""
    }
    println """</table>
<h2>Add:</h2>
<form method='POST'>
iv : <input type='text' name='iv'>
<br>
sv : <input type='text' name='sv'>
<br>
<input type='submit' value='Add'>
</form>
</body>
</html>"""
}
if (request.method == 'POST') {
    o = new Data(Integer.parseInt(request.getParameter('iv')), request.getParameter('sv'))
    mgr.add(o)
    response.sendRedirect('multiline.groovy')
}

Another capability is Groovys markup builder, which is a form of DSL.

dsl.groovy:

import test.*

mgr = new DataManager()
if (request.method == 'GET') {
    data = mgr.getAll()
    html.html {
        head {
            title 'Data'
        }
        body {
            h1 'Data'
            h2 'Current:'
            table (border:'yes') {
                tr {
                    th 'iv'
                    th 'sv'
                }
                data.each {o ->
                    tr {
                        td "${o.iv}"
                        td "${o.sv}"
                    }
                }
            }                                                                                     
            h2 'Add:'
            form (method:'POST') {
                label 'iv:'
                input (type:'text',name:'iv')
                br()
                label 'sv:'
                input (type:'text',name:'sv')
                br()
                input (type:'submit',value:'Add')
            }
        }
    }
}
if (request.method == 'POST') {
    o = new Data(Integer.parseInt(request.getParameter('iv')), request.getParameter('sv'))
    mgr.add(o)
    response.sendRedirect('dsl.groovy')
}

You can do some really cool stuff in Groovy.

PHP:

PHP is one of the worlds most popular web languages.

And it is actually possible to use PHP in a Java EE web application by using a third party module Caucho Quercus.

To make it work you need to:

  1. copy quercus.jar to WEB-INF/lib directory
  2. modify WEB-INF/web.xml

Note that you need to extract quercus.jar from the quercus.war that Caucho offer as download on their web site.

WEB-INF/web.xml fragment:

    <servlet>
        <servlet-name>QuercusServlet</servlet-name>
        <servlet-class>com.caucho.quercus.servlet.QuercusServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>QuercusServlet</servlet-name>
        <url-pattern>*.php</url-pattern>
    </servlet-mapping>

Let us first see the classic hello world.

test.php:

<?php
echo 'Hello world!';
?>

http://localhost:8080/test/test.php is the URL.

Let us now implement the TestServlet we used previously.

data.php:

<?php
import test.Data;
import test.DataManager;

$request = quercus_servlet_request(); // get HttpServletRequest object
$response = quercus_servlet_response(); // get HttpServletResponse object
$mgr = new DataManager();
if($_SERVER['REQUEST_METHOD'] === 'GET') {
    $request->setAttribute('data', $mgr->getAll());
    $response->reset(); // necessary as Quercus has called getOutputStream and JSP page call getWriter
    $request->getRequestDispatcher('data.jsp')->forward($request, $response);
}
if($_SERVER['REQUEST_METHOD'] === 'POST') {
    $o = new Data((int)$_REQUEST['iv'], $_REQUEST['sv']);
    $mgr->add($o);
    $response->sendRedirect('data.php');
}
?>

http://localhost:8080/test/data.php is the URL.

But the main purpose of Quercus is not to support PHP as language in a Java EE environment but to support pure PHP web applications executing on Java EE application servers.

And Quercus support most PHP code.

Example using PDO for database access.

PDO.php:

<?php
function get_connection() {
    $con = new PDO('mysql:host=localhost;dbname=Test', 'root', '');
    $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    //$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    return $con;
}

function t1_get_one($f2) {
    $con = get_connection();
    $stmt = $con->prepare('SELECT F1 FROM T1 WHERE F2=:F2');
    $stmt->execute(array(':F2' => $f2));
    if($row = $stmt->fetch()) {
        $f1 = $row['F1'];   
    } else {
        die("$f2 not found");
    }
    return $f1;
}

function t1_get_all() {
    $con = get_connection();
    $stmt = $con->prepare('SELECT F1,F2 FROM T1');
    $stmt->execute(array());
    $res = array();
    while($row = $stmt->fetch()) {
        $res[] = $row;
    }
    return $res;
}

function t1_put($f1, $f2) {
    $con = get_connection();
    $stmt = $con->prepare('INSERT INTO T1(F1,F2) VALUES(:F1,:F2)');
    $stmt->execute(array(':F1' => $f1, ':F2' => $f2));
}

function t1_remove($f1) {
    $con = get_connection();
    $stmt = $con->prepare('DELETE FROM T1 WHERE F1=:F1');
    $stmt->execute(array(':F1' => $f1));
}

function t1_display($data) {
    $rows = "";
    foreach($data as $row) {
        $f1 = $row['F1'];
        $f2 = $row['F2'];
        $rows .= sprintf("<tr>\r\n<td>$f1</td>\r\n<td>$f2</td>\r\n</tr>\r\n");
    }
    return "<table border='yes'>\r\n<tr>\r\n<th>F1</th>\r\n<th>F2</th>\r\n</tr>\r\n$rows</table>\r\n";   
}

function test() {
    $f2 = t1_get_one('BB');
    echo "$f2<br>\r\n";
    $data = t1_get_all();
    echo t1_display($data);
    t1_put(999, 'XXX');
    $data = t1_get_all();
    echo t1_display($data);
    t1_remove(999);
    $data = t1_get_all();
    echo t1_display($data);
}

test();
?>

The code was an existing piece of code written to be run with real PHP not quercus.

Everything worked except that I had to outcomment one line:

    //$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

That is not supported by Quercus PDO.

To use a database connection pool simply replace:

    $con = new PDO('mysql:host=localhost;dbname=Test', 'root', '');

with:

    $con = new PDO('java:comp/env/jdbc/MySqlTest');

where the data source is defined in the servers configuration.

Example using mysqli for database access.

mysqli.php:

<?php
function get_connection() {
    $con = new mysqli('localhost', 'root', '', 'Test');
    if(mysqli_connect_errno()) {
        die(mysqli_connect_error());
    }
    return $con;
}

function t1_get_one($f2) {
    $con = get_connection();
    $stmt = $con->prepare('SELECT F1 FROM T1 WHERE F2=?') or die(mysqli_error($con));
    $stmt->bind_param('s', $f2);
    $stmt->execute() or die(mysqli_error($con));
    $rs = $stmt->get_result();
    if($row = $rs->fetch_array(MYSQLI_ASSOC)) {
        $f1 = $row['F1'];   
    } else {
        die("$f2 not found");
    }
    $stmt->close();
    $con->close();
    return $f1;
}

function t1_get_all() {
    $con = get_connection();
    $stmt = $con->prepare('SELECT F1,F2 FROM T1') or die(mysqli_error($con));
    $stmt->execute() or die(mysqli_error($con));
    $rs = $stmt->get_result();
    $res = array();
    while($row = $rs->fetch_array(MYSQLI_ASSOC)) {
        $res[] = $row;
    }
    $stmt->close();
    $con->close();
    return $res;
}

function t1_put($f1, $f2) {
    $con = get_connection();
    $stmt = $con->prepare('INSERT INTO T1(F1,F2) VALUES(?,?)') or die(mysqli_error($con));
    $stmt->bind_param('is', $f1, $f2);
    $stmt->execute() or die(mysqli_error($con));
    $stmt->close();
    $con->close();
}

function t1_remove($f1) {
    $con = get_connection();
    $stmt = $con->prepare('DELETE FROM T1 WHERE F1=?') or die(mysqli_error($con));
    $stmt->bind_param('i', $f1);
    $stmt->execute() or die(mysqli_error($con));
    $stmt->close();
    $con->close();
}

function t1_display($data) {
    $rows = "";
    foreach($data as $row) {
        $f1 = $row['F1'];
        $f2 = $row['F2'];
        $rows .= sprintf("<tr>\r\n<td>$f1</td>\r\n<td>$f2</td>\r\n</tr>\r\n");
    }
    return "<table border='yes'>\r\n<tr>\r\n<th>F1</th>\r\n<th>F2</th>\r\n</tr>\r\n$rows</table>\r\n";   
}

function test() {
    $f2 = t1_get_one('BB');
    echo "$f2<br>\r\n";
    $data = t1_get_all();
    echo t1_display($data);
    t1_put(999, 'XXX');
    $data = t1_get_all();
    echo t1_display($data);
    t1_remove(999);
    $data = t1_get_all();
    echo t1_display($data);
}

test();
?>

The code was an existing piece of code written to be run with real PHP not quercus.

Everything worked.

To use a database connection pool simply replace:

    $con = new mysqli('localhost', 'root', '', 'Test');

with:

    $con = new mysqli();

and add information to servlet definition in WEB-INF/web.xml:

    <servlet>
        <servlet-name>QuercusServlet</servlet-name>
        <servlet-class>com.caucho.quercus.servlet.QuercusServlet</servlet-class>
        <init-param>
            <param-name>database</param-name>
            <param-value>jdbc/MySqlTest</param-value>
        </init-param>
    </servlet>

where the data source is defined in the servers configuration.

Note that Quercus is available both under open source license and as a commercial product. If you want to use it then I recommend that you check license condition and feature set carefully before starting usage.

I have never considered Caucho's many other products including their flagship product the Resin server attractive. But I am very impressed by Quercus and its high level of PHP compatibility. It is so cool.

Article history:

Version Date Description
1.0 September 3rd 2017 Initial version
1.1 October 6th 2017 Add filter and session listener sections
1.2 October 13th 2017 Do filter with annotations instead of web.xml
1.3 October 25th 2017 Add section about page directive

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj