Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Приложение dukeetf

Пример приложения dukeetf , расположенного в каталоге tut-install/examples/web/dukeetf/, демонстрирует, как использовать асинхронную обработку в сервлете для предоставления обновлённых данных веб-клиентам. Пример напоминает сервис, который предоставляет периодические обновления цены и объёма торгов биржевым инвестиционным фондом (ETF).

Здесь рассматриваются следующие темы:

Архитектура приложения dukeetf

Приложение dukeetf состоит из сервлета, Enterprise-бина и страницы HTML.

  • Сервлет помещает запросы в асинхронном режиме, сохраняет их в очереди и записывает ответы, когда становятся доступными новые данные о цене и объёме торгов.

  • Enterprise-бин обновляет информацию о цене и объёме каждую секунду.

  • HTML-страница использует код JavaScript, чтобы отправлять сервлету запросы на новые данные, анализировать ответ сервлета и обновлять информацию о цене и объёме без перезагрузки страницы.

В примере приложения dukeetf используется программная модель, известная как long polling. В традиционной модели HTTP-запросов и ответов пользователь должен сделать явный запрос (например, кликнуть ссылку или отправить форму), чтобы получить любую новую информацию с сервера, и страница должна быть перезагружена. Long polling предоставляет веб-приложениям механизм для отправки обновлений клиентам, использующим HTTP, без явного запроса пользователя. Сервер обрабатывает соединения асинхронно, а клиент использует JavaScript для создания новых соединений. В этой модели клиенты делают новый запрос сразу после получения новых данных, и сервер сохраняет соединение открытым, пока новые данные не станут доступны.

Сервлет

Класс DukeETFServlet использует асинхронную обработку:

@WebServlet(urlPatterns={"/dukeetf"}, asyncSupported=true)
public class DukeETFServlet extends HttpServlet {
...
}

В следующем коде метод init инициализирует очередь для хранения клиентских запросов и регистрирует сервлет с Enterprise-бином, который предоставляет обновления цены и объёма. Раз в секунду вызывается метод send у PriceVolumeBean для отправки обновлений и закрытия соединения:

@Override
public void init(ServletConfig config) {
   /* Очередь запросов */
   requestQueue = new ConcurrentLinkedQueue<>();
   /* Регистрация в EJB, который предоставляет обновления цены и объёма */
   pvbean.registerServlet(this);
}

/* PriceVolumeBean вызывает этот метод каждую секунду для отправки обновлений */
public void send(double price, int volume) {
   /* Отправка обновлений всем подключенным клиентам */
   for (AsyncContext acontext : requestQueue) {
      try {
         String msg = String.format("%.2f / %d", price, volume);
         PrintWriter writer = acontext.getResponse().getWriter();
         writer.write(msg);
         logger.log(Level.INFO, "Sent: {0}", msg);
         /* Закрытие соединения
          * Клиент (JavaScript) постоянно открывает новое */
         acontext.complete();
      } catch (IOException ex) {
         logger.log(Level.INFO, ex.toString());
      }
   }
}

Сервисный метод переводит клиентские запросы в асинхронный режим и добавляет слушатель к каждому запросу. Слушатель реализован как анонимный класс, который удаляет запрос из очереди, когда сервлет заканчивает писать ответ или когда возникает ошибка. Наконец, метод service добавляет запрос в очередь запросов, созданную в методе init. Сервисный метод описан следующим образом:

@Override
public void doGet(HttpServletRequest request,
                  HttpServletResponse response) {
   response.setContentType("text/html");
   /* Переключение запроса в асинхронный режим */
   final AsyncContext acontext = request.startAsync();
   /* Удаление из очереди после выполнения */
   acontext.addListener(new AsyncListener() {
      public void onComplete(AsyncEvent ae) throws IOException {
         requestQueue.remove(acontext);
      }
      public void onTimeout(AsyncEvent ae) throws IOException {
         requestQueue.remove(acontext);
      }
      public void onError(AsyncEvent ae) throws IOException {
         requestQueue.remove(acontext);
      }
      public void onStartAsync(AsyncEvent ae) throws IOException {}
   });
   /* Добавление в очередь */
   requestQueue.add(acontext);
}

Enterprise-бин

