Java 3 - 2025

Week 2

2026

  • The Bootstrap template code didn't look good.
  • Change references to top.jsp to top.jspf

MongoDB

Azure SQL Server

Azure SQL Server

  • Open the SQL Server. Note your server admin name and password. Reset your password here if necessary.
  • Click the Networking link in the Security group.

Azure SQL Server

  • Enable Public network access on selected networks. 
  • Add a firewall rule to allow all IP Addresses (0.0.0.0 to 255.255.255.255)
  • Check the box to allow Azure services and resources to access this server.

Allow Connections at Kirkwood

  • Your IP address at Kirkwood will change depending on your location. Add the following firewall rules to include access anywhere on campus. 
  • Name: Kirkwood 1
    • start: 
    • end: 
  • Name: Kirkwood 2
    • start: 
    • end: 

Azure SQL Server

  • Click SQL Databases from the Settings section.
  • Click the name of your database.

Azure SQL Server

  • Click Connection strings in the Settings section.
  • Click the JDBC tab.
  • Copy the JDBC (SQL Authentication) string. Replace your password with {your_password_here}

Azure SQL Server

  • Open your web app's environment variables.
  • Add a new Application setting.
  • Title it something like "AZURE_SQL_CONNECTIONSTRING". Paste the connection string with password included.
  • Uncheck the Deployment slot setting.
  • Click Apply and Confirm.

Azure SQL Server

  • Open IntelliJ's Database tab. Add a new Azure SQL Database Resource.
  • Click the button to download missing drivers.
  • Enter your connection string in the URL field and click the Test Connection button.
  • If successful, click the OK button.
  • If not successful, try connecting from home.

Azure SQL Server

  • Add the Microsoft JDBC Driver for SQL Server dependency to your pom.xml file.
  • Add your connection string to the .env file with the key "AZURE_SQL_CONNECTIONSTRING"
  • In the shared package, create a class called "AzureSQL_Connect".
  • Copy the getConnection and main methods from the MySQL_Connect class. 
  • Change the driver line to this:
    Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
  • Change the name of the environment variable.
  • Run the main method to verify a successful connection.
  • Use the Microsoft SQL Server scripts from this page to create the Products, Orders, Vendors, Customers, and Order_Items tables.
  • Use an AI tool to convert the User table and stored procedures.
  • NOTE: Because this is a free database, it may go to sleep. You may get an error when accessing the data. Wait a few seconds and try again and it may work.

Delete Users

  • Run this query to delete one of the users. 

DELETE FROM user WHERE user_id = 2;
  • Refresh the local version of the website. It should display "There is 1 user".

  • Delete the last user. 

  • Refresh the local version of the website. It should display "There are 0 users".

Deploy to Azure

  • Commit/push changes to GitHub.

  • Visit "/users" on your live site. A 500 server error will occur.

  • Go to Azure and click your web app. In the left navigation, click Monitoring > App Service Logs.  

  • Turn on File System Application logging. 

  • Type 7 in the retention period field. Click Save.

  • Click Log Stream to find the .env file not found error.

    • If you don't see the error, click the Reconnect button.

  • Last semester, we built the war file from IntelliJ and deployed it to Azure through the Azure Toolbox plugin. This worked because IntelliJ has access to the .env file.

  • This semester, the GitHub Action builds the war file and deploys to Azure using continuous integration and continuous delivery/deployment (CI/CD). GitHub does not have access to the .env file because we ignored it in the .gitignore file.

Deployment Descriptor

  • In IntelliJ, open the src/main/webapp/WEB-INF/web.xml file.

  • This file is called the deployment descriptor. Its purpose is to provide instructions to the Jakarta EE web application server for deploying and running the web application.

  • Add this code between the <web-app> tags.

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <page-encoding>UTF-8</page-encoding>
        <include-prelude>/WEB-INF/top.jspf</include-prelude>
        <trim-directive-whitespaces>true</trim-directive-whitespaces>
        <default-content-type>text/html</default-content-type>
    </jsp-property-group>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <page-encoding>UTF-8</page-encoding>
        <include-coda>/WEB-INF/bottom.jspf</include-coda>
        <trim-directive-whitespaces>true</trim-directive-whitespaces>
        <default-content-type>text/html</default-content-type>
    </jsp-property-group>
