Java 3

Week 2

top-nav

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

bottom-nav

<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>&copy; 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>

top.jsp

  • In the "WEB-INF/" folder, create a new file called top.jsp.
    • This file will be used to define tag library declarations, the DOCTYPE tag, html tag, head tags, meta tags, title tag, link tags, and a top-nav include tag to be available to all JSPs.
  • Add tag library declarations (lines 2-4) at the beginning of top.jsp
  • A pageTitle attribute will eventually be set to each servlet.
  • Add Bootstrap and Bootstrap Icon CSS files.
    • You may add additional stylesheets to this file later.
<%@ 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" %>

taglib directives

  • The first directive states that all JSPs will contain HTML.
  • The second directive will activate the core (c) JSTL group.
    • Core tags provide logic and control flow support, URL management, and more.
  • The third directive tag will activate the formatting (fmt) JSTL group.
    • Formatting tags are used to format and display text, date/time, and numbers for internationalized Websites.
  • The fourth directive tag will activate the functions (fn) JSTL group.
    • Function tags are commonly used for string manipulation.
      We will discuss the fmt and fn JSTL libraries later.
  • We may add custom taglib directives later.

bottom.jsp

  • In the "WEB-INF/" folder, create a new file called bottom.jsp.
    • This file will be used to define script tags, and the closing body and html tags to be available to all JSPs.
  • Add Bootstrap and jQuery JavaScript files.
    • You may add additional JavaScript script tags to this file later.
  • The integrity and crossorigin attributes are used for Subresource Integrity (SRI) checking. This allows browsers to ensure that resources hosted on third-party servers have not been tampered with. Use of SRI is recommended as a best-practice, whenever libraries are loaded from a third-party source. Read more.
<%@ 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>

Home Servlet

  • In your project's "controllers/" package, create a new servlet called "HomeServlet".
  • Add the following code.
  • Note the URL value of the servlet is just an empty string ("")
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);
    }
}

Including files

  • Delete the index.jsp file found in the webapp folder.
  • Including JSPs in other JSP saves you from repeating the same code on every JSP.
  • Create a home.jsp file inside the WEB-INF folder.
  • Replace the content of home.jsp with the top and bottom include directives.
  • The include directive requires a file attribute. The file will be included at compile time.
<%@ include file="/WEB-INF/top.jsp" %>

<%@ include file="/WEB-INF/bottom.jsp" %>

Home Page Content

<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 &raquo;</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 &raquo;</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 &raquo;</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>

Custom CSS

  • Create a webapp/styles/ folder. Create a styles.css file. Add the styles from the Bootstrap Carousel demo page.
  • Add a new link tag to the head section of top.jsp
.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;
  }
}

Signup Servlet

  • 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);
    }
}
  • Edit the Sign-up hyperlink in the top-nav.jsp file
<a href="signup" class="btn btn-primary">Sign-up</a>

Including files

  • Create a signup.jsp file inside the WEB-INF folder.
  • Replace the content of with the top and bottom include directives.
<%@ include file="/WEB-INF/top.jsp" %>

<%@ include file="/WEB-INF/bottom.jsp" %>

Signup Form

  <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>
  • Create a second password form input for the user to confirm their password.
  • Change the "Remember Me" text to "By clicking Sign up, you agree to the terms of use."
  • Change the old "By clicking Sign up..." text to "Already have an account? <a href="login">Log in here</a>".

Signup Form

  • 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"

Signup Servlet doPost

  • 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" : "");

Signup Servlet processRequest

  • 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);
}
  • Call this method at the end of the doGet and doPost methods.
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);
}

signup.jsp

  • 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.

Validators

  • 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();
}

User class

  • 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;
}

Signup Servlet

  • 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;
}

signup.jsp

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

signup.jsp

  • 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.

Get User By Email

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;
  • Step 1: Create/Update a stored procedure
  • Step 2: The User POJO does not need to be updated.

UserDAO

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;
}
  • Step 3: Implement a method to get a user by their email address.

Signup Servlet

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;
}
  • Step 4: Add an if statement that checks if the user already exists. Here we get the context-parameter set in the web.xml file. If we are on https we use the Azure URL. If we are on http we use localhost.
  • Run the program. Try to sign up a user with the email address already added to your database.

Update User

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
  • Step 1: Create/Update a stored procedure
  • Note that this stored procedure does not update the user's password or created_at field.
    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;
        }
    }
  • Implement a method to update a User. 

UserDAO

pom.xml

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);
    }
}
  • Implement a method to add a User. 
  • The stored procedure was created in the Week 1 presentation.

UserDAO

if (!errorFound) {
    try {
        UserDAO.add(user);
        req.setAttribute("userAddSuccess", "User added");
    } catch (RuntimeException e) {
        req.setAttribute("userAddFail", "User not added");
    }
}
  • After checking if there are errors and before you forward the request and response to the servlet, if there are no errors, call the method to add a user to the database.

Signup Servlet

<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>
  • Add code before the form to display a message.

signup.jsp

  • 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>
  • In top.jsp, after including top-nav.jsp, add the following c:choose statement.
    • Success messages display green.
    • Danger messages display red.
    • Warning messages display yellow.
  • The c:remove statement is used to remove a session attribute after it is used.

Flash Message

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;
  • Replace this code...
    req.setAttribute("userAddSuccess", "User added");
  • with the following...

Signup Servlet

  • 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;

SQL

  • 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.

top-nav.jsp

<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

Sign in

  • Create a new servlet called "SignoutServlet". Add this code.

Signout Servlet

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

Get User By ID

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
  • Step 1: Create/Update a stored procedure

Delete User

CREATE PROCEDURE sp_delete_user(IN p_user_id int)
BEGIN
    DELETE FROM user WHERE user_id = p_user_id;
END;
  • Step 1: Create/Update a stored procedure