This lab is a continuation of Lab 4. You will add user logins and sessions to the web application that you started in that lab. You do not need to have completed the last section of Lab 4, "On Your Own," before working on Lab 5. Your work for labs 4 and 5 will be due during the week after Spring Break. (You will get instructions later about how to turn it in.)
In the polls web site from Lab 4, the user has to enter a user name and password in the form for creating a poll. You will modify this so that a user logs in on using a separate form and then can create as many polls as they want, without entering their user name and password again. (In a real application, there would of course be lots of more interesting things for a logged-in user to do.) To implement this, you have to keep track of the user from one page request to another, and for that you need sessions.
The idea is that some attribute of the session object can be used to store information about the logged-in user. In this simple case, we can use a String-valued attribute to save the logged-in user's name. (More generally, we might use some sort of UserData object.) Any JSP or servlet that needs to check for a logged-in user can test the value of this attribute.
Step 1: A Login Page. Start by creating a new JSP, Login.jsp, where the user can log in. You can use this simple HTML form on the page:
<form action="ProcessLogin" method="POST"> <p>Username: <input type="text" name="username"></p> <p>Password: <input type="password" name="password"></p> <p><input type="submit" value="Click Here to Log In"></p> </form>
Step 2: A Login Servlet. Next, create a servlet named ProcessLogin to process the data from the form. You will have to rewrite the processRequest method. (You can get some ideas from the MakePoll servlet.) You can use the method call "DataAccess.checkUser(context, user, password)" to check that the user is legal, where user is obtained by calling request.getParameter("user") and password is obtained by calling request.getParameter("password"). If the user name and password are valid, you can forward the user to index.jsp, with a message such as "You have been successfully logged in." If not, you can return the user to the login page.
The whole point of logging in is to save information about the logged-in user in the session object. Using "loggedin" as the name of the session attribute, you can do this by saying:
HttpSession session = request.getSession(); session.setAttribute("loggedin", user);
Step 3: Using the login info. The JSP page index.jsp has input boxes for user name and password. These are no longer necessary, since the user will log in once and for all on another page. Remove the unnecessary input boxes from index.jsp.
The MakePoll servlet, which processes the form submission from index.jsp, also has to be modified. It is no longer responsible for validating the user, but it should still check that there really is a validated user associated with the session. It can use session.getAttribute("loggedin") to test for this (where session is obtained as request.getSession()). If there is no validated user, the user should be redirected back to the login page. (See the next section.)
Forwarding is often used in a Java web application to allow several application components (servlets/JSPs/HTMLs) to participate in processing a request and creating the response that will be sent back to the user. Sometimes, however, a redirect is more appropriate. When using forward, you can transport information from one application component to another using request.setAttribute(). You can't do this with forward, but you can use session.setAttribute() for the same purpose (assuming that you are redirecting to another page in the same web application). When you use attributes in this way, it's a good idea to remove the relevant attributes from the session after their values have been read.
Step 4: Redirects to the Login Page. If someone tries to access index.jsp or MakePoll without being logged in, they should be redirected to the login page. You can test this by checking for the presence of the "loggedin" attribute in the session. If the value is null, you can do the redirect with
response.sendRedirect("/polls/Login.jsp"); return; // Stop processing after sending the redirect!!
Here, polls has to be the name of the web application, and "/polls/Login.jsp" is an ordinary relative URL of the login page.
You should display a message such as "Login required" on the login page when this happens. Since you are using a redirect, you want to store the message in a session attribute before sending the redirect. On the login page, you can test for the presence of this attribute in the session; if it is present, you can display it on the page. After displaying the message, remove the attribute from the session. See index.jsp for an example that uses a request attribute instead of a session attribute.
The session object can be used to store data between page requests in the same session. It is also possible to store data that is accessible from anywhere in a web application. This is data that is shared by all sessions. The data can be stored as attributes of the application object (available as the predefined variable application in a JSP and as getServletContext()in a servlet). As an example, you will use an attribute of the application object to keep track of the number of sessions that are currently active in your web application. To do this, use a session listener, which will be informed every time a session is created or destroyed.
Step 5: Creating a listening object. A session listener is an object of type HttpSessionListener. To listen for session events, you have to create a class that implements this listener. Create a new Java class in your web application with name SessionHandler in package listeners. Here is the code for the listener, which you can copy into your class.
package listeners; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * Keeps track of the number of sessions, and logs session creation and * destruction. Methods are synchronized to prevent errors due to * multithreading. */ public class SessionHandler implements HttpSessionListener { synchronized public void sessionCreated(HttpSessionEvent e) { ServletContext application = e.getSession().getServletContext(); if (application.getAttribute("sessioncount") == null) application.setAttribute("sessioncount", 0); int ct = (Integer)application.getAttribute("sessioncount"); ct++; application.setAttribute("sessioncount", ct); application.log("Session created with id = " + e.getSession().getId() + ". There are " + ct + " active sessions."); } synchronized public void sessionDestroyed(HttpSessionEvent e) { ServletContext application = e.getSession().getServletContext(); int ct = (Integer)application.getAttribute("sessioncount"); ct--; application.setAttribute("sessioncount", ct); application.log("Session destroyed with id = " + e.getSession().getId() + ". There are " + ct + " active sessions."); } }
Step 6: Register the Listener. Next, you have to tell the server to register the listener with your application. To do this, you have to add configuration information to the file web.xml, which can be found in the WEB-INF directory of your application. Find this file and double-click on it to open it. You will see a specialized editor that understands the format of the file and has interface widgets for changing all the configuration options.
You want to add a "Web Application Listener." The widget for doing this is in the "General" Pane, which should be visible when the file is first opened. Click on the "+" next to "Web Application Listeners", then click the "Add" button. In the dialog box, enter the full name of your class, listeners.SessionHandler, in the "Listener Class" box (or click "Browse" and select the class from the list). Click OK. That's all there is to it -- you should see the class listed as a "Listener Class" under "Web Application Listeners". (If you want to see the raw XML configuration, go to the "XML" pane in the editor window.)
Note that you might want to set the "Session Timeout" in the "General" pane to a short value, to make it easier to test session destruction.
When you run your application after configuring the listener, you will see messages in the Apache Tomcat Log whenever a session is created or destroyed. You can find the log in the bottom section of the NetBeans window.
Step 7: Use the Application Data. The application listener adds an attribute to the application object to store the number of sessions. The name of the attribute is "sessioncount", and you can retrieve the session count on a JSP page as:
int sessionCount = (Integer)application.getAttribute("sessioncount");
or more simply as: <%= application.getAttribute("sessioncount") >
Use this to display the current number of users on the page Login.jsp. (The number will include the person who is logging in.)
To test your work with multiple sessions, you can access your web application from two different web browsers.
Tomcat comes with a "manager" web application that can be used to start, stop, and reload individual web applications. It can also be used to expire sessions, which can be useful during testing. You need a user name and password to log into the manager application. You can find them in the file
.netbeans/6.5/apache-tomcat-6.0.18_base/conf/tomcat-users.xml
Note the "." at the beginning of the name; this is a hidden file in your home directory. The username and password are in a line that looks something like this:
<user username="ide" password="3z8InR4K" roles="manager,admin"/>
You can use the username and password from this line to log into the manager application, or you can edit the file and add another line, with an easier-to-remember username and password and with roles="manager".
When tomcat is running on your local computer, the manager application can be accessed at http://localhost:8084/manager/html
To end the sessions in your web application, find the "Expire Sessions" button for your application on the manager web page. Enter 0 as the idle time in the box next to the button, and click the button.