</jsp-config>
  • The <jsp-config> element can contain 1 or more <jsp-property-group> elements.

  • Each group must define distinct <url-pattern> tags. In this example, we are telling Tomcat to apply the base.jspf file to the beginning of all files ending in .jsp anywhere in the application.

Deployment Descriptor

  • <page-encoding> indicates page encoding information. UTF-8 is the preferred encoding for e-mail and web pages.

    • UTF is backwards compatible with ASCII, results in fewer internationalization issues, has been implemented in all modern operating systems, and has been implemented in standards such as JSON.

  • <trim-directive-whitespaces> controls whether template text containing only white spaces must be removed from the response output. Without this, there will be a couple empty lines at the top of our HTML source code.

  • <default-content-type> Specifies the default content type. If the page directive does not include a contentType attribute, it specifies the default response content type.

  • Read about other deployment descriptor elements.

Context Parameters

  • Context Parameters can be added to the web.xml file to set global variables for the entire application.

    • Use environment variables for variables that need to be accessed by servlets, like passwords and API keys.

    • Use the web.xml file for variables that need to be accessed by JSPs.

<context-param>
    <param-name>appURLLocal</param-name>
    <param-value>http://localhost:8080/your_project_name_war_exploded</param-value>
</context-param>
<context-param>
    <param-name>appURLCloud</param-name>
    <param-value>https://your-app-name.azurewebsites.net</param-value>
</context-param>
  • Substitute the param-value tags with your URLs.

  • I would like you to remove the last "/" at the end of both URLs.

top.jspf

  • Create the top.jspf file in the WEB-INF folder.

    • jspf stands for Java Server Page Fragment

  • Add this code to include page and taglib directives, to set an "appURL" variable based on whether you are running the app locally or on Microsoft Azure, and to set doctype, html, head, meta, title, Bootstrap link and script, jQuery script, and body tags.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<fmt:setLocale value="en-US" />
<c:choose>
    <c:when test="${pageContext.request.serverName eq 'localhost' }">
        <c:set var="appURL" value="${initParam['appURLLocal']}"></c:set>
    </c:when>
    <c:otherwise>
        <c:set var="appURL" value="${initParam['appURLCloud']}"></c:set>
    </c:otherwise>
</c:choose>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <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">
  
  	<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
            crossorigin="anonymous"></script>
    <script defer src="https://code.jquery.com/jquery-3.7.1.min.js"
            integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
            crossorigin="anonymous"></script>
</head>
<body>

bottom.jspf

  • Create the bottom.jspf file in the WEB-INF folder

  • Add this code to close the body and html tags.

</body>
</html>
  • Remove the related code from admin-users.jsp. The deployment descriptor will now include it via base.jspf.

  • Remove the related code from admin-users.jsp. The deployment descriptor will now include it via base.jspf.

  • Temporarily add this to admin-user.jsp to see the value set.
    <h1>${appURL}</h1> 

  • Run the app and view the users page.

  • Note that the page title requires it to be set as a servlet request attribute.

Custom Error Pages

  • By default, Java web applications display detailed error messages that disclose the server version and detailed stack trace information that can, in some situations, display snippets of Java code. This information is a boon to hackers looking for as much information about their victims as possible.

  • Fortunately, it's very easy to configure web.xml to display custom error pages.

  • An error page will be displayed using the following configuration whenever the application responds with an HTTP 500 error.

  • You can add additional entries for other HTTP status codes.

    • 405 errors will occur when Azure fails to send emails.

<error-page>
    <error-code>404</error-code>
    <location>/fileNotFound</location>
</error-page>
<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/errorHandler</location>
</error-page>