Класс PriceVolumeBean является Enterprise-бином, который использует сервис таймера из контейнера для обновления информации о цене и объёме и вызывает метод send сервлета раз в секунду:

@Startup
@Singleton
public class PriceVolumeBean {
    /* Использование сервиса таймера контейнера */
    @Resource TimerService tservice;
    private DukeETFServlet servlet;
    ...

    @PostConstruct
    public void init() {
        /* Инициализация EJB и создание таймера */
        random = new Random();
        servlet = null;
        tservice.createIntervalTimer(1000, 1000, new TimerConfig());
    }

    public void registerServlet(DukeETFServlet servlet) {
        /* Сервлет для отправки обновлений */
        this.servlet = servlet;
    }

    @Timeout
    public void timeout() {
        /* Настройка цены и объёма и отправка обновления */
        price += 1.0*(random.nextInt(100)-50)/100.0;
        volume += random.nextInt(5000) - 2500;
        if (servlet != null)
            servlet.send(price, volume);
    }
}

Смотрите Использование сервиса таймера в главе 37 «Запуск примеров Enterprise-бина» для получения дополнительной информации о сервисе таймера.

HTML-страница

HTML-страница состоит из таблицы и кода JavaScript. Таблица содержит два поля, на которые ссылается код JavaScript:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>...</head>
<body onload="makeAjaxRequest();">
  ...
  <table>
    ...
    <td id="price">--.--</td>
    ...
    <td id="volume">--</td>
    ...
  </table>
</body>
</html>

Код JavaScript использует API XMLHttpRequest, который обеспечивает функциональность для передачи данных между клиентом и сервером. Скрипт выполняет асинхронный запрос к сервлету и назначает Callback-метод. Когда сервер предоставляет ответ, Callback-метод обновляет поля в таблице и создаёт новый запрос. Код JavaScript выглядит следующим образом:

var ajaxRequest;
function updatePage() {
   if (ajaxRequest.readyState === 4) {
      var arraypv = ajaxRequest.responseText.split("/");
      document.getElementById("price").innerHTML = arraypv[0];
      document.getElementById("volume").innerHTML = arraypv[1];
      makeAjaxRequest();
   }
}
function makeAjaxRequest() {
   ajaxRequest = new XMLHttpRequest();
   ajaxRequest.onreadystatechange = updatePage;
   ajaxRequest.open("GET", "http://localhost:8080/dukeetf/dukeetf",
                    true);
   ajaxRequest.send(null);
}

API XMLHttpRequest поддерживается большинством современных браузеров и широко используется при разработке веб-клиента Ajax (асинхронный JavaScript и XML).

Смотрите Приложение dukeetf2 в главе 19 «Java API для веб-сокетов» для эквивалентной версии этого примера, реализованной с использованием конечной точки веб-сокета.

Запуск приложения dukeetf

В этом разделе описывается, как запустить пример dukeetf в IDE NetBeans и из командной строки.

Здесь рассматриваются следующие темы:

Запуск приложения dukeetf в IDE NetBeans

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. В меню «Файл» выберите «Открыть проект».

  3. В диалоговом окне «Открыть проект» перейдите к:

    tut-install/examples/web/servlet
  4. Выберите каталог dukeetf.

  5. Нажмите Открыть проект.

  6. На вкладке «Проекты» кликните правой кнопкой мыши проект dukeetf и выберите «Выполнить».

    Эта команда собирает и упаковывает приложение в WAR-файл (dukeetf.war), расположенный в каталоге target, развёртывает его на сервере и запускает окно веб-браузера со следующим URL:

    http://localhost:8080/dukeetf/

    Откройте этот же URL в другом веб-браузере, чтобы увидеть, как обе страницы одновременно получают обновления цены и объёма.

Запуск приложения dukeetf с помощью Maven

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. В окне терминала перейдите в:

    tut-install/examples/web/servlet/dukeetf/
  3. Введите следующую команду для развёртывания приложения:

    mvn install
  4. Откройте окно веб-браузера и введите следующий URL:

    http://localhost:8080/dukeetf/

    Откройте этот же URL в другом веб-браузере, чтобы увидеть, как обе страницы одновременно получают обновления цены и объёма.


Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)