Marc Hauschildt
Web Technologies and Computer Software Development Instructor at Kirkwood Community College in Cedar Rapids, IA.
Week 5
Jakarta EE extends the Java Standard Edition (SE) .
IntelliJ IDEA creates a project with some boilerplate code that you can build and deploy successfully.
pom.xml is the Project Object Model with Maven configuration information, including dependencies and plugins necessary for building the project.
Remind students not to copy pom.xml, only dependencies
index.jsp is the starting page of your application that opens when you access the root directory URL. It renders Hello World!
and a link to /hello-servlet.
HelloServlet.java is the HelloServlet
class extends HttpServlet
and is annotated with @WebServlet
. It processes requests to /hello-servlet
: a GET request returns HTML code that renders Hello World!
.
Click the Tomcat menu arrow and select "Edit Configurations".
On the Server tab, change the "On 'Update' action" option to "Restart Server".
You must restart the server to recompile the project whenever you change the java source code.
This differs from an interpreted language like Python or PHP, which doesn't have to be re-compiled.
Change the "On frame deactivation" option to "Update classes and resources".
This will update your Java Server Pages (JSPs) without redeploying and restarting the server whenever you switch from IntelliJ to your web browser and refresh the page.
Changes to HTML, CSS, and JavaScript don't require recompiling java files.
Save the run configuration by clicking OK.
To run the configuration, click the Run button or press Shift + F10.
This run configuration builds a war artifact, starts the Tomcat server, and deploys the artifact to the server. You should see the corresponding output in the Services window.
Once this is done, it opens the specified URL in your web browser. The index.jsp file from the webapp folder will automatically display. Click the link to navigate to the /hello-servlet path.
In the IntelliJ project notice the new "target" folder that was created. This folder contains the compiled files.
Make a change to index.jsp. For example, change the title and h1 element to say "Java 2 Web Demos"
Change focus from IntelliJ to the web browser and press the refresh button.
The change should be displayed because you changed a front-end resource (a JSP).
Click the "Hello Servlet" link.
Go back to IntelliJ and make a change to HelloServlet.java. For example, change the message to "My First Servlet!".
Change focus from IntelliJ to the web browser and press the refresh button.
The change will not display because you changed a back-end resource (a Servlet) that needs to be recompiled.
Go back to IntelliJ and click the Run button (or press Shift + F10) to restart the server.
The site will redeploy and the changes will be visible.
Click the Tomcat menu arrow and select "Edit Configurations".
If Tomcat Server does not appear, click the Add button, expand the Tomcat Server node, and select Local.
Fix any warnings at the bottom of the run configuration settings dialog. Examples include:
Warning: No artifacts marked for deployment
Likely cause: You cloned the project from GitHub.
Solution: Click the Fix button (or click the Deployment tab), Click the add button, select Artifact, select "project-name:war exploded".
On the Server tab, be sure the URL is set to:
http://localhost:8080/project_name_war_exploded/
Error: Application Server 'Tomcat 10.X.X' is not configured
Likely cause: The project was set up on a computer with a different version of Tomcat than your current computer.
Solution: Select the correct Application server from the dropdown menu.
Servlets allow the web application container (Tomcat) to receive HTTP requests from the client and reply with an HTTP response.
Servlets contain the functionality needed to implement business logic within the application to process dynamic web page content as the response to the request.
In terms of Model-View-Controller (MVC) architecture, Servlets are considered the controller.
Any other Java class that does not extend HttpServlet are called "Plain Old Java Objects" (POJOs).
Right-click the package that contains HelloServlet and create a new Java Class called MyCalculator.
Add the @WebServlet annotation before the class signature. After the annotation, add a parenthesis with a name and value property.
@WebServlet (name = "myCalculator", value = "/my-calculator")
The value can be anything, but it must start with a slash. It is recommended to use all lowercase letters, substitute spaces with dashes, and keep it as short as possible.
The value is what will be typed in the browser's address bar.
public class MyCalculator extends HttpServlet
System.out.println("Received a GET request");
In terms of Model-View-Controller (MVC) architecture, JSPs are considered the view, or presentation layer.
<%-- --%>
.<!-- -->
.A directive tag provides instructions for the JSP pre-processor.
The tags start with <%@
and end with %>
. A page directive provides instructions to the container (Tomcat) that pertain to the current JSP page.
The page directive provides you with some controls over how the JSP is translated, rendered, and transmitted back to the client. It can also import a class, include other JSP, or include a JSP tag library.
The one created by IntelliJ sets the page's content type and character encoding.
The UTF-8 character encoding is important to ensure that HTML displays correctly on all systems in many languages.
You may create page directives anywhere in your JSP page, but they are typically coded at the top.
Notice that the rest of the content is HTML.
Replace the HTML with the Bootstrap Quickstart code.
Change the title to "Calculator App".
<title>My Calculator</title>
Add this to the body section.
<h1>My Calculator</h1>
Because this is a brand new file, you will need to re-run the app.
Replace /my-calculator with /my-calculator.jsp. Notice the title that appears.
Right-click the page and choose "View page source". Notice that the page directive does not display.
Replace the h1 element with this code.
This form contains two input fields, one for the first number and one for the second number, both using the "text" input type.
While there is a "number" input type, I do not want you to use them. We must validate numerical inputs with Java, not HTML.
Bootstrap classes are used for styling. Additionally, each input field is placed in a "form-group" div to properly format the labels and inputs using Bootstrap.
<div class="container my-4">
<div class="row">
<div class="col-6">
<h1>My Calculator</h1>
<p class="lead">Enter two numbers and press submit to calculate the result.</p>
<form>
<div class="form-group mb-2">
<label for="num1">Number 1:</label>
<input type="text" class="form-control" id="num1">
</div>
<div class="form-group mb-2">
<label for="num2">Number 2:</label>
<input type="text" class="form-control" id="num2">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
Edit the form element to include a method and action attribute.
The method will be GET or POST.
The action will be the url path to the servlet. If the url path is "/my-calculator" use "my-calculator" for the action.
<form method="POST" action="my-calculator">
If you submit the form, the browser will navigate to /my-calculator, and you will get a 405: Method not allowed error because the servlet currently doesn't handle post requests.
Create a doPost method in your MyCalculator servlet.
System.out.println("Received a POST request");
When you submit the form again, the browser will navigate to /my-calculator and will display a blank page. In IntelliJ, the terminal will say "Received a POST request".
In the JSP we need to add name attributes to the two input fields.
<input type="text" ... name="num1">
<input type="text" ... name="num2">
The name attributes are typically the same as the id attributes.
Please note that we are using text inputs, not number inputs. In this class, I DO NOT want you to use number inputs. You will be required to validate numbers using Java, not HTML.
<input type="number" ...>
Also, in this class, I DO NOT want you to use required attributes. You will be required to validate required inputs using Java, not HTML.
<input required ...>
When the form is submitted using the POST method the doPost method of the Servlet will be called.
The parameters num1 and num2 will be sent along with the request.
Inside the doPost method we need to get those values by calling the request object's getParameter method. The result return will be a String.
Remove or comment out the println statement after you have verified that it works
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String num1 = req.getParameter("num1");
String num2 = req.getParameter("num2");
System.out.println(num1 + " " + num2);
}
Create a Validators class in a package called "shared". This class will contain static methods that can be shared between multiple projects.
Add a method that will check if a String is a valid number.
public static boolean isANumber(String str) {
try {
Double.parseDouble(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
In the doPost method, write if statements to validate the inputs.
This code requires the isANumber() method to be imported.
import static edu.kirkwood.shared.Validators.isANumber;
If the inputs are not valid, set an attribute on the request object with an error message.
The setAttribute() method always takes two Strings as input.
The first is the key, the second is the value.
Do not continue the function if there is an error.
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String num1 = req.getParameter("num1");
String num2 = req.getParameter("num2");
boolean errorFound = false;
if(!isANumber(num1)) {
req.setAttribute("num1Error", "Number 1 is not valid");
errorFound = true;
}
if(!isANumber(num2)) {
req.setAttribute("num2Error", "Number 2 is not valid");
errorFound = true;
}
if(errorFound) {
req.getRequestDispatcher("my-calculator.jsp").forward(req, resp);
return; // Do not continue this method
}
}
Forward the request and response objects in the doGet method so we can access the form directly by visiting "/my-calculator" instead of "/my-calculator.jsp".
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("my-calculator.jsp").forward(req, resp);
}
A scriptlet starts with <%
and ends with %>
.
These tags will typically go below the page directive, above the doctype tag.
Any Java code can be written between the tags.
The getAttribute method returns an Object that needs to be casted to its appropriate value.
When using scriptlets, it's important to always check if the values are null. If you don't your program will encounter a 500 server error because of a NullPointerException.
<%
String num1Error = (String)request.getAttribute("num1Error");
if(num1Error == null) {
num1Error = "";
}
String num2Error = (String)request.getAttribute("num2Error");
if(num2Error == null) {
num2Error = "";
}
%>
An expression starts with <%=
and ends with %>
.
These tags will go directly inside HTML.
Use these to display the output of a variable set in a scriplet.
Remember, if the value is null, this code will encounter a 500 server error.
Submitting the form with invalid numbers will display the error messages.
<div class="form-group mb-2">
<label for="num1">Number 1:</label>
<input type="text" class="form-control" id="num1" name="num1">
<div style="color: red;"><%= num1Error %></div>
</div>
<div class="form-group mb-2">
<label for="num2">Number 2:</label>
<input type="text" class="form-control" id="num2" name="num2">
<div style="color: red;"><%= num2Error %></div>
</div>
If you enter values in the form, they will not be returned from the servlet. Add code to add those attributes to the request object.
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String num1 = req.getParameter("num1");
String num2 = req.getParameter("num2");
req.setAttribute("num1", num1);
req.setAttribute("num2", num2);
// code omitted
}
Add code to the scriptlet to get the num1 and num2 values.
<div class="form-group mb-2">
<label for="num1">Number 1:</label>
<input type="text" class="form-control" id="num1" name="num1" value="<%= num1 %>">
<div style="color: red;"><%= num1Error %></div>
</div>
<div class="form-group mb-2">
<label for="num2">Number 2:</label>
<input type="text" class="form-control" id="num2" name="num2" value="<%= num2 %>">
<div style="color: red;"><%= num2Error %></div>
</div>
Add value attributes to the HTML inputs. Use an expression to display the String.
<%
String num1 = (String)request.getAttribute("num1");
if(num1 == null) {
num1 = "";
}
String num2 = (String)request.getAttribute("num2");
if(num2 == null) {
num2 = "";
}
// code omitted
%>
In the servlet, if there are no errors, call a method to add the two numbers.
In this example, I use the BiFunction functional interface to create a lambda expression that returns the sum of two String values.
Set the result as a request attribute.
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// code omitted
if(errorFound) {
req.getRequestDispatcher("my-calculator.jsp").forward(req, resp);
return; // Do not continue this method
}
double sum = getSum.apply(num1, num2); // Call the BiFunction object's abstract method apply
String result = String.format("%s + %s = %s", num1, num2, sum);
req.setAttribute("result", result);
req.getRequestDispatcher("my-calculator.jsp").forward(req, resp);
}
// This lambda expression equivalent to lines 22-26
private static BiFunction<String, String, Double> sum = (num1, num2) -> {
double n1 = Double.parseDouble(num1);
double n2 = Double.parseDouble(num2);
return n1 + n2;
};
/*
private static double getSum(String num1, String num2) {
double n1 = Double.parseDouble(num1);
double n2 = Double.parseDouble(num1);
return n1 + n2;
}
*/
Add code to the scriptlet to get the result
<form method="POST" action="my-calculator">
<!-- Code omitted -->
</form>
<div style="color: green;"><%= result %></div>
Use an expression to display the String.
<%
// code omitted
String result = (String)request.getAttribute("result");
if(result == null) {
result = "";
}
%>
The program will work when you run it.
If you enter 1.1 and 2.2 into the form the result will be something like 3.3000000000005.
Create a Helpers class in the shared package. Add code to round a decimal to a specific number of decimal places.
import java.text.DecimalFormat;
public class Helpers {
public static String round(double number, int numDecPlaces) {
DecimalFormat decimalFormat = new DecimalFormat("0.#"); // Step 1: Instantiate a DecimalFormat object
// Step 2: Set the DecimalFormat pattern - 0.# means something will always print to the left of the decimal
decimalFormat.setMaximumFractionDigits(numDecPlaces); // Step 3: Call the non-static method to set the number of decimal places
return decimalFormat.format(number); // Step 4: Format the decimal number as a string and return it.
}
}
Call that method when you are assigning the result.
String result = String.format("%s + %s = %s", num1, num2, round(sum, 4));
By Marc Hauschildt
Web Technologies and Computer Software Development Instructor at Kirkwood Community College in Cedar Rapids, IA.