Week 2
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
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".
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.
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.
<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.
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.
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>
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.
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>
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);
}
}
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);
}
}
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>
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
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.
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
Check out the available static error fields from the RequestDispatcher interface.
In the ErrorHandler, before setting the pageTitle attribute, get some of those fields and assign them to String variables.
Format the error message output.
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>
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();
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.
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.
Mac Users: The ChatGPT thread on the previous page contains directions on how to set system environment variables.
export AZURE_MYSQL_CONNECTIONSTRING='jdbc:mysql..'
printenv
to display a list of currently set environment variables.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.
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>
In the "WEB-INF/" folder, create a new file called bottom-nav.jspf. Add this HTML code.
Note the use of the <fmt:formatDate> tag to get the current year
<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>
<%@ 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" %>
<%@ include file="/WEB-INF/bottom-nav.jspf" %>
</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);
}
}
<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;
}
/* 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>
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 class="navbar-brand fs-3 mb-2" href="${appURL}">Toy Store</a>
<a href="${appURL}/signup" class="btn btn-warning">Sign-up</a>
<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>
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"
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"
Already have an account? <a href="${appURL}/login">Log in</a>