Этот материал — часть цикла “Введение в Enterprise-разработку”. Предыдущие статьи: Часть 6. Контейнеры сервлетов - 1В прошлой статье мы познакомились с сервлетами, научились с их помощью создавать веб-приложения. Пришло время повнимательнее взглянуть на то, без чего этот праздник был бы невозможен — контейнеры сервлетов.

Содержание:

Что такое контейнер сервлетов

Это программа, которая запускается на сервере и умеет взаимодействовать с созданными нами сервлетами. Иными словами, если мы хотим запустить наше веб-приложение на сервере, мы сначала разворачиваем контейнер сервлетов, а потом помещаем в него сервлеты. Схема работы проста: когда клиент обращается на сервер, контейнер обрабатывает его запрос, определяет, какой именно сервлет должен его обработать и передает его. Часть 6. Контейнеры сервлетов - 2

Как используют контейнеры сервлетов

Кроме маршрутизации запросов, контейнер сервлетов выполняет и другие функции:
  1. Динамически генерирует HTML-страницы с JSP-файлов.
  2. Зашифровывает/расшифровывает HTTPS-сообщения.
  3. Предоставляет разграниченный доступ для администрирования сервлетов.
В общем, звучит неплохо, осталось только разобраться, как это все применить. Ну а чтобы научиться что-то использовать, нужно просто... попробовать это использовать :) Поэтому сегодня будем практиковаться! Самый популярный контейнер сервлетов — Apache Tomcat. Он имеет открытый исходный код, и его можно использовать бесплатно. Скачай Tomcat для своей операционной системы по этой ссылке, и посмотрим на работу с контейнерами “в деле”.

Установка и запуск Tomcat

  1. Для установки Tomcat просто распакуй скачанный архив в нужную директорию.

  2. Учти, что для запуска и работы Tomcat нужна Java версии 8 или выше. Убедись, что переменная среды JAVA_HOME ссылается на актуальную версию jdk.

  3. Далее необходимо сконфигурировать доступ пользователей к Tomcat. Это делается в файле tomcat-users.xml, который находится в папке conf.

    В Tomcat заранее предусмотрены четыре роли:

    • manager-gui — доступ к графическому интерфейсу и странице статусов;
    • manager-script — доступ к текстовому интерфейсу и странице статусов;
    • manager-jmx — доступ к JMX и странице статусов;
    • manager-status — доступ только к странице статусов.

    Внутри тега <tomcat-users> явно пропишем эти роли и назначим их нашему юзеру:

    <role rolename="manager-gui"/>
    <role rolename="manager-script"/>
    <role rolename="manager-jmx"/>
    <role rolename="manager-status"/>
    <user username="user" password="password"
        roles="manager-gui, manager-script, manager-jmx, manager-status"/>

    Теперь все готово к запуску!

  4. В папке bin запусти файл startup.bat (startup.sh на Linux).

  5. Через несколько секунд в браузере открой ссылку http://localhost:8080/. Там появится графический менеджер:

    Часть 6. Контейнеры сервлетов - 3

    Если видишь такое меню, значит, Tomcat запущен.

  6. Если не работает, вручную проверь переменные среды JAVA_HOME и CATALINA_HOME:

    • JAVA_HOME — должна ссылаться на актуальную версию джавы 8+;
    • CATALINA_BASE — должна ссылаться на Tomcat или отсутствовать (не должна указывать на другую версию Tomcat).

Развертывание приложения в Tomcat