ErrorHandler

  • In the "shared" package, create a new class called "ErrorHandler". 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 static jakarta.servlet.RequestDispatcher.*;
import java.io.IOException;

@WebServlet("/errorHandler")
public class ErrorHandler extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.setAttribute("pageTitle", "Error");
        req.getRequestDispatcher("WEB-INF/error.jsp").forward(req, resp);
    }
}

FileNotFound

  • In the "shared" package, create a new class called "FileNotFound". 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 static jakarta.servlet.RequestDispatcher.*;
import java.io.IOException;

@WebServlet("/fileNotFound")
public class FileNotFound extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.setAttribute("pageTitle", "File Not Found");
        req.getRequestDispatcher("WEB-INF/fileNotFound.jsp").forward(req, resp);
    }
}

ErrorHandler

  • In the "WEB-INF" folder, create a file called "error.jsp". Add this code.

<main>
    <div class="container pt-5">
        <div class="row">
            <div class="col-12 text-center">
                <h2>Oh no!</h2>
                <p class="mb-4">Something went wrong. We are sorry for the inconvenience.</p>
                <a href="${appURL}" class="btn btn-primary">Take me to the Homepage</a>
            </div>
        </div>
    </div>
</main>
  • In the "WEB-INF" folder, create a file called "fileNotFound.jsp". Add this code.

<main>
    <div class="container pt-5">
        <div class="row">
            <div class="col-12 text-center">
                <h1>404</h1>
                <h2>Page Not Found</h2>
                <p class="mb-4">The page you are looking for does not exist.</p>
                <a href="${appURL}" class="btn btn-primary">Take me to the Homepage</a>
            </div>
        </div>
    </div>
</main>

JSP Template

  • The JSP Template created in Java is no longer needed since we are now using top.jspf and bottom.jspf

  • Go to IntelliJ Preferences

  • Editor > File and Code Templates

  • Other tab

  • JSP files

  • Jsp File.jsp

  • Delete Content

2026

  • https://github.com/jackmobtv/mcpherson-movies/blob/master/src/main/java/net/mcphersonmovies/mcphersonmovies/controller/ERRORTEST.java
  • Admin Users Update/Delete

Test the Error Handlers

  • Add this code to the doGet method of the AdminUsers servlet to cause a server error.

String query = req.getParameter("q");
query.toString();
  • Run Tomcat locally. Visit "/users" to see the custom error page. Visit "/users2" to see the custom file not found page.

  • Remove the two lines of code from the doGet method that is causing the server error.

  • Commit and push the work to GitHub. Wait for the app to deploy and test the live website.

  • Test the "Take me to the Homepage" buttons. Note they should go to localhost when running Tomcat locally and they should go to your azurewebsites.net URL when running on the live server.

Test the Error Handlers

  • create a servlet called ErrorTest with 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("/err")
public class ErrorTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        throw new ServletException("Test Error");
    }
}
  • Send me a screencast showing how the email error feature works when visiting "/err" on the live Azure site.

  • https://github.com/Gwibbon/javaIII-literature-library/blob/main/src/main/java/edu/kirkwood/literaturelibrary/controller/ErrorHandler.java

ErrorHandler

String errorMsg = "<strong>Error code:</strong> " + req.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) + "<br>";
errorMsg += "<strong>Exception:</strong> " + req.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE) + "<br>";
errorMsg += "<strong>Message:</strong> " + req.getAttribute(RequestDispatcher.ERROR_MESSAGE); // Some Exceptions may not have messages
errorMsg += "<strong>Servlet:</strong> " + req.getAttribute(ERROR_SERVLET_NAME) + "<br>";
errorMsg += "<strong>Request URI:</strong> " + req.getAttribute(ERROR_REQUEST_URI) + "<br>";

req.setAttribute("errorMsg", errorMsg);
  • In web.xml, set a context-param for debugging.

<context-param>
    <param-name>debugging</param-name>
    <param-value>true</param-value>
</context-param>

