Об авторе
Консультант и автор книг Адам Биен является членом Экспертной группы по Java EE 6 и 7, EJB 3.X, JAX-RS, и JPA 2.X JSRs. Работает с технологией Java, начиная с JDK 1.0, и с Servlets/EJB 1.0. В настоящий момент является архитектором и разработчиком в проектах по Java SE и Java EE. Под его редакцией вышло несколько книг, посвященных JavaFX, J2EE и Java EE. Является автором книг Real World Java EE Patterns—Rethinking Best Practices и Real World Java EE Night Hacks—Dissecting the Business Tier. Адам обладает наградой Java Champion, Top Java Ambassador 2012, JavaOne 2009, 2011 и 2012 Rock Star. Периодически устраивает мастер-классы по Java EE в здании мюнхенского аэропорта (http://airhacks.com).
С лямбда-выражениями и поддержкой асинхронной коммуникации JavaFX представляет новые возможности интеграции для серверных служб.
В организации редко можно встретить изолированные приложения. Корпоративное приложение для настольной системы отображает и манипулирует данными одной и более серверных служб, представленных сервером приложений. Во времена Swing и J2EE сообщение было однонаправленным и синхронным. JavaFX и Java EE 6 и 7 представили разнообразные новые синхронные, асинхронные, push- и pull-стратегии интеграции. В данной статье речь пойдет об интеграции Java EE-служб с Java FX-приложениями.
JavaFX – это Java
JavaFX – это Java. Следовательно, лучшие практики, которые применяются для Swing-приложений, могут применяться и для JavaFX. Интеграция серверных служб не зависит от используемых протоколов и технологий.
Службы имеют разнообразные конфигурации, такие как IP-адреса, порты и файлы свойств. А методы API часто выбрасывают исключения, специфичные для протокола, типа java.rmi.RemoteException и, следовательно, засоряют презентационную логику ненужными деталями. Тонкий упаковщик вокруг собственной службы инкапсулирует детали реализации и представляет более значимые детали интерфейса. Это классический GoF паттерн Adapter.
Возрождение Business Delegate
J2EE-клиенты в существенной степени опирались на технологию Remote Method Invocation, основанную на протоколе Internet Inter-ORB Protocol (RMI-IIOP), а позже, на Java API для XML-based RPC (JAX-RPC) и Java API для XML Web Services (JAX-WS) с серверными службами. Оба API интенсивно используют контролируемые исключения и привязаны к конкретной технологии. Паттерн Business Delegate был необходим для разъединения презентационной логики и протокола:
«Используйте Business Delegate для уменьшения сцепления между клиентами уровня представления и бизнес-службами. Business Delegate скрывает детали реализации бизнес-службы, такие как поиск и доступ к EJB-архитектуре».
Business Delegate часто расширялся фабричным методом, который создавал реальные прокси в варианте по умолчанию и mock-объекты в тестовом окружении. C современными mock-библиотеками, такими как Mockito, Business Delegate можно имитировать напрямую. Business Delegate в контексте JavaFX и JavaEE может быть реализован как адаптер Plain Old Java Object (POJO), который инкапсулирует детали реализации и предоставляет JavaFX удобный интерфейс.
Рисунок 1
Сначала запрос, потом ответ
Отправка блокирующего запроса на сервер приложения и ожидание получения данных – простейшая возможная интеграция с серверной частью. Business Delegate становится службой, которая сообщается с серверной частью, как это показано в Листинге 1:
Листинг 1
Класс MethodMonitoring легко реализовать, протестировать и интегрировать с презентером. Так как метод getMethodStatistics потенциально может блокировать на неограниченное количество времени синхронный вызов из метода UI listener, то использование его сделает UI неотзывчивым.
Асинхронная интеграция
К счастью, JAX-RS 2.0 API так же поддерживает асинхронную модель обратного вызова. Вместо блокирования, метод getMethodStatistics инициирует запрос и регистрирует обратный вызов (см. Листинг 2).
Листинг 2
Обратный вызов трансформирует входящий JsonObject в объект предметной области и передает его в реализацию функции java.util.function.Consumer. Реализация Business Delegate по-прежнему не зависит от JavaFX API и использует Java 8 java.util.function.Consumer в качестве обратного вызова. С Java 7 любой настраиваемый интерфейс или класс могли быть использованы в качестве обратного вызова. Однако с Java 8 реализация презентера JavaFX может быть значительно упрощена, как показано в Листинге 3.
Листинг 3
java.util.function.Consumer может быть реализован как лямбда-выражение, что значительно снижает объем кода. JavaFX – это однопоточный инструментарий для разработки пользовательского интерфейса, поэтому к нему нельзя получить доступ из нескольких асинхронных потоков.
Лямбда-реализация интерфейса java.lang.Runnable передается в метод Platform.runLater и добавляется в очередь событий на выполнение. Как говорится в Javadoc, «Запускайте определенный Runnable на потоке приложения JavaFX в произвольный момент времени в будущем. Этот метод, который может быть вызван из любого потока, ставит Runnable в очередь событий, а затем незамедлительно возвращает программе (функции, методу), вызывающей его. Все Runnable выполняются в том порядке, в котором они ставятся в очередь. Runnable, переданный в метод runLater, будет выполнятся до того, как какой-либо Runnable передастся в последующий вызов runLater».
Метод Platform#runLater не годится для выполнения продолжительных задач, скорее, он служит для обновления компонентов UI JavaFX из асинхронного потока.
Задачи для реальной работы
Platform.runLater не предназначен для выполнения трудных задач, а служит для быстрого обновления узлов JavaFX. Асинхронный вызов долгосрочных методов требует создания потоков и изначально поддерживается классом JavaFX javafx.concurrent.Task. Класс Task реализует интерфейсы Worker и EventTarget, наследует от класса java.util.concurrent.FutureTask, и может рассматриваться как мост между поточностью Java и механизмом событий JavaFX. Task может либо напрямую использоваться Thread как обычный Runnable или передаваться на ExecutorService как Callable. В любом случае, синхронные Java EE API без возможности асинхронного выполнения, например, IIOP, могут быть сначала упакованы в Business Delegate:
Листинг 4
На следующем этапе блокирующий метод Business Delegate упаковывается в Task и становится способным к асинхронному выполнению.
На следующем этапе блокирующий метод Business Delegate упаковывается в Task и становится способным к асинхронному выполнению.
Листинг 5
Класс Task является Runnable и Future, поэтому он может либо напрямую выполняться Thread, либо передаваться в Executor. JavaFX идет с классом javafx.concurrent.Service, который незаметно для пользователя интегрирует поточную обработку с UI, используя свойство привязки bindable. Service по сути является фабричным методом Task:
Листинг 6
Состояние Service, так же как и результаты выполнения Task, становится доступным как bindable-свойства JavaFX:
Листинг 7
Класс Task – удобное средство асинхронной интеграции синхронных унаследованных ресурсов или для выполнения продолжительных процессов на клиентской части.
Блокирование асинхронности
Модель Comet и long polling (длинный опрос) являются не лучшими способами симуляции push HTTP-коммуникации с браузерами. HTTP – это запросно-ответный протокол, поэтому ответ может передаваться только в ответ на запрос. Продавливание данных в браузер через HTTP без первоначального запроса, таким образом, невозможно. Связь в стиле long-polling реализовать легко: браузер инициирует соединение, которое блокируется сервером. Сервер использует блокирующее соединение для передачи данных в браузер, который тут же прерывает соединение. Браузер обрабатывает данные и повторно инициирует последующее блокирующее соединение с сервером. Если информации больше нет, сервер передает в браузер запрос 204.
JavaFX-приложения развертываются на предприятии как отдельные Java-приложения и, следовательно, не ограничиваются HTTP-коммуникацией. Однако зачастую оконечные точки REST доступны для HTML5-клиентов и могут повторно использоваться JavaFX-приложениями. REST и JSON становятся новым низкосортным способом коммуникации с HTML5-клиентами, Java-приложениями и даже низкоуровневыми устройствами.
JavaFX-приложения могут напрямую участвовать в long polling и получать уведомления так же, как и HTML5-клиенты. Единственное отличие между синхронной коммуникацией и long polling – это повторная инициация блокирующего вызова. Повторяющийся опрос может быть напрямую реализован через javafx.concurrent.Service. Вне зависимости от того, удастся выполнение или нет, сервис будет сброшен, а затем перезапущен:
Листинг 8
Интеграция Push
Push-коммуникация – это запросно-ответный стиль связи, только без запросной части; сервер приложений может вытолкнуть данные в любой момент времени. Java Message Service (JMS), WebSockets и memory grids имеют механизм уведомления, который работает по принципу «запустил и забыл» (fire-and-forget), и могут легко интегрироваться с JavaFX.
JSR 356 реализует протокол WebSocket, является частью Java EE 7 и поступает с Java client API. Спецификация WebSocket вводит двунаправленный бинарный протокол и идеально подходит для интеграции клиентов UI. Сообщения WebSocket могут иметь бинарный или текстовый характер и получаются с подклассом Endpoint, как показано в Листинге 9:
Листинг 9
Класс SnapshotEndpoint получает строковое сообщение и конвертирует его с помощью Java Archite cture for XML Binding (JAXB) API. Объект предметной области Snapshot – это аннотированный POJO:
Листинг 10
JSR 356 API поддерживает расширения, поэтому сериализация и десериализация могут быть вынесены в выделенный класс. Мы также не ограничены только JAXB; можно использовать любую доступную презентацию объекта, такую как JSON или сериализацию. SnapshotEndpoint выполняется на клиентской части выделенным потоком WebSocket, поэтому сообщение не может быть использовано только для обновления UI. С методом Platform.runLater, сообщение корректно передается от WebSocket в поток JavaFX.
Endpoint отвечает только за фактическую коммуникацию. В дополнение, WebSocket контейнер требует первоначальной конфигурации и инициализации для того, что реализуется в выделенном классе:
Листинг 11
До сих пор мы практически не использовали интеграционные возможности JavaFX, а лишь фокусировались на различных интеграционных стилях. Однако свойства JavaFX делают синхронную и асинхронную интеграцию особенно интересной.
Интеграция JavaFX
Свойство javafx.beans.property.ObjectProperty упаковывает экземпляр Object и является bindable. Заинтересованный клиент может зарегистрироваться как слушатель или напрямую привязаться к свойству и получать уведомления при замене упакованного экземпляра. (См. раздел «Binding for Narrow Interfaces» статьи в Java Magazine «JavaFX Data Binding for Enterprise Applications.») Вне зависимости от протокола связи и синхронности, ответ несет объект предметной области, который должен быть отражен UI. С ObjectProperty UI может просто напрямую привязываться к значению и получать автоматические уведомления при получении данных. Презентер напрямую привязывается к ObjectProperty, что делает дополнительные методы управления избыточными:
Листинг 12
“Привязывающееся” ObjectProperty устраняет мусор и делает интерфейс предельно «узким». Привязывание может так же применяться к исходящей связи. Презентер меняет состояние объекта или модели предметной области, что приводит к уведомлению службы (Business Delegate). Исходящая коммуникация не требует синхронизации с потоками UI. Асинхронные операции могут напрямую выполняться из потока UI, а длительные операции могут либо упаковываться в Service или асинхронно выполняться в Business Delegate. Однако не все операции UI меняют состояние объекта предметной области. Простые пользовательские действия, такие как “save” или “refresh” могут напрямую передаваться в вызовы метода Business Delegate.
Дальнейшее повышение отзывчивости и упрощение
UI JavaFX управляется событиями и является асинхронным. Так же, Java EE 7 API, такие как JAX-RS 2.0, JMS или WebSockets, имеют асинхронные возможности. Использование JavaFX совместно с асинхронными Java EE 7 API значительно упрощает код. Все операции Business Delegate могут асинхронно выполняться без блокирования UI или даже самого Business Delegate. Паттерн взаимодействия не зависит от протокола связи и может последовательно применяться ко всем асинхронным Java EE 7 API.
Запрос передается на сервер в режиме «запустил и забыл». Вместо ожидания ответа метод обратного вызова регистрируется на обработку ответа. Обратный вызов получает данные, заполняет объект предметной области и заменяет текущий объект с помощью ObjectProperty#set в методе Platform.runLater. Изменения в объекте предметной области транслируются в презентер, который передает сообщение об изменениях всем заинтересованным получателям.
Полная асинхронная коммуникация значительно снижает количество необходимого кода. Использование подхода «запустил и забыл» в обоих направлениях с привязкой данных избавляет от необходимости синхронизации данных между временной моделью и основным состоянием серверной части. Все действия напрямую передаются в Business Delegate и все запросы, поступающие от сервера приложений непосредственно обновляют UI.
Так же опыт взаимодействия (UX) может быть значительно улучшен с помощью полностью асинхронного взаимодействия; вне зависимости от того, как дорого обходится взаимодействие с серверной частью, UI никогда не заблокируется.
Заключение
На первый взгляд, интеграция JavaFX с серверными службами очень сходна со Swing. Platform#runLater эквивалентен javax.swing.SwingUtilities#invokeLater и содержание javafx.concurrent.Service очень сходно с javax.swing.SwingWorker.
Современные Java EE 7 API, привязывание данных JavaFX (см. «JavaFX Data Binding for Enterprise Applications») и возможности Инверсии управления с FXML и Scene Builder (см. «Integrating JavaFX Scene Builder into Enterprise Applications») позволяют нам значительно упростить презентацию логики и ввести последовательные подходы по реализации настольных приложений со множественными представлениями.
С серверными частями Java EE 6 и 7 вы можете продолжить применять асинхронный стиль взаимодействия на серверной части.
3-5 июня пройдет мастер-класс Адама Биена «Java EE: Архитектура, шаблоны и решения». Подробнее о мастер-классе и условиях участия здесь.
ссылка на оригинал статьи http://habrahabr.ru/company/luxoft/blog/218541/
Добавить комментарий