S

sem2practice1

Практика по сервлетам и JSP

Практика №1

Будем разрабатывать простой чат (без AJAX).

Скриншоты для Netbeans 8.2, для IDEA инструкция по созданию проекта тут: https://yadi.sk/i/yMsPxbISPg55lg

Создание заготовки

  1. Создайте веб-приложение image

  2. Выберите сервер Tomcat для более быстрого запуска и отладки image

  3. Добавьте к проекту заготовки классов Message (Сообщение чата с автором, адресатом и датой) и MessageService (хранилище сообщений) Message.javaMessageService.java#

  4. Скопируйте в основную папку веб-страниц проекта страницу index.jsp index.jsp

  5. Добавьте к проекту новую JSP-страницу (потом переименуете, чтобы была доступна c index.jsp)

    image

    Поместите на неё форму посылки сообщения

       <form action="адрес, куда послать запрос, относительно текущего пути" method="HTTP-метод посылки запроса">
            Ваше имя:<br/>
            <input name="user"/><br/>
            Ваше сообщение:<br/>
            <textarea cols="50" rows="3"  name="msg"></textarea><br/>
            <input type="submit" title="Опубликовать"/>
        </form>
  6. Соберите WAR-архив проекта (кнопка полной сборки в Netbeans рядом с кнопкой запуска). Просмотрите его содержимое архиватором или IDE. Где классы? Выкладывается ли на сервер исходный java-код или скомпилированные JSP?

  7. Проверьте запуск приложения. Если ошибка - отключите прокси в NetBeans или корректно настройте интеграцию с сервером приложений в другой IDE.

  8. Убедитесь, что в порядке файл WEB-INF/web.xml

    <?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 webapp_4_0.xsd"
      version="4.0">
    </web-app>

    Без version могут не работать EL-выражения ${m} или что-то еще, без xmlns - автодополнение в этом файле.