Запустить Tomcat у нас получилось, стало быть, пришло время развернуть в нем какой-то проект. Давай используем сервлеты из прошлой статьи. MainServlet:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/hello")
public class MainServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
       HttpSession session = req.getSession();
       Integer visitCounter = (Integer) session.getAttribute("visitCounter");
       if (visitCounter == null) {
           visitCounter = 1;
       } else {
           visitCounter++;
       }
       session.setAttribute("visitCounter", visitCounter);
       String username = req.getParameter("username");
       resp.setContentType("text/html");
       PrintWriter printWriter = resp.getWriter();
       if (username == null) {
           printWriter.write("Hello, Anonymous" + "");
       } else {
           printWriter.write("Hello, " + username + "");
       }
       printWriter.write("Page was visited " + visitCounter + " times.");
       printWriter.close();
   }
}
IndexServlet:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/")
public class IndexServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
       resp.sendRedirect(req.getContextPath() + "/hello");
   }
}
Перед развертыванием наши сервлеты необходимо упаковать в war-архив. Обычно для этого используется Мавен, но для создания war-архива нужен файл web.xml, в котором мапятся все сервлеты. Мы же писали сервлеты с использованием новой аннотации @WebServlet, поэтому web.xml у нас нет. Благо, IDEA может выполнить грязную работу за нас и поштучно завернуть наш проект в war-архив. Для этого нужно открыть структуру проекта (Ctrl + Shift + Alt + S) -> Artifacts -> Выбрать нужный варник -> Поставить галочку возле поля “Include in project build” -> Нажать “ОК”. Часть 6. Контейнеры сервлетов - 4Билдим проект с помощью комбинации Ctrl + F9. Теперь в директории target лежит наш war-архив Часть 6. Контейнеры сервлетов - 5Файл можно переназвать как-то попроще — например, servlet.war — и перенести в более удобное место — в C:\my\. Когда варник готов к использованию, помещаем его в контейнер. Это можно сделать двумя способами.
  1. С помощью графического интерфейса

    Для этого переходим по ссылке http://localhost:8080/manager/html. Tomcat должен запросить логин и пароль.

    Если ты повторял все действия за мной, то логин — user, пароль — password.

    После успешной авторизации ты увидишь Tomcat Web Application Manager. В разделе Applications уже содержатся 5 приложений — это служебные приложения Tomcat, необходимые для упрощения работы с ним. В будущем их можно будет удалить.

    Часть 6. Контейнеры сервлетов - 6

    Ниже находится раздел Deploy. С его помощью можно выбрать war-архив для развертывания. Пропишем путь и контекст вручную:

    Часть 6. Контейнеры сервлетов - 7

    Нажимаем “Deploy”, видим, что в разделе Applications появилось наше приложение:

    Часть 6. Контейнеры сервлетов - 8 С помощью графического интерфейса Tomcat мы его можем останавливать, перезапускать, устанавливать длину сессии и удалять. При развертывании мы указали контекст /demo, а значит, обращаться к нашему приложению нужно по ссылке http://localhost:8080/demo. Проверь, все должно работать.

  2. Через файловую систему

    Чтобы задеплоить приложение таким способом, необходимо открыть директорию, в которой разархивирован Tomcat, перейти в webapps. Здесь находятся знакомые нам служебные приложения:

    Часть 6. Контейнеры сервлетов - 9

    Все, что требуется от нас — переместить сюда наш servlet.war.

    Ждем несколько секунд, видим, что появилась новая папка servlet, а это значит, что наше приложение развернуто. Переходим в знакомый нам Application Manager интерфейс — http://localhost:8080/manager/. Здесь мы видим, что наше приложение развернуто в контексте /servlet:

    Часть 6. Контейнеры сервлетов - 10

    При развертывании таким способом, контекст автоматически присваивается по названию развернутого war-архива. Для смены контекста можно переназвать новосозданную папку с приложением, но перед этим нужно удалить варник: в ином случае Tomcat повторно развернет приложение с именем архива.

    Как видишь, деплоить приложения в Tomcat намного проще, чем может показаться. Но и другими его функциями пользоваться несложно. Давай проверим.

Использование HTTPS протокола вместо HTTP