ErrorHandler

  • Update error.jsp to conditionally display error messages based on the value of the debugging setting.

<c:choose>
    <c:when test="${initParam['debugging'] eq 'true'}">
        <p>${errorMsg}</p>
    </c:when>
    <c:otherwise>
        <h2>Oh no!</h2>
        <p class="mb-4">Something went wrong. We are sorry for the inconvenience.</p>
        <a href="${appURL}" class="btn btn-primary">Take me to the Homepage</a>
    </c:otherwise>
</c:choose>
  • Add this code to the doGet method of the AdminUsers servlet to cause a server error.

String query = req.getParameter("q");
query.toString();

ErrorHandler

  • Run Tomcat locally. Visit "/users" to see the custom error page. 

  • Remove the two lines of code from the doGet method that is causing the server error.

  • Commit and push the work to GitHub. Wait for the app to deploy and test the live website.

  • Visit "/users" to see the custom error page.

  • Set the debugging context-param to anything but true if you do not want users to see error messages on your live site.

End Day 3

Start Day 4

Config Class

  • In the shared package, create a class called Config. Add this code I got from ChatGPT to retrieve system environment variables.

import java.util.Optional;

public class Config {
    public static String getEnv(String key) {
        return Optional.ofNullable(System.getenv(key))
                .orElseThrow(() -> new IllegalStateException("Environment variable " + key + " is not set"));
    }
}
  • Windows Users: Create an AZURE_MYSQL_CONNECTIONSTRING environment variable the same way you created a JAVA_HOME, MAVEN_HOME, and CATALINA_HOME environment variable. Restart your computer.

Environment Variables

Mac Environment Variables

  • Mac Users: The ChatGPT thread on the previous page contains directions on how to set system environment variables. 

  • Open "Users/your-username/" in the Finder app. Press Command + Shift + period to display hidden files. 
  • I created a file called ".zshrc" and opened it in VS Code.
  • I typed this text with my full connection string and saved the file:
    export AZURE_MYSQL_CONNECTIONSTRING='jdbc:mysql..'
    • I had issues when my database password contained $ and % characters. Please remove those and reset the password.
  • I opened the terminal and typed printenv to display a list of currently set environment variables.
  • Restart your computer.

MySQL_Connect Class

  • Update the MySQL_Connect class to use the Config class instead of the Dotenv class.

String connectionString = "";
try {
    connectionString = Config.getEnv("AZURE_MYSQL_CONNECTIONSTRING");
} catch (IllegalStateException e) {
    throw new SQLException(e.getMessage());
}
  • Run Tomcat. Go to "/users". The program should work without errors.

  • Commit and push the work to GitHub. Wait for the app to deploy and test the live website.

  • Visit "/users" to see the list of users, not an error page.

top-nav

  • Find Bootstrap header examples on this page and this page.

  • In the "WEB-INF/" folder, create a new file called top-nav.jspf. Add this HTML code.

<header class="p-3 text-bg-light">
    <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 fs-3 mb-2" 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 class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search">
                        <input type="search" class="form-control" placeholder="Search..." aria-label="Search">
                    </form>
                    <div class="text-end">
                      	<a href="#" class="btn btn-outline-dark me-2">Login</a>
                        <a href="#" class="btn btn-warning">Sign-up</a>
                    </div>
                </div>
            </div>
        </nav>
    </div>
</header>

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; <fmt:formatDate value="${date}" pattern="yyyy" /> 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.jspf

  • Open top.jspf
  • Add the fn taglib, jsp:useBean tag, Bootstrap Icon CSS files, and an include directive for top-nav.jspf
    • You may add additional stylesheets and JS files here 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" %>
<jsp:useBean id="date" class="java.util.Date" />
<fmt:setLocale value="en-US" />
<c:choose>
    <c:when test="${pageContext.request.serverName eq 'localhost' }">
        <c:set var="appURL" value="${initParam['appURLLocal']}"></c:set>
    </c:when>
    <c:otherwise>
        <c:set var="appURL" value="${initParam['appURLCloud']}"></c:set>
    </c:otherwise>