Добавление и показ сообщений

  1. Создайте сервлет AddServlet, обрабатывающий POST-запрос на адрес /add.do . Использовать аннотации для назачения сервлета на адрес. Установите в форме адрес и метод для перехода на этот сервлет. Проверьте, что ему передаётся управление с формы

    • Обратите внимание, в сервлетах "/" считается корнем этого приложения (http://localhost/Chat/), а в html для браузера - путем к серверу (http://localhost)
  2. Получите в сервлете объект типа MessageList из атрибутов приложения.

     MessageService svc = (MessageService) getServletContext().getAttribute("msgSvc")
    // если атрибута нет, вернет null
  3. Чтобы атрибут приложения появился, занесите его туда при старте приложения - создайте класс AppListener, методы которого будут запускаться автоматически

     @WebListener
     public class AppListener implements ServletContextListener {
    ...
     sce.getServletContext().setAttribute("msgSvc", new MessageService());
    ...
     }
  4. Принимайте параметры user и msg из запроса в AddServlet

    public class AddServlet extends HttpServlet{
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String user = req.getParameter("user");
        String msg = req.getParameter("msg");

    Если они допустимые (не null и не пустые строки), добавтье новое сообщение в список (вызвать метод объекта MessageService).

  5. Реализуйте в сервлете вывод текущего списка сообщений в ответ клиенту (UL/LI или таблицы HTML). Список сообщений - getAllMessages() у MessageService.

    try (PrintWriter out = response.getWriter()) {
            for (Message m: ....) {
               out.println(....);
            }
    }

    проверьте работу.

  6. Перенесите этот код в отдельный файл oldstylejsp.jsp со скриптлетом в html/body

    <body>
    ...
    <%
       MessageService svc = (MessageService) application.getAttribute("msgSvc");
       for (Message m: ....) {
               out.println(....);
       }
    %>
  7. Передайте управление на JSP-страницу из сервлета. Попробуйте передачу forward

    request.getRequestDispatcher("oldStyleJsp.jsp").forward(request, response);

    и response.sendRedirect(). В чём отличие?

  8. Создайте более современную jsp-страницу messages.jsp с использованием JSTL-тегов. Добавьте библиотеку JSTL к проекту (Netbeans - в дереве проекта добавить библиотеку из числа стандартных, в других средах - найти jar-файлы jstl или через Maven)

    <%@taglib  prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    ....
        <ul>
        <c:forEach var="m" items="${messages}" >
        <li>${m}</li>
        </c:forEach>
        </ul>
    

В случае maven подойдет вот такой фрагмент pom.xml:

   <dependencies>
   <dependency>
     <groupId>javax.servlet.jsp</groupId>
     <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
     <scope>provided</scope>
   </dependency>

   <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>8.0.1-b5</version>
      <scope>provided</scope>
   </dependency>
   </dependencies>
  1. Передайте управление на новую страницу, предварительно заполнив список сообщений messages в атрибуты запроса:

      request.setAttribute("messages", svc.getAllMessages());
  2. Сделайте вывод сообщений более красивым (например, ${m.svoistvo} вызывает m.getSvoistvo()).

  3. Если выводить сообщения с помощью <c:out value="${m.text}", то появится защита от подстановки произвольного HTML/JS кода. Для имен пользователя тоже можно.

Вход в систему

Сделаем имитацию входа в систему.

  1. Добавьте в папку WEB-INF фрагмент JSP login.jspf

     <%@ page pageEncoding="UTF-8" %>
    <form>
        <input type="text" placeholder="имя" />
        <input type="password" placeholder="пароль" name="p"/>
        <input type="submit" value="Вход"/>
    </form>

    (создать jsp - галочка "создать в виде сегмента jsp", в IDEA - можно просто jsp)

  2. Используйте <%@include file="...."%> для подключения фрагмента входа в систему в index.jsp.

  3. Добавьте форме action и method. Направьте запрос от формы на адрес login.do , которому назначьте новый сервлет. Сервлет должен проверить пароль (например, пуст будет всегда первая буква имени) и при успешном входе занести имя пользователя в атрибуты сессии.

     request.getSession().setAttribute("username",...)

    и перенаправить браузер на главную страницу приложения с помощью sendRedirect.

  4. Скопируйте addMessage.jsp в newMessage.jsp . Поприветствуйте пользователя в заголовке, смените назначение поля формы user - теперь это адресат сообщения (если пустой - в общий чат). В сервлете AddServlet используйте имя пользователя из сессии

     String user request.getSession().getAttribute("username"

    Параметр запроса теперь соответствует адресату сообщения. Если он пустой, создаём сообщение без адресата, как раньше. Если не пустой - вызываем addMessage с тремя параметрами.

  5. Назначьте сервлету AddServlet дополнительные адреса - "/view.do" "/viewPrivate.do". Проверьте их в начале кода (String path = request.getServletPath()). Если add.do, делаем все как раньше, если view.do - не добавляем сообщения, а только показываем, если viewPrivate.do - показываем личные сообщения пользователя. Заготовки методов поиска сообщений есть в классе MessageService.

    • Используйте одну и ту же страницу messages.jsp в качестве просмотрщика сообщений. Меняется содержимое атрибута messages.
    • Ссылки на страницы view.do, viewPrivate.do можно добавить на стартовую страницу, форма и передача параметров для них не требуются.
  6. (по желанию) Cгенерируйте ссылки "ответить" для каждого сообщения в списке личных сообщений. JSTL-EL. Можно направлять на "newMessage.jsp?reply=username" и использовать на странице newMessage.jsp EL-выражение ${param.user}.

Фильтр

  1. Добавьте фильтр и опишите его в web.xml (хотя можно и аннотациями).

     public class CounterFilter implements Filter {
     
     private FilterConfig filterConfig = null;
     
     public void doFilter(ServletRequest request, ServletResponse response,
             FilterChain chain)
             throws IOException, ServletException {
         // сессия - ((HttpServletRequest) request).getSession()
         // приложение - filterConfig.getServletContext()
         // до передачи управления
         chain.doFilter(request, response);
         // после возврата управления
     }
    
     public FilterConfig getFilterConfig() {  return (this.filterConfig);  }
     public void destroy() {       }
    
     public void init(FilterConfig filterConfig) {        
         this.filterConfig = filterConfig;
     }
     }

    Фильтр должен считать запросы в объектах класса AtomicInetger counter. Храните в сессии атрибут sessionCounter со счётчиком запросов в текущей сессии, а в приложении атрибут globalCounter с общим счётчиком. Если атрибута нет (null), пусть фильтр его заносит.

  2. Покажите счётчики на какой-либо существующей jsp-странице через EL.

  3. (по желанию) Создайте отдельный фильтр, блокирующий viewPrivate.do и add.do, если в сессии нет имени пользователя. Можно не вызывать chain.doFilter, а перенаправлять с помощью sendRedirect на страницу с ошибкой и формой входа в систему.

Стоит выложить результат в хранилище Gitlab (можно дублировать к себе). Создать проект в веб-интерфейсе, скопировать адрес и использовать его в диалогах image image