Introduction
JSP stands for Java Servlet Pages, a shorthand way of writing simple Servlets, more akin to other web scripting languages like PHP and ASP. A JSP file basically contains HTML, but with embedded JSP tags with snippets of Java code inside them.
To be active, JSP files have to be placed in a servlet context directory. Here in this page, we'll use the Personal Tomcat system to develop a series of JSP examples, and we'll assume that we've already set up a personal tomcat context directory called example-context for all our examples to go inside. That is, we've already done the following:
mktomcat6 ~/example-context cd ~/example-context
A Simple Example JSP file - eg1.jsp
Here is eg1.jsp, a very simple (and completely pointless) JSP File:
<html> <body bgcolor="#ffffff"> <h1>Hello world!</h1> <% out.println("<p> This web page is produced by JSP"); %> </body> </html>
As you can see, most of this is pure HTML, but within the <%..%> section, we abruptly switch to writing pure Java code which prints out some more HTML code. When you access this file via a web browser and Tomcat, Tomcat translates the JSP file into a servlet class, adding various standard declarations (including the mysterious class instance out, an open file handle to which you print HTML output), compiles it, loads the class into memory, and then invokes a particular auto-generated class method - which runs your JSP code. Sounds complex? Yes, it is, especially when you realise that it is quite possible to write a JSP which compiles into invalid Java code! If you're interested, explore your new context directory a bit more thoroughly - after invoking a jsp URL - you should be able to find the auto-generated Java servlet source file!
Ok, let's put eg1.jsp into our Personal Tomcat servlet context, alongside index.jsp:
-
Using the Linux shell's cute directory stack feature, let's nip temporarily inside the webapps/ROOT subdirectory: just type pushd webapps/ROOT.
-
Explanation: This changes our current working directory to ~/example-context/webapps/ROOT, while remembering that we were previously in the ~/example-context directory - and can return there later. Note that pushd prints out this information as a list of directories; the first item printed is ~/example-context/webapps/ROOT (the current directory), and the second item printed is ~/example-context (the "stacked" directory).
-
Use any text editor to create the eg1.jsp file, for example: pico eg1.jsp, then cut and paste the above JSP content in; then save the file and quit pico.
-
Swap the top two places on the directory stack by typing: pushd (with no argument): the first item printed (your new current directory) is ~/example-context, and the second item (stacked) is ~/example-context/webapps/ROOT.
-
Start Tomcat via: tomcat6 start.
-
Point your web browser at: http://localhost:59999/eg1.jsp and eg1.jsp will be executed to generate the obvious web page. Wow, that was exciting, wasn't it?
-
Before you forget: tomcat6 stop.
A Slightly Better Example JSP file - eg2.jsp
The above example was pretty pointless, because all the Java code we called did was to print out static HTML which we could have done without all the JSP/Java Servlet overhead. Now, let's make a modified version - eg2.jsp which (while still being pointless) at least requires a programming language to be pointless in:
-
Swap back into the webapps/ROOT subdirectory again by: pushd.
-
cp eg1.jsp eg2.jsp
-
pico eg2.jsp and change it so it reads:
<html> <body bgcolor="#ffffff"> <h1>Hello world!</h1> <ul> <% int i; for( i = 0; i < 20; i++ ) { out.println( "<br><li>Testing testing... "+i ); } %> </ul> </body> </html>
-
Swap dirs again by: pushd.
-
Start Tomcat once more: tomcat6 start
-
Point your web browser at: http://localhost:59999/eg2.jsp and you'll see that you get an itemized list of 20 messages.
-
Don't forget to tomcat6 stop, and then pushd to get back into the web app root for further editing.
An Example with remembered state - eg3.jsp
Even a simple JSP page, once translated into a servlet and loaded into memory by Tomcat, becomes a long-lived servlet instance which can store state information as instance variables. Here, we present a very simple example which adds a single instance variable:
static int n = 0;
In pure Java terms, all we need to do is to declare the appropriate variable inside the class before the methods start. But how do we do this in JSP when we're not writing the whole class? Using the <%! .. %> syntax.
-
Ok, check we're back in the web app root: if you've forgotten where you are with the directory stack, type dirs to show you the whole stack. Unless the first item is ~/example-context/webapps/ROOT, type pushd to swap back.
-
Create eg3.jsp with contents:
<%! public static int n = 0; %> <h1>Example With State</h1> <% out.println("<p><b>I remember, n="+n+"</b>"); n++; %>
-
Swap dirs again, and tomcat6 start as usual.
-
Point your web browser at: http://localhost:59999/eg3.jsp and it'll say I remember, n=0
- Still in the web browser, reload the jsp page several times, each time you will see the counter increment. It's not only Elephants that can remember, Tomcats can too:-)
-
This counter will be only be reset if you restart Tomcat. tomcat6 stop followed by tomcat6 start, then reload the page in your web browser.
-
Finally, tomcat6 stop, and pushd to get back into the web app root for further editing.
Importing Modules into JSP
Often, in JSP code, you want to be able to import some java module namespaces, just like you do in ordinary Java code. The only issue in JSP is how you mark the import declaration so that the Java servlet that is generated from the JSP file places the import declaration into a syntactically valid place.
Suppose, for example, that your code needs to do:
import java.sql.*;
To do this in JSP, add the following as the first line in the .jsp file:
<%@ page language="java" import="java.sql.*"%>
Using JDBC in JSP
See our separate JDBC with DoC Supported Databases document for a single worked example (Films and directors) shown in multiple different forms - including a pair of JSP versions, one connecting to Postgres and the other to Microsoft SQL Server. Specifically, Postgres.jsp and MSSQL.jsp
Let's try to get them both working in Personal Tomcat, it's simple. Proceed as follows:
- As usual, check you're back in the web app root via dirs and (if necessary) pushd.
-
Take a copy of both of the above files into the web app root, either by viewing each JSP file via the web, then cutting and pasting it's text into an example, or by using wget to fetch each file, as in wget http://www.doc.ic.ac.uk/csg-res/static/jdbc/Postgres.jsp or (most neatly) by using your DoC-specific knowledge of where the CSG web pages live in Unix filesystem terms, so we can copy both files straight in by:
-
cp /vol/www/csg-res/static/jdbc/*.jsp
-
- Swap dirs again, and start Tomcat.
-
Point your web browser at: http://localhost:59999/Postgres.jsp and you will see the Postgres example run, similarly http://localhost:59999/MSSQL.jsp will run the Microsoft SQL server example.
- Finally, stop tomcat, and swap back into the web app root for further editing.
Creating Java Beans - and using them from JSP
A Java Bean is basically a reusable Java component - a precompiled Java class - typically offering get/set methods to manipulate instance variables and/or external connections, which can be used from a Java Servlet or JSP page. Java Bean classes must be compiled and are usually packaged into a .jar file with an accompanying manifest file, and then the .jar file is placed in the WEB-INF/lib directory in a servlet context in order for Tomcat to find it.
Let's look at a very simple example of a Bean class, how to package it, and how to use it from a JSP page. It's good practice to put Java source code (for the bean itself, or indeed servlets) in a logically separate src subdirectory, so let's create one and create the bean:
- This time, check you're still in the top level of the example-context directory, via dirs and (if necessary) pushd to swap back.
-
An empty src directory already exists at the top-level, so enter it (temporarily) via: pushd src. Note that the directory stack is 3 deep now.
-
Inside the src directory, create a dcwbeans directory via mkdir dcwbeans and create the file bean1.java inside dcwbeans containing the following:
package dcwbeans; public class bean1 { private String name = "wibble"; public void setName( String n ) { name = n; } public String getName() { return name; } public String stars( int n ) { String s = ""; while( n-- > 0 ) { s += "*"; } return s; } }
-
Create the following Makefile which has rules to compile and jar this java class into bean1.jar and to install the finished jar into the live context directory:
CONTEXT = ../webapps/ROOT DIR = $(CONTEXT)/WEB-INF/lib JARS = bean1.jar all: $(JARS) clean: /bin/rm -f $(JARS) */*.class install: $(JARS) install -m644 $(JARS) $(DIR) bean1.jar: dcwbeans/bean1.class bean1.mf jar cfm bean1.jar bean1.mf dcwbeans/bean1.class dcwbeans/bean1.class: bean1.java javac -d . bean1.java
-
You also have to create a stupid little manifest file called bean1.mf containing:
Name: dcwbeans/bean1.class Java-Bean: True
-
Compile and jar up your bean via make and install it into WEB-INF/lib via make install.
- Your bean is now ready for use.
-
Leave the src directory by popd. Swap back into the web app root as normal by pushd.
-
Create eg4.jsp containing the following:
<html> <body> <%-- Create an instance of the bean --%> <jsp:useBean id="b" class="dcwbeans.bean1"/> <table> <tr> <th>b.name is </th> <td><jsp:getProperty name="b" property="name"/> </td> </tr> <tr> <th>modifying b.name</th> <td><jsp:setProperty name="b" property="name" value="wobble"/> value changed</td> </tr> <tr> <th>b.name is now</th> <td><jsp:getProperty name="b" property="name"/> </td> </tr> </table> </body> </html>
-
What does this mean? Briefly, the jsp:useBean tag causes an instance of the bean class to be created, which is referred to as b in the jsp code; the jsp:getProperty tag allows you to retrieve the value of a named property in a named bean - as in:
<jsp:getProperty name="b" property="name"/>
-
which retrieves the name property of bean b. Effectively, this becomes a method call to b.getName().
-
Similarly, the jsp:setProperty tag allows you to override the value of a named property in a named bean, effectively becoming a method call to b.setName( value ).
- Swap dirs again, and start Tomcat.
-
Point your web browser at: http://localhost:59999/eg4.jsp and you will see the example run. Initially, the bean's name property was wibble - this was the default value in the Java bean code. Then the JSP script itself set the bean's name to wobble. Finally, the bean's name property was extracted - and this time, sure enough, it was wobble.
- Finally, stop tomcat, and swap back into the web app root for further editing.
More about Beans
-
In addition to get/set method pairs that are automatically accessed via jsp:getProperty and jsp:setProperty tags, a bean class may define any number of additional methods, which can be accessed directly from a JSP file by a chunk of JSP code like:
<%= b.stars(20) %>
-
As long as the bean class that b is a member of contains a method String stars( int ), this method will be called at this point. Obviously, it would be an error if no such method existed. Try adding the above stars call somewhere suitable in eg4.jsp, restart Tomcat, and check that the row of stars appear where you expected. Don't forget to stop Tomcat.
-
Finally, looking back at the above Makefile, all that mucking about with building a manifest file and compiling and constructing the jar file is very repetitive and dull. I have written a simple perl script which generates the manifest file, compiles the java file and constructs the jar file for you. It's called /homes/dcw/bin/mkbean. If you'd like to use it, the example-context/src/Makefile simplifies down to:
-
CONTEXT = ../webapps/ROOT DIR = $(CONTEXT)/WEB-INF/lib JARS = bean1.jar MKBEAN = /homes/dcw/bin/mkbean all: $(JARS) clean: /bin/rm -f $(JARS) */*.class install: $(JARS) install -m644 $(JARS) $(DIR) bean1.jar: bean1.java $(MKBEAN) bean1
-
Using this framework, adding a second bean (bean2.jar for example) would require changing the JARS line to:
JARS = bean1.jar bean2.jar
- and appending the following rule at the end of the Makefile, leaving a blank line between the bean1 rule and the new bean2 rule:
bean2.jar: bean2.java $(MKBEAN) bean2
- Apparently, Java Beans are an enormously important part of the future of Software Engineering, the best thing since pre-frozen yak butter, etc etc, especially when someone sticks the word Enterprise on the front. So not yet another irritating Java silliness then! (Of course, it's possible that there may be more to Java Beans than I have entirely grasped:-) Your mileage may vary.)
Locating the Servlets auto-generated from JSP
-
Behind the scenes, Tomcat turns a JSP script (the first time it sees it) into a full blown Java servlet, compiles it, loads it into the Tomcat JVM, and then runs the servlet's doGet method. When that same JSP file is accessed again, Tomcat simply calls the servlet's doGet method. It is often useful and instructive to look at the Servlet code that Tomcat generates from your JSP scripts. These are stored in the example-context/work directory. When I just looked, eg1.jsp was turned into work/Catalina/localhost/_/org/apache/jsp/eg1_jsp.java.
-
Now, after all these JSP pages and Beany thingies, you'd probably like to get stuck into some proper servlets to see what's behind the cute (but irritating) JSP system.