Marc Hauschildt
Web Technologies and Computer Software Development Instructor at Kirkwood Community College in Cedar Rapids, IA.
Week 2
In the "WEB-INF/" folder, create a new file called top-nav.jsp. Add this HTML code.
<div class="container">
<nav class="navbar navbar-expand-lg bg-body-tertiary rounded" aria-label="Eleventh navbar example">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample09" aria-controls="navbarsExample09" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExample09">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" aria-disabled="true">Disabled</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li>
</ul>
<form role="search">
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
</form>
<div class="d-lg-flex col-lg-3 justify-content-lg-end">
<a href="#" class="btn btn-outline-primary me-2">Login</a>
<a href="#" class="btn btn-primary">Sign-up</a>
</div>
</div>
</div>
</nav>
</div>
In the "WEB-INF/" folder, create a new file called bottom-nav.jsp. Add this HTML code.
<div class="container">
<footer class="py-5">
<div class="row">
<div class="col-6 col-md-2 mb-3">
<h5>Section</h5>
<ul class="nav flex-column">
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Home</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Features</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Pricing</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">FAQs</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">About</a></li>
</ul>
</div>
<div class="col-6 col-md-2 mb-3">
<h5>Section</h5>
<ul class="nav flex-column">
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Home</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Features</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Pricing</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">FAQs</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">About</a></li>
</ul>
</div>
<div class="col-6 col-md-2 mb-3">
<h5>Section</h5>
<ul class="nav flex-column">
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Home</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Features</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">Pricing</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">FAQs</a></li>
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-body-secondary">About</a></li>
</ul>
</div>
<div class="col-md-5 offset-md-1 mb-3">
<form>
<h5>Subscribe to our newsletter</h5>
<p>Monthly digest of what's new and exciting from us.</p>
<div class="d-flex flex-column flex-sm-row w-100 gap-2">
<label for="newsletter1" class="visually-hidden">Email address</label>
<input id="newsletter1" type="text" class="form-control" placeholder="Email address">
<button class="btn btn-primary" type="button">Subscribe</button>
</div>
</form>
</div>
</div>
<div class="d-flex flex-column flex-sm-row justify-content-between py-4 my-4 border-top">
<p>© 2024 Company, Inc. All rights reserved.</p>
<ul class="list-unstyled d-flex">
<li class="ms-3"><a class="link-body-emphasis" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#twitter"/></svg></a></li>
<li class="ms-3"><a class="link-body-emphasis" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#instagram"/></svg></a></li>
<li class="ms-3"><a class="link-body-emphasis" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#facebook"/></svg></a></li>
</ul>
</div>
</footer>
</div>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<%@ taglib prefix="fn" uri="jakarta.tags.functions" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${pageTitle}</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body>
<%@ include file="/WEB-INF/top-nav.jsp" %>
<%@ include file="/WEB-INF/bottom-nav.jsp" %>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
<script
src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>
</body>
</html>
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("")
public class HomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("pageTitle", "Home");
req.getRequestDispatcher("WEB-INF/home.jsp").forward(req, resp);
}
}
<%@ include file="/WEB-INF/top.jsp" %>
<%@ include file="/WEB-INF/bottom.jsp" %>
<main>
<div id="myCarousel" class="carousel slide mb-6" data-bs-ride="carousel">
<div class="carousel-indicators">
<button type="button" data-bs-target="#myCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
<button type="button" data-bs-target="#myCarousel" data-bs-slide-to="1" aria-label="Slide 2"></button>
<button type="button" data-bs-target="#myCarousel" data-bs-slide-to="2" aria-label="Slide 3"></button>
</div>
<div class="carousel-inner">
<div class="carousel-item active">
<svg class="bd-placeholder-img" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" preserveAspectRatio="xMidYMid slice" focusable="false"><rect width="100%" height="100%" fill="var(--bs-secondary-color)"/></svg>
<div class="container">
<div class="carousel-caption text-start">
<h1>Example headline.</h1>
<p class="opacity-75">Some representative placeholder content for the first slide of the carousel.</p>
<p><a class="btn btn-lg btn-primary" href="#">Sign up today</a></p>
</div>
</div>
</div>
<div class="carousel-item">
<svg class="bd-placeholder-img" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" preserveAspectRatio="xMidYMid slice" focusable="false"><rect width="100%" height="100%" fill="var(--bs-secondary-color)"/></svg>
<div class="container">
<div class="carousel-caption">
<h1>Another example headline.</h1>
<p>Some representative placeholder content for the second slide of the carousel.</p>
<p><a class="btn btn-lg btn-primary" href="#">Learn more</a></p>
</div>
</div>
</div>
<div class="carousel-item">
<svg class="bd-placeholder-img" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" preserveAspectRatio="xMidYMid slice" focusable="false"><rect width="100%" height="100%" fill="var(--bs-secondary-color)"/></svg>
<div class="container">
<div class="carousel-caption text-end">
<h1>One more for good measure.</h1>
<p>Some representative placeholder content for the third slide of this carousel.</p>
<p><a class="btn btn-lg btn-primary" href="#">Browse gallery</a></p>
</div>
</div>
</div>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#myCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#myCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
<!-- Marketing messaging and featurettes
================================================== -->
<!-- Wrap the rest of the page in another container to center all the content. -->
<div class="container marketing">
<!-- Three columns of text below the carousel -->
<div class="row">
<div class="col-lg-4">
<svg class="bd-placeholder-img rounded-circle" width="140" height="140" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="var(--bs-secondary-color)"/></svg>
<h2 class="fw-normal">Heading</h2>
<p>Some representative placeholder content for the three columns of text below the carousel. This is the first column.</p>
<p><a class="btn btn-secondary" href="#">View details »</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4">
<svg class="bd-placeholder-img rounded-circle" width="140" height="140" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="var(--bs-secondary-color)"/></svg>
<h2 class="fw-normal">Heading</h2>
<p>Another exciting bit of representative placeholder content. This time, we've moved on to the second column.</p>
<p><a class="btn btn-secondary" href="#">View details »</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4">
<svg class="bd-placeholder-img rounded-circle" width="140" height="140" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="var(--bs-secondary-color)"/></svg>
<h2 class="fw-normal">Heading</h2>
<p>And lastly this, the third column of representative placeholder content.</p>
<p><a class="btn btn-secondary" href="#">View details »</a></p>
</div><!-- /.col-lg-4 -->
</div><!-- /.row -->
<!-- START THE FEATURETTES -->
<hr class="featurette-divider">
<div class="row featurette">
<div class="col-md-7">
<h2 class="featurette-heading fw-normal lh-1">First featurette heading. <span class="text-body-secondary">It’ll blow your mind.</span></h2>
<p class="lead">Some great placeholder content for the first featurette here. Imagine some exciting prose here.</p>
</div>
<div class="col-md-5">
<svg class="bd-placeholder-img bd-placeholder-img-lg featurette-image img-fluid mx-auto" width="500" height="500" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: 500x500" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="var(--bs-secondary-bg)"/><text x="50%" y="50%" fill="var(--bs-secondary-color)" dy=".3em">500x500</text></svg>
</div>
</div>
<hr class="featurette-divider">
<div class="row featurette">
<div class="col-md-7 order-md-2">
<h2 class="featurette-heading fw-normal lh-1">Oh yeah, it’s that good. <span class="text-body-secondary">See for yourself.</span></h2>
<p class="lead">Another featurette? Of course. More placeholder content here to give you an idea of how this layout would work with some actual real-world content in place.</p>
</div>
<div class="col-md-5 order-md-1">
<svg class="bd-placeholder-img bd-placeholder-img-lg featurette-image img-fluid mx-auto" width="500" height="500" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: 500x500" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="var(--bs-secondary-bg)"/><text x="50%" y="50%" fill="var(--bs-secondary-color)" dy=".3em">500x500</text></svg>
</div>
</div>
<hr class="featurette-divider">
<div class="row featurette">
<div class="col-md-7">
<h2 class="featurette-heading fw-normal lh-1">And lastly, this one. <span class="text-body-secondary">Checkmate.</span></h2>
<p class="lead">And yes, this is the last block of representative placeholder content. Again, not really intended to be actually read, simply here to give you a better view of what this would look like with some actual content. Your content.</p>
</div>
<div class="col-md-5">
<svg class="bd-placeholder-img bd-placeholder-img-lg featurette-image img-fluid mx-auto" width="500" height="500" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: 500x500" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="var(--bs-secondary-bg)"/><text x="50%" y="50%" fill="var(--bs-secondary-color)" dy=".3em">500x500</text></svg>
</div>
</div>
<hr class="featurette-divider">
<!-- /END THE FEATURETTES -->
</div><!-- /.container -->
</main>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
Create a new servlet in the called Signup. Add this code.
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/signup")
public class Signup extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("pageTitle", "Sign up for an account");
req.getRequestDispatcher("WEB-INF/signup.jsp").forward(req, resp);
}
}
<a href="signup" class="btn btn-primary">Sign-up</a>
<%@ include file="/WEB-INF/top.jsp" %>
<%@ include file="/WEB-INF/bottom.jsp" %>
<div class="container col-xl-10 col-xxl-8 px-4 py-5">
<div class="row align-items-center g-lg-5 py-5">
<div class="col-lg-7 text-center text-lg-start">
<h1 class="display-4 fw-bold lh-1 text-body-emphasis mb-3">Vertically centered hero sign-up form</h1>
<p class="col-lg-10 fs-4">Below is an example form built entirely with Bootstrap’s form controls. Each required form group has a validation state that can be triggered by attempting to submit the form without completing it.</p>
</div>
<div class="col-md-10 mx-auto col-lg-5">
<form class="p-4 p-md-5 border rounded-3 bg-body-tertiary">
<div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingInput" placeholder="name@example.com">
<label for="floatingInput">Email address</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="floatingPassword" placeholder="Password">
<label for="floatingPassword">Password</label>
</div>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign up</button>
<hr class="my-4">
<small class="text-body-secondary">By clicking Sign up, you agree to the terms of use.</small>
</form>
</div>
</div>
</div>
Add form attributes.
<form method="post" action="signup">
Add name attributes to all form inputs that match the ids.
id="email" name="email"
id="password1" name="password1"
id="password2" name="password2"
Add a name and value attribute to the checkbox.name="termsOfUse" value="agree"
Create a doPost method to get the parameters from the form submission.
String email = req.getParameter("email");
String password1 = req.getParameter("password1");
String password2 = req.getParameter("password2");
String[] termsOfService = req.getParameterValues("termsOfUse");
Immediately set those values as attributes. Note the ternary operator being used to set the termsOfUse attribute to "agree" only if the checkbox was checked.
req.setAttribute("email", email);
req.setAttribute("password1", password1);
req.setAttribute("password2" password2);
req.setAttribute("termsOfUse", (termsOfUse != null && termsOfUse[0].equals("agree")) ? "agree" : "");
Create a processRequest method to the title and forward to the JSP.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
processRequest(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// code omitted
processRequest(req, resp);
}
protected void processRequest(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setAttribute("pageTitle", "Sign up for an account");
req.getRequestDispatcher("WEB-INF/signup.jsp").forward(req, resp);
}
Use expressions in value attributes to get attributes from the servlet
value="${email}"
value="${password1}"
value="${password2}"
Use a c:if tag inside in the checkbox input to re-select it.
<c:if test = "${termsOfUse == "agree"}">checked</c:if>
Run Tomcat. When you enter values and press submit, the values should remain in the form.
Add a method to the Validators class to validate a strong password.
public static boolean isStrongPassword(String password) {
// Password requires 8 characters with at least 3 out 4 (uppercase letter, lowercase letter, number, special character ~`!@#$%^&*()_-+={}[]|\:;"'<>,.?/
Pattern passwordPattern = Pattern.compile("^(?:(?=.*\\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))(?!.*(.)\\1{2,})[A-Za-z0-9~`!\\@#\\$%\\^&*()_\\-+={}\\[\\]\\|\\\\:;\"'<>,.?\\/]{8,128}$");
Matcher matcher = pattern.matcher(passwordStr);
return matcher.matches();
}
Set the email and password only if they are valid.
The setPassword method will allow null values because we will set the password to null after it has been validated.
public void setEmail(String email) {
if (!Validators.isValidEmail(email)) {
throw new IllegalArgumentException("Invalid email address");
}
this.email = email;
}
public void setPassword(char[] password) {
if (password != null) {
String passwordStr = String.valueOf(password);
if (!Validators.isStrongPassword(passwordStr)) {
throw new IllegalArgumentException("Password requires 8 characters with at least 3 out 4 (uppercase letter, lowercase letter, number, special character ~`!@#$%^&*()_-+={}[]|\\:;\"'<>,.?/");
}
}
this.password = password;
}
After setting the attributes in the doPost method, create a User object and validate all of the inputs.
User user = new User();
boolean errorFound = false;
try {
user.setEmail(email);
} catch(IllegalArgumentException e) {
req.setAttribute("emailError", e.getMessage());
errorFound = true;
}
try {
user.setPassword(password1.toCharArray());
} catch(IllegalArgumentException e) {
req.setAttribute("password1Error", e.getMessage());
errorFound = true;
}
if(password2.equals("")) {
req.setAttribute("password2Error", "This input is required");
errorFound = true;
}
if(!password1.equals(password2)) {
req.setAttribute("password2Error", "Passwords don't match");
errorFound = true;
}
if(termsOfService == null || !termsOfService[0].equals("agree")){
req.setAttribute("termsOfUseError", "You must agree to our terms of use");
errorFound = true;
}
Use c:if tags with expressions to display error messages at the end of each input group.
<c:if test="${not empty emailError}"><div class="invalid-feedback">${emailError}</div></c:if>
<c:if test="${not empty password1Error }"><div class="invalid-feedback">${password1Error}</div></c:if>
<c:if test="${not empty password2Error }"><div class="invalid-feedback">${password2Error}</div></c:if>
<c:if test="${not empty termsOfUseError }"><div class="invalid-feedback">${termsOfUseError}</div></c:if>
Use c:if tags with expressions in the form input class attributes.
class="form-control <c:if test="${not empty emailError}">is-invalid</c:if> "
class="form-control <c:if test="${not empty password1Error }">is-invalid</c:if> "
class="form-control <c:if test="${not empty password2Error }">is-invalid</c:if> "
class="form-check-input <c:if test="${not empty termsOfServiceError}">is-invalid</c:if>
Run the project and test invalid inputs.
CREATE PROCEDURE sp_get_user(IN p_email VARCHAR(255))
BEGIN
SELECT user_id, first_name, last_name, email, phone, password, language, status, privileges, created_at
FROM user
WHERE email = p_email;
END;
public static User get(String email) {
User user = null;
try (Connection connection = getConnection()) {
if (connection != null) {
try (CallableStatement statement = connection.prepareCall("{CALL sp_get_user(?)}")) {
statement.setString(1, email);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
int id = resultSet.getInt("user_id");
String firstName = resultSet.getString("first_name");
String lastName = resultSet.getString("last_name");
String phone = resultSet.getString("phone");
char[] password = resultSet.getString("password").toCharArray();
String language = resultSet.getString("language");
String status = resultSet.getString("status");
String privileges = resultSet.getString("privileges");
Instant created_at = resultSet.getTimestamp("created_at").toInstant();
user = new User(id, firstName, lastName, email, phone, password, language, status, privileges, created_at);
}
}
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return user;
}
if(UserDAO.get(email) != null) {
req.setAttribute("emailError", "A user with that email already exists. <a href=\"signin\">Sign in</a> or <a href=\"reset-password\">reset the password</a>.");
errorFound = true;
}
CREATE PROCEDURE sp_update_user(
IN p_user_id int,
IN p_first_name VARCHAR(255),
IN p_last_name VARCHAR(255),
IN p_email VARCHAR(255),
IN p_phone VARCHAR(255),
IN p_language VARCHAR(255),
IN p_status VARCHAR(255),
IN p_privileges VARCHAR(255)
)
BEGIN
UPDATE user
SET first_name = p_first_name,
last_name = p_last_name,
email = p_email,
phone = p_phone,
language = p_language,
status = p_status,
privileges = p_privileges
WHERE user_id = p_user_id;
END
public static boolean update(User user) {
try(Connection connection = getConnection();
CallableStatement statement = connection.prepareCall("{CALL sp_update_user(?,?,?,?,?,?,?,?)}")
) {
statement.setInt(1, user.getId());
statement.setString(2, user.getFirstName());
statement.setString(3, user.getLastName());
statement.setString(4, user.getEmail());
statement.setString(5, user.getPhone());
statement.setString(6, user.getLanguage());
statement.setString(7, user.getStatus());
statement.setString(8, user.getPrivileges());
int rowsAffected = statement.executeUpdate();
return rowsAffected == 1;
} catch(SQLException e) {
System.out.println(e.getMessage());
return false;
}
}
public static void add(User user) {
try (Connection connection = getConnection()) {
if (connection != null) {
try (CallableStatement statement = connection.prepareCall("{CALL sp_add_user(?, ?)}")) {
statement.setString(1, user.getEmail());
String encryptedPassword = BCrypt.hashpw(new String(user.getPassword()), BCrypt.gensalt(12));
statement.setString(2, encryptedPassword);
statement.executeUpdate();
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (!errorFound) {
try {
UserDAO.add(user);
req.setAttribute("userAddSuccess", "User added");
} catch (RuntimeException e) {
req.setAttribute("userAddFail", "User not added");
}
}
<c:if test="${not empty userAddSuccess}">
<div class="alert alert-success mb-2" role="alert">${userAddSuccess}</div>
</c:if>
<c:if test="${not empty userAddFail}">
<div class="alert alert-danger mb-2" role="alert">${userAddFail}</div>
</c:if>
Test out the form. If you get a success message go to MySQL workbench and run: SELECT * FROM users;
To test the fail message, create a user with the same email address.
If you add two users with different email addresses but the same password, you will notice their hashed passwords are different.
<c:choose>
<c:when test="${not empty flashMessageSuccess}">
<div class="alert alert-success mb-2" role="alert">
${flashMessageSuccess}
</div>
<c:remove var="flashMessageSuccess" scope="session" />
</c:when>
<c:when test="${not empty flashMessageDanger}">
<div class="alert alert-danger mb-2" role="alert">
${flashMessageDanger}
</div>
<c:remove var="flashMessageDanger" scope="session" />
</c:when>
<c:when test="${not empty flashMessageWarning}">
<div class="alert alert-warning mb-2" role="alert">
${flashMessageWarning}
</div>
<c:remove var="flashMessageWarning" scope="session" />
</c:when>
</c:choose>
HttpSession session = req.getSession(); // Get existing session
session.invalidate(); // Remove all existing session attributes
session = req.getSession(); // Create new session
user.setStatus("active");
UserDAO.update(user);
user.setPassword(null);
session.setAttribute("activeUser", userFromDatabase);
session.setAttribute("flashMessageSuccess", "Welcome!");
resp.sendRedirect(req.getContextPath()); // Redirects to the home page
return;
req.setAttribute("userAddSuccess", "User added");
Delete any previous database records before continuing.
DELETE FROM user;
Run the program. Create a new user. If successful, you will be redirected to the home page and see a success flash message.
Run this SQL to confirm the user's account is active.
SELECT * FROM user;
Edit the Sign Up and Sign In buttons so they only display when the "activeUser" session variable is not set.
When "activeUser" is set, display the My Account and Sign Out buttons.
<div class="d-lg-flex col-lg-3 justify-content-lg-end">
<c:choose>
<c:when test="${empty sessionScope.activeUser}">
<a href="#" class="btn btn-outline-primary me-2">Login</a>
<a href="signup" class="btn btn-primary">Sign-up</a>
</c:when>
<c:otherwise>
<a href="#" class="btn btn-outline-secondary me-2">Edit Profile</a>
<a href="signout" class="btn btn-secondary">Sign out</a>
</c:otherwise>
</div>
Assuming the session was just created, and you are on the homepage with the flash message displaying, if you refresh the page the flash message will disappear and the Login/Sign-up buttons will become a Edit Profile/Sign uut buttons.
X
Create a new servlet called "SignoutServlet". Add this code.
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/signout")
public class SignoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(); // Get existing session
session.invalidate(); // Remove all existing session attributes
session = req.getSession(); // Create new session
session.setAttribute("flashMessageWarning", "You are logged out. See you next time!");
resp.sendRedirect(req.getContextPath()); // Redirects to the home page
}
}
CREATE PROCEDURE sp_get_user_by_id(IN p_user_id int)
BEGIN
SELECT user_id, first_name, last_name, email, phone, password, language, status, privileges, created_at
FROM user
WHERE user_id = p_user_id;
END
CREATE PROCEDURE sp_delete_user(IN p_user_id int)
BEGIN
DELETE FROM user WHERE user_id = p_user_id;
END;
By Marc Hauschildt
Web Technologies and Computer Software Development Instructor at Kirkwood Community College in Cedar Rapids, IA.