</c:choose>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <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">
    <%-- Additional CSS --%>
    
    <script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
            crossorigin="anonymous"></script>
    <script defer src="https://code.jquery.com/jquery-3.7.1.min.js"
            integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
            crossorigin="anonymous"></script>
    <%-- Additional JS --%>
</head>
<body>
<%@ include file="/WEB-INF/top-nav.jspf" %>

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.

JavaScript

  • JavaScript files can be added before the closing </head> tag in top.jspf or before the closing </body> tag in bottom.jspf.
  • Add Bootstrap and jQuery JavaScript files.
  • 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.
  • You may add additional JavaScript script tags to this file later.

bottom.jspf

  • Open bottom.jspf.
  • Add an include directive for bottom-nav.jspf
<%@ include file="/WEB-INF/bottom-nav.jspf" %>
</body>
</html>

Home Servlet

  • Delete index.jsp
  • 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);
    }
}

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

.bd-placeholder-img {
    font-size: 1.125rem;
    text-anchor: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
}

/* CUSTOMIZE THE CAROUSEL
-------------------------------------------------- */

/* Carousel base class */
.carousel {
    margin-bottom: 4rem;
}
/* Since positioning the image, we need to help out the caption */
.carousel-caption {
    bottom: 3rem;
    z-index: 10;
}

/* Declare heights because of positioning of img element */
.carousel-item {
    height: 32rem;
}


/* MARKETING CONTENT
-------------------------------------------------- */

/* Center align the text within the three columns below the carousel */
.marketing .col-lg-4 {
    margin-bottom: 1.5rem;
    text-align: center;
}
/* rtl:begin:ignore */
.marketing .col-lg-4 p {
    margin-right: .75rem;
    margin-left: .75rem;
}
/* rtl:end:ignore */


/* Featurettes
------------------------- */

.featurette-divider {
    margin: 5rem 0; /* Space out the Bootstrap <hr> more */
}

/* Thin out the marketing headings */
/* rtl:begin:remove */
.featurette-heading {
    letter-spacing: -.05rem;
}

/* rtl:end:remove */

/* RESPONSIVE CSS
-------------------------------------------------- */

@media (min-width: 40em) {
    /* Bump up size of carousel content */
    .carousel-caption p {
        margin-bottom: 1.25rem;
        font-size: 1.25rem;
        line-height: 1.4;
    }

    .featurette-heading {
        font-size: 50px;
    }
}

@media (min-width: 62em) {
    .featurette-heading {
        margin-top: 7rem;
    }
}

@media (min-width: 768px) {
    .bd-placeholder-img-lg {
        font-size: 3.5rem;
    }
}
<c:if test="${pageTitle eq 'Home'}"><link rel="stylesheet" href="${appURL}/styles/home.css"></c:if>
  • Add a new link tag to the head section of top.jspf to conditionally use this css file
  • Run Tomcat to view the new homepage. Visit /users to see the consistent navigation and footer.

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 class="navbar-brand fs-3 mb-2" href="${appURL}">Toy Store</a>

<a href="${appURL}/signup" class="btn btn-warning">Sign-up</a>

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="text" 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="form-check mb-3">
            <input type="checkbox" class="form-check-input" value="remember-me">
            <label>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>

Signup Form

  • Add form attributes.

    <form method="post" action="${appURL}/signup">

  • Add name attributes to all form inputs that match the ids.

    id="email" name="email"
    id="password1" name="password1"

  • Create a second password form input for the user to confirm their password.
  • Change the "Remember me" text to
    I agree to the <a href="${appURL}/terms" target="_blank">terms of use</a>.
  • Add an id, name and value attribute to the checkbox.
    id="terms" name="terms" value="agree"

  • Add a for attribute to the checkbox's label.
  • Change "By clicking Sign up..." text to: Already have an account? <a href="${appURL}/login">Log in</a>

End Day 4