Creating entities
Let's create the User class in the entities package , and it will contain two private string variables name and password . Let's create constructors (default and one that would take both values), getters/setters, override the toString() method just in case, as well as the equals() and hashCode() methods . That is, we will do everything that a decent Java developer does when creating a class.public class User {
private String name;
private String password;
public User() {
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (name != null ? !name.equals(user.name) : user.name != null) return false;
return password != null ? password.equals(user.password) : user.password == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
}
Now we can start creating a list of users. We will add users to it, and from where we will pick them up for display. However, there is one problem here. We don't create our servlet objects, Tomcat does it for us . The methods that we override in them are also already defined for us, and we cannot add a parameter. How then to create a common list that would be visible in both of our servlets? If we simply create our own list object in each servlet, then it turns out that we will add users to one list, and display the list of users with the ListServlet servlet— in another. It turns out that we need an object that would be common to both servlets. Generally speaking, we need an object that is common to all classes in our program; the only object in the entire program. I hope you heard something about design patterns. And, perhaps, for someone, this is the first real need to use the Singleton pattern in their program. You can pervert and “cut down” some cool Singleton , with double checks and synchronization (yes, we have a multi-threaded application, since Tomcat servlets are launched in different threads), but I will use the early initialization option, because here it is quite enough, and it is suitable for our purposes.
Model creation
Let's create a class (and implement the Singleton template in it ) in the model package and name it something unusual. Let's say Model . Let's create a private user list object in our class, and implement two methods: one to be able to add a user, and the second to return a list of strings (usernames). Since our user object consists of a name and a password, and we would not want to “shine” user passwords, we will only have a list of names.public class Model {
private static Model instance = new Model();
private List<User> model;
public static Model getInstance() {
return instance;
}
private Model() {
model = new ArrayList<>();
}
public void add(User user) {
model.add(user);
}
public List<String> list() {
return model.stream()
.map(User::getName)
.collect(Collectors.toList());
}
}
A little about mvc
Since you have heard about singleton , it means that you have probably heard about another design pattern - MVC (model-view-controller, in Russian model-view-controller, or just like in English model-view-controller). Its essence is to separate the business logic from the presentation. That is, separate the code that determines what to do from the code that determines how to display. View (view or just views) is responsible for how to present some data. In our case, the views are our jsp pages. That is why I put them in a folder called views . Model- this is actually the data itself with which the program works. In our case, these are users (list of users). Well, controllers are the link between them. They take data from the model and pass it to the views (or receive some data from Tomcat, process it and pass it to the model). Business logic (what exactly the program should do) needs to be described in them, and not in the model or in the view. So everyone minds their own business:- the model stores the data;
- views draw a beautiful representation of the data;
- controllers handle the data.
<form method="post">
<label>Name:
<input type="text" name="name"><br />
</label>
<label>Password:
<input type="password" name="pass"><br />
</label>
<button type="submit">Submit</button>
</form>
Here, the form has a method attribute with a value of post . This indicates that the data from this form will fly to the server in the form of a POST request. The action attribute is not specified, which means that the request will be sent to the same address where we went to this page ( /add ). Thus, our servlet bound to this address, upon receiving a GET request, returns this jsp with the form for adding users, and if it receives a POST request, it means that the form sent its data there (which we will pull out from the request object in the doPost ( ) method , process and transfer to the model for saving). It is worth noting that the input fields have the parameter name(for a field with a name it has the value name, and for a field with a password it has the value pass ). This is a pretty important point. Since in order to get this data from the request (inside the servlet already) (the name and password that will be entered) - we will use exactly these name and pass . But more on that later. The send data button itself is again made in the form of a button , and not an output field, as is usually the case. I don’t know how universal this option is, but it works for me (Chrome browser).
Handling a POST Request by a Servlet
Let's go back to the AddServlet servlet . Let me remind you: in order for our servlet to be able to “catch” GET requests, we overridden the doGet () method from the HttpServlet class . To teach our servlet to catch POST requests as well, we need to override the doPost() method as well . It receives similar request and response objects from Tomcat , which we will work with. To begin with, we will pull out the name and pass parameters from the request that the form sent (if you named them differently in the form, then you write exactly those names). After that, we will create our user object using the received data. Then we get the model object and add the created user to the model.@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
String password = req.getParameter("pass");
User user = new User(name, password);
Model model = Model.getInstance();
model.add(user);
}
Passing data to a view
Let's move on to the ListServlet servlet . The doGet() method has already been implemented here , which simply transfers control to the list.jsp view . If you don't already have it, do it by analogy with the same method from the AddServlet servlet . Now it would be nice to get a list of usernames from the model and pass them to the view, which will receive them and display them beautifully. To do this, we will again use the request object that we received from Tomcat . We can add an attribute to this object, giving it some name, and, in fact, the object itself, which we would like to pass to the view. Due to the fact that when transferring the execution process from the servlet to the view, we pass there the same request and response objects that the servlet itself received, then by adding our list of names to the request object, we can then from this request object in the view we can our list of user names and get. We have finished with the ListServlet class , so here is the code of the entire class:package app.servlets;
import app.model.Model;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class ListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Model model = Model.getInstance();
List<String> names = model.list();
req.setAttribute("userNames", names);
RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/list.jsp");
requestDispatcher.forward(req, resp);
}
}
java code execution in jsp files
It's time to tackle the list.jsp file . It will only be executed when the ListServlet sends an execution process here. In addition, we have already prepared a list of usernames from the model in that servlet and passed it here in the request object. Since we have a list of names, we can loop through it and output each name. As I said, jsp files can execute java code (which is how they differ from static html pages). In order to execute some code, it is enough to put the construction in the place we need:<!-- html code -->
<%
// java code
%>
<!-- html code -->
Inside such a construction, we get access to several variables:
- request is our request object, which we passed from the servlet, where it was simply called req ;
- response - the response object, in the servlet it was called resp ;
-
out is an object of type JspWriter (inherited from the usual Writer ), with which we can “write” something directly into the html page itself. The out.println("Hello world!") entry is very similar to the System.out.println("Hello world!") entry , but don't confuse them!
out.println() “writes” to the html page, and System.out.println writes to the system output. If you call the System.out.println() jsp method inside the Java code section , you will see the results in the Tomcat console , and not on the page.
<ul>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
for (String s : names) {
out.println("<li>" + s + "</li>");
}
}
%>
</ul>
If you want to display the list only when there are users, and otherwise display a warning that there are no users yet, we can rewrite this section a bit:
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ui>");
for (String s : names) {
out.println("<li>" + s + "</li>");
}
out.println("</ui>");
} else out.println("<p>There are no users yet!</p>");
%>
Now that we know how to pass data from servlets to views, we can slightly improve our AddServlet so that a notification is displayed when a user has been successfully added. To do this, in the doPost() method , after we have added a new user to the model, we can add the name of this user to the attributes of the req object and pass control back to the add.jsp view . And in it already make a section with Java code, in which a check is made whether there is such an attribute in the request, and if so, then a message is displayed that the user has been successfully added. After these changes, the complete AddServlet servlet code will look something like this:
package app.servlets;
import app.entities.User;
import app.model.Model;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AddServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
requestDispatcher.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
String password = req.getParameter("pass");
User user = new User(name, password);
Model model = Model.getInstance();
model.add(user);
req.setAttribute("userName", name);
doGet(req, resp);
}
}
Here, at the end of the doPost() method , we set an attribute with the name of the user added to the model, after which we call the doGet() method , into which we pass the current request and response. And the doGet () method already transfers control to the view, where it sends the request object with the added user name attached as an attribute. It remains to tweak add.jsp so that it displays such a notification if such an attribute is present. Final add.jsp :
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add new user</title>
</head>
<body>
<div>
<h1>Super app!</h1>
</div>
<div>
<%
if (request.getAttribute("userName") != null) {
out.println("<p>User '" + request.getAttribute("userName") + "' added!</p>");
}
%>
<div>
<div>
<h2>Add user</h2>
</div>
<form method="post">
<label>Name:
<input type="text" name="name"><br />
</label>
<label>Password:
<input type="password" name="pass"><br />
</label>
<button type="submit">Submit</button>
</form>
</div>
</div>
<div>
<button onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
The page body consists of:
- div-a with a header;
- div-container for content, it checks if there is an attribute with the username;
- div with the form for adding users;
- well, at the end of the footer with a button to return to the main page.
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Users</title>
</head>
<body>
<div>
<h1>Super app!</h1>
</div>
<div>
<div>
<div>
<h2>Users</h2>
</div>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ui>");
for (String s : names) {
out.println("<li>" + s + "</li>");
}
out.println("</ui>");
} else out.println("<p>There are no users yet!</p>");
%>
</div>
</div>
<div>
<button onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
Thus, we have a fully working web application that can store and add users, as well as display a list of their names. It remains only to embellish ... :)
Adding styles. We use the W3.CSS framework
At the moment, our application is working, but absolutely outrageous. Therefore, we will add a background, color for text and buttons, style lists, do alignment, add indents, and the like. If you write styles by hand, it can take a lot of time and nerves. Therefore, I suggest using the W3.CSS CSS framework. It already has ready-made classes with styles, it remains only to place the css classes that we want to apply in the right places. In order to add them to our pages, we first need to include a file with styles. This can be done in two ways:-
go through our pages and in the head section insert a direct link to the file with styles
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
This option is suitable if you have a permanent Internet connection. Then, when you open your pages on a local server, the styles will be pulled from the Internet.
-
If you want to have all the styles locally and not be dependent on the Internet connection, download the style file and place it somewhere inside the web folder (for example, web/styles/w3.css ), then go through all our pages ( index.html, add.jsp, list.jsp ) and enter a link to this file with styles inside the head section
<link rel="stylesheet" href="styles/w3.css">
After that, just go through the tags and add the styles that you like. I will not dwell on this in detail, but will immediately give my ready-made versions of my three files with arranged style classes.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Super app!</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-center">
<div class="w3-bar w3-padding-large w3-padding-24">
<button class="w3-btn w3-hover-light-blue w3-round-large" onclick="location.href='/list'">List users</button>
<button class="w3-btn w3-hover-green w3-round-large" onclick="location.href='/add'">Add user</button>
</div>
</div>
</body>
</html>
add.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Add new user</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-padding">
<%
if (request.getAttribute("userName") != null) {
out.println("<div class=\"w3-panel w3-green w3-display-container w3-card-4 w3-round\">\n" +
" <span onclick=\"this.parentElement.style.display='none'\"\n" +
" class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-green w3-border w3-border-green w3-hover-border-grey\">×</span>\n" +
" <h5>User '" + request.getAttribute("userName") + "' added!</h5>\n" +
"</div>");
}
%>
<div class="w3-card-4">
<div class="w3-container w3-center w3-green">
<h2>Add user</h2>
</div>
<form method="post" class="w3-selection w3-light-grey w3-padding">
<label>Name:
<input type="text" name="name" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
</label>
<label>Password:
<input type="password" name="pass" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
</label>
<button type="submit" class="w3-btn w3-green w3-round-large w3-margin-bottom">Submit</button>
</form>
</div>
</div>
<div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
<button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
list.jsp
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Users list</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body class="w3-light-grey">
<div class="w3-container w3-blue-grey w3-opacity w3-right-align">
<h1>Super app!</h1>
</div>
<div class="w3-container w3-center w3-margin-bottom w3-padding">
<div class="w3-card-4">
<div class="w3-container w3-light-blue">
<h2>Users</h2>
</div>
<%
List<String> names = (List<String>) request.getAttribute("userNames");
if (names != null && !names.isEmpty()) {
out.println("<ul class=\"w3-ul\">");
for (String s : names) {
out.println("<li class=\"w3-hover-sand\">" + s + "</li>");
}
out.println("</ul>");
} else out.println("<div class=\"w3-panel w3-red w3-display-container w3-card-4 w3-round\">\n"
+
" <span onclick=\"this.parentElement.style.display='none'\"\n" +
" class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-red w3-border w3-border-red w3-hover-border-grey\">×</span>\n" +
" <h5>There are no users yet!</h5>\n" +
"</div>");
%>
</div>
</div>
<div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
<button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
</div>
</body>
</html>
That's all :) If you have any questions or comments, or vice versa something doesn't work, leave a comment. UPD: if you have problems with 404 error when clicking on buttons, although everything is done correctly, you may need to fix the deployment configuration in the idea. to do this, go to Edit configurations (there at the top near the start button), go to the Deployment tab in the right part of the window and make it so that the Application context is simply / Well, I’ll attach a couple of screenshots of what came out of it all.
- make a servlet and a jsp for deleting a user and a couple more for changing/editing an existing user. You will get a real CrUD web application :) on servlets));
- replace the list (List) with a database, so that the added users do not disappear after the server is restarted :)
GO TO FULL VERSION