Если помнишь, разницу между HTTP и HTTPS мы рассматривали в отдельной статье. HTTPS — тот же протокол, что и HTTP, только с использованием шифрования данных, которые передаются. На стороне клиента шифрованием занимается браузер, а шифрование на стороне сервера должны обеспечить мы. Так как HTTP запросы принимает и маршрутизирует Tomcat, логично будет делегировать ему и шифрование. Для этого необходимо:
  1. Сгенерировать самоподписанный сертификат;
  2. Сделать дополнительные настройки сервера.
Давай попрактикуемся в этом.

Генерация сертификата

В JDK независимо от версии поставляется большое количество утилит, одна из которых — keytool. Это инструмент для генерации ключей шифрования и работы с ними. Чтобы его использовать, с помощью командной строки перейдем в директорию C:\Program Files\Java\jdk1.8.0_181\bin и выполним команду keytool -genkey -alias tomcat -keyalg RSA.
  • keytool — запускаем утилиту с параметрами;
  • -genkey — указываем, что мы хотим сгенерировать новый ключ;
  • -alias tomcat — создаем псевдоним ключа;
  • -keyalg RSA — выбираем RSA как алгоритм генерации ключа.
После выполнения команды, утилита затеет с нами диалог: Часть 6. Контейнеры сервлетов - 11Вводим необходимую информацию. Теперь мы создали хранилище ключей в нашем домашнем каталоге (для Windows это C:\Users\{username}\.keystore) и ключ tomcat в нем. Мы сгенерировали простой сертификат, на который будет ругаться большинство браузеров. Такой сертификат не подойдет для коммерческих приложений: его можно использовать только в тестовых целях. На продакшн-сервере необходимо использовать сертификат от центра сертификации (например, https://letsencrypt.org/).

Настраиваем сервер

Теперь, когда сертификат готов, нужно подкорректировать серверные настройки, а именно — SSL-коннектор. Это делается в файле server.xml, который находится в apache-tomcat-9.0.30/conf/. Находим там блоки типа:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                         type="RSA" />
        </SSLHostConfig>
 </Connector>
и возле них помещаем свою конфигурацию:
<Connector
       protocol="org.apache.coyote.http11.Http11NioProtocol"
       port="8443" maxThreads="200"
       scheme="https" secure="true" SSLEnabled="true"
       keystoreFile="C:\Users\user\.keystore" keystorePass="mypass"
       clientAuth="false" sslProtocol="TLS"/>
Параметрам keystoreFile и keystorePass присваиваем актуальные для нас значения, сохраняем и перезапускаем Tomcat с помощью файлов shutdown.bat и startup.bat. Теперь сервер готов обрабатывать https-запросы, вот только немного по измененному адресу — https://localhost:8443/demo/hello. При переходе по ссылке у тебя появится предупреждение о сомнительности сертификата, что и неудивительно. Как описывалось немного ранее, для получения нормального сертификата нужно воспользоваться услугами одного из сервисов сертификации. Но пока мы достигли нашей цели: приложение работает по HTTPS-протоколу, а это главное! Часть 6. Контейнеры сервлетов - 12

Динамическая генерация HTML-страниц

Теперь продолжим обзор других фич контейнеров сервлетов — динамической генерации HTML-страниц. Представь себе идеальный мир, где вместо скучного статичного HTML-кода можно было бы писать JAVA-код, используя переменные, циклы, массивы и другие конструкции языка. Представил? Хорошая новость — что-то похожее существует, плохая — не в полной мере. Если ты не догадался, речь идет о технологии JSP (Java Server Pages). Если коротко, это технология, которая позволяет вставлять в HTML-страницу куски JAVA-кода. Правда, потом этот код все равно превращается в HTML перед отправкой клиенту, но он будет динамически сгенерирован с учетом различных факторов. Например, можно использовать условные конструкции, и в зависимости от какого-то условия отдавать различный контент. Пример JSP-страницы:
<%@ page language="java"" %>
<html>
<head>
<title>JSP</title>
</head>

<body>
<%
String firstName="name";
String secondName="surname";

    if(firstName.equals("name")){
      out.print("Hello :"+firstName+"<br>");
    }

    if(firstName.equals("name") && secondName.equals("surname"))
    {
      out.print("Hello, my dear friend! <br>");
    }
    else
    {
      out.print("I don’t know you. Go away! <br>");
    }
%>
</body>
</html>
Дополнительно о JSP можно почитать здесь. Вообще... мы не ради этого здесь собрались, а ради контейнеров сервлетов! Причем тут JSP? Все просто: превращение JAVA-кода из JSP в HTML-код осуществляет именно контейнер сервлетов. Когда сервлет собирается вернуть в качестве ответа JSP-контент, контейнер обращает на это внимание, и перед отправкой такого контента клиенту сначала превращает его в понятную для браузера HTML-страницу. Сегодня известно много аналогов технологии JSP — Thymeleaf, FreeMarket, Mustache и другие. Все они работаю по схожему принципу. Какой из них выбирать для работы — дело вкуса. Это касается и выбора контейнера сервлетов. В примерах мы использовали Tomcat — самый распространенный контейнер, но в некоторых проектах используются и другие. С самыми популярными стоит кратко ознакомиться и посмотреть на их отличия от Tomcat.

Альтернативы Tomcat

  1. GlassFish — контейнер с открытым исходным кодом, разработку которого поддерживает Oracle.

    В отличие от Tomcat, это полноценный веб-сервер, который кроме сервлетов может оперировать и другими компонентами из фреймворка JavaEE. В то же время, он использует намного больше оперативной памяти. Более гибкий при тонкой настройке сервера, что усложняет его использование. Стоит использовать при разработке приложений на фреймворке JavaEE.

  2. WildFly — ранее Jboss. Также имеет открытый исходный код. Разрабатывается компанией Red Hat. Название изменили, чтобы избежать путаницы с другим продуктом компании — JBoss Enterprise Application Platform.

    WildFly, как и GlassFish, — полноценный веб-сервер. Кстати, под капотом WildFly использует Tomcat, как контейнер сервлетов. В отличии от GlassFish, WildFly более легковесный и простой в настройке.

  3. Jetty — аналогично предыдущим имеет открытый исходный код. Развивается компанией Eclipse.

    Как и Tomcat, является простым контейнером сервлетов, без поддержки всех компонентов фреймворка JavaEE. В то же время, он более легковесный, и его можно запустить даже на мобильном телефоне. Он быстро запускается и останавливается, хорошо масштабируется. В отличие от Tomcat, имеет меньшее комьюнити и базу знаний.

  4. WebLogic — лицензированное программное обеспечение, требующие покупки перед использованием. Принадлежит компании Oracle.

    По сравнению с Tomcat, его функционал немного шире. Может работать с протоколом ftp. Но он не настолько гибкий при разработке и тестировании приложений.

  5. WebSphere ( WebSphere Application Server, если быть точным) — платное программное обеспечение. Разрабатывается компаниеей IBM. Аналогично WildFly и GlassFish является полноценным сервером приложений. Но у него более дружелюбный интерфейс настройки, плюс высокая надежность в работе.

    Из минусов — он использует очень много ресурсов, долго запускается и останавливается, что не очень удобно при разработке небольших проектов.

Какой контейнер сервлетов или сервер приложений выбрать, зависит от конкретного проекта. Бывают проекты, где даже явный аутсайдер сможет проявить себя максимально качественно, но на первых порах лучше качественно разобраться с чем-то одним. Наверное, идеальный кандидат на роль этого одного — Tomcat. Первые шаги в его изучении мы уже сделали, а дальше дело за тобой! В завершающих статьях цикла “Введение в Enterprise-разработку” мы с тобой познакомимся с паттерном MVC. Часть 7. Знакомство с паттерном MVC (Model-View-Controller) Часть 8. Пишем небольшое приложение на spring-boot