Knowledge required to understand the article: You've already more or less figured out Java Core and would like to look at JavaEE technologies and web programming. It would make the most sense for you to be currently studying the Java Collections quest, which deals with topics close to the article.
This material is the logical continuation of my article Creating the simplest web project in IntelliJ Idea Enterprise. In that article, I demonstrated how to create a working web project template. This time I'll show you how to create a simple but totally attractive web application using the Java Servlet API and the JavaServer Pages API. Our application will have a home page with two links:
  • a link to a page for adding users;
  • a link to the list of users.
As before, I will use IntelliJ Idea Enterprise Edition, Apache Maven (we'll just connect some dependencies), and Apache Tomcat. In the end, we'll "beautify" our application using the W3.CSS framework. We'll assume that you already have an empty project that we'll now add to. If not, run through the first article and make one. It will only take a few minutes :)

A little about our future application's structure

Our home page (/) will be a most ordinary static HTML page with a header and two links/buttons:
  • add a new user (navigates to /add);
  • view the list of users (navigates to /list).
Tomcat will catch requests for these addresses and send them to one of the two servlets that we're going to make (we'll specify the mapping in web.xml). The servlets will then process the requests, prepare data (or save data, if we're adding a user), and transfer control to the appropriate JSP files, which then "render" the result. We'll store the data in a plain vanilla list (List).

Create a static home page

If you index.jsp in your web folder, delete it. Instead, create a simple HTML file called index.html in this folder:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My super project!</title>
</head>
<body>
    <!-- header -->
    <div>
        <h1>Super app!<//h1>
    </div>

    <div>       <!-- content -->
        <div>    <!-- button holder -->
            <button onclick="location.href='/list'">List users<//button>
            <button onclick="location.href='/add'">Add user<//button>
        </div>
    </div>
</body>
</html>
There's nothing complicated here. In the title tag, we indicate the title of our page. In the page body, we have two main divs: header and content. The content div includes a holder for our buttons. And there we have two buttons that take you to the corresponding address with a click. You can run the project and see how it looks now. If you click on the buttons, you get 404-error pages, because we the corresponding pages don't exist yet. But we can tell that the buttons work. Note that this isn't the most universal approach: if JavaScript is turned off in the browser, then these buttons won't work. But we'll assume that no one disables JavaScript. :) Obviously, you could get by with simple links, but I prefer buttons. You can do it however you prefer. And don't worry about that fact that my examples will have a lot of divs. We'll fill them with styles later, and everything will look more beautiful. :)

Create JSP files to render the result

In the same web directory, create a folder where we'll add our JSP files. I called it 'views', but once again you can improvise. In this folder, we'll create two JSP files:
  • add.jsp — a page for adding users;
  • list.jsp — page to display the list of users.
Assign appropriate page headers to them. Something like "Add new user" and "User list", and we'll leave it like that.

Create two servlets

Servlets will receive and process the requests that Tomcat sends them. In the src/main/java folder, create the app package, where we will put our source code. Other packages will also go there. So, to prevent these packages from being created inside one another, we'll create some class in the app package (we'll delete it later). Now create three different packages in the app package:
  • entities — our entities (the class that describes user objects) go here;
  • model — this is where our model goes (we'll talk about this a little later);
  • servlets — and this is where our servlets go.
Once you've done this, you can calmly delete that class from the app package (if you created it, of course). In the servlets package, create two classes:
  • AddServlet — processes requests sent to /add;
  • ListServlet — processes requests sent to /list.

Connecting dependencies in Maven

Tomcat 9.* implements the specifications for Servlet 4.0 and JavaServer Pages 2.3. That's what stated in the second line of the first paragraph of Tomcat 9's official documentation. This means that if you, like me, use this version of Tomcat, then the code you will write and run will use these versions. But we'd like to have these specifications in our project, so that our code, which uses them, at least compiles successfully. And to do this, we need to load them into our project. This is where Maven comes to the rescue.

The general rule is this: if you need to connect something to your project using Maven:

  • go to the repository website from Maven;
  • find for the required version of the required library;
  • get the dependency code that needs to be pasted into your pom.xml;
  • paste! :)
Let's begin. First, prepare the POM file. Somewhere after the /version entry, but before /project, insert the following:
<dependencies>

</dependencies>
We do this to indicate that we will list the required dependencies within these tags. Now go to mvnrepository.com. There's a search field at the top. To start, search for 'servlet'. The first result, which has been used more than seven thousand times, suits us. Remember, we need version 4.0 (for Tomcat 9). Other versions may be appropriate for older implementations. This is a fairly recent version, so there aren't that many uses. But we need it. A page opens where you can get the code for this dependency for a variety of package managers, or you can simply download it. But since we want to connect it using Maven, we'll select the code on the Maven tab. We copy and paste into the dependency section of our POM file. If you get a notification asking if you want to enable auto-import in the lower right corner of the IDEA, go ahead and agree to it. If you accidentally refused, go to "Settings" and turn on auto-import manually: Settings (Ctrl + Alt + S) -> Build, Execution, Deployment -> Maven -> Importing. This will let you keep the POM file and the IDEA configuration files for this project in sync. Following the same principle, we'll find and connect JavaServer Pages 2.3 (search for "JSP"). And since we've already started Maven, let's just tell it that our source files follow Java 8 syntax, and that we need to compile them into bytecode for that version. After all these steps, our pom.xml will look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ru.javarush.info.fatfaggy</groupId>
    <artifactId>my-super-project</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compile.source>1.8</maven.compile.source>
        <maven.compile.target>1.8</maven.compile.target>
    </properties>

    <dependencies>
        <!-- Servlet API 4.0 for tomcat 9 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- JavaServer Pages API 2.3 for tomcat 9 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

Make our servlets into real servlets

At the moment, the pair of servlets we created are actually ordinary classes. They don't have any functionality. But now we've connected the Servlet API to our project, and accordingly we can use its classes. To make our servlets "real", all we need to do is make them inherit the HttpServlet class.

Mapping or markup

Now it would be nice to somehow tell Tomcat that requests for the /add address are processed by our AddServlet, and requests for the /list address are handled by the ListServlet. This process is called mapping (markup). This is done in web.xml using the same principle:
  • to start, describe the servlet (provide some name and specify the path to the class itself);
  • then bind this servlet to a specific address (specify the servlet's name, which we just gave it, and specify the address whose requests should be sent to this servlet).
Describe the servlet:
<servlet>
    <servlet-name>add</servlet-name>
    <servlet-class>app.servlets.AddServlet</servlet-class>
</servlet>
Now bind it to the address: add /add As you can see, servlet-name is the same in both cases. As a result, Tomcat knows that if a request for /add is received, it must be sent to the app.servlets.AddServlet. We do the same thing with the second servlet. In the end, our web.xml has approximately the following content:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- add servlet -->
    <servlet>
        <servlet-name>add</servlet-name>
        <servlet-class>app.servlets.AddServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>add</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>

    <!-- list servlet -->
    <servlet>
        <servlet-name>list</servlet-name>
        <servlet-class>app.servlets.ListServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>list</servlet-name>
        <url-pattern>/list</url-pattern>
    </servlet-mapping>
</web-app>
By the way, we didn't create markup for the home page (/). The fact is that we don't need it in this case. Our home page is a simple HTML file that just displays two buttons. It has no dynamic content, so we don't need to create a separate servlet for requests from / that will do nothing but forward execution to some JSP (which also would have to be created) to draw two buttons for us. We don't need this. A static page suits us. When Tomcat receives a request, it will check whether there's a single servlet that could process the request for that address, and then will see that this address actually already contains the ready HTML file, which it will serve up. We can run our application again (restart the server or redeploy it again—whatever you prefer) and make sure the home page is rendered, nothing broke, and the transitions occur when we click the buttons (though we again get an error). By the way, whereas before we got a 404 error, now we get a 405. It means that the mapping worked and the servlets were found, but they had no suitable method to handle the request.

Short digression: what happens "under the hood"?

You've probably already thought about how our application works in Tomcat. What happens in there? And where's the main() method? As soon as you go to localhost:8080 in your browser, the browser sends a request to this address using the HTTP protocol. I hope you're already aware that there are many different types of requests, and the most popular are GET and POST. Each request should be answered. A GET request is expected to receive a response of ready-to-use HTML code, returned to the browser. The browser then replaces the code will all the pretty letters, buttons, and forms. A POST request is a little more interesting, since it also carries some information. For example, suppose you enter credentials in a registration or sign-in form on a website, and click "Send". This causes a POST request with your personal information to be sent to the server. The server receives this information, processes it, and returns some response (for example, an HTML page with your profile). The principal difference between them is that GET requests are only used to retrieve data from the server, while POST requests carry some information (and the data on the server can change). For example, when you upload your picture to the server, it is carried there in a POST request and the server adds it to the database, i.e. a change occurs. Now back to Tomcat. When it receives a request from a client, it looks at the address. It checks whether there is a suitable servlet to process requests for that address (or an available resource that can be returned immediately). If it doesn't find something to return, then it responds with a 404-error rather than an HTML page. But if it finds a suitable servlet "sitting" at that address, then it looks at the request type (GET, POST, or something else) and asks the servlet if it has a method that can handle this type of query. If the servlet says it doesn't know how to handle this type, then Tomcat returns a 405 code. And this is just what happened in our project. But if a suitable servlet is found, and it has a suitable method, then Tomcat creates a servlet object, starts it on a new thread (which lets it run on its own), and Tomcat continues its own work, accepting and sending requests. Additionally, Tomcat creates two more objects: an HttpServletRequest (which I'll call the "request" for short), and an HttpServletResponse (which I'll call the "response"). It puts all the data received from the client's request into the first object, so all that data can be extracted from it. And then after all this, it passes these two these objects to the appropriate method of the servlet that was started on a separate thread. As soon as the servlet finishes its work and has a response ready to be sent to the client, it waves a flag at Tomcat, saying "I'm done. Everything is ready". Tomcat receives the response and sends it to the client. This lets Tomcat receiving requests and send responses, without becoming distracted, and all the work is done by servlets running on separate threads. That means that when we write the servlet code we determine what work will be performed. And you can think of the main() method as being located inside Tomcat itself (yep, it's written in Java), and when we "launch" Tomcat, the main() method is started.

Use servlets to catch GET methods and send super simple responses

At the moment, our servlets have no suitable methods (GET), so Tomcat returns a 405 error. Let's create them! The HttpServlet class, we our servlets inherit, declares various methods. To assign specific code to the methods, we simply override them. In this case, we need to override the doGet() method in both servlets.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
As you can see, this method takes two arguments: req (request) and resp (response). These are the very objects that Tomcat creates and populates for us when it calls the appropriate method in the servlet. To begin, we'll create the simplest responses. To do this, we'll take the resp object and get a PrintWriter object from it. This type of object is used to compose a response. We'll use it to output a simple string.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    PrintWriter writer = resp.getWriter();
    writer.println("GET method from AddServlet");
}
We'll do something similar in the ListServlet, and then we'll restart our server. As you can see, everything works! When you click on the buttons, you get pages with the text that we "wrote" with the PrintWriter. But the JSP files that we prepared to generating pages with responses are not being used. That's simply because they're never executed. Our servlet creates the response itself and finishes running, signaling to Tomcat that it is ready to respond to the client. Tomcat just takes the response and sends it back to the client. Let's pass control from the servlets to the JSP files. We'll change the code of our methods as follows:
  • we obtain a request dispatcher object from the request object, and pass it the address of the JSP page that we want to transfer control to;
  • we use this object to transfer control to the specified JSP page, not forgetting to pass the request and response objects we received from Tomcat.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
    requestDispatcher.forward(req, resp);
}
In the body tag of the JSP pages, you can add something so we can clearly see which page is being displayed. Once you've done that, restart the server and check. We click the buttons on the main page and the pages open, which means the requests are being sent to the servlets. Then control is passed to the JSP pages, which are now being rendered. That's all for now. In the next part of this article, we'll work on the functionality of our application.