Работающий проект можно посмотреть по адресу: http://www.yyakovlev.ru:8081/status.jsp, его исходный текст – здесь. Проект стал дополнением к материалам моих курсов по Java EE.
Целью разработки проекта «Супермаркет» была демонстрация применения основных технологий Java EE 5 на практике: EJB-компоненты и их клиенты, JSP и сервлеты в рамках принципов MVC, JMS. Позднее я добавил Spring в основу приложения-клиента и Spring MVC для web-интерфейса по продуктам. Я начинал разработку, готовясь к последним лекциям своих курсов в 2009 году. На заключительном занятии я демонстрировал создание «с нуля» тогда еще прототипа будущего проекта, на базе Glassfish 2.1 и Netbeans 6.5. Эпизодически возвращаясь к разработке, я получил следующий результат.
В супермаркете имеется N касс (C.R. – cash registers), которые моделируются N потоками выполнения. Касса, запустившись, оказывается в состоянии бездействия (IDLE), затем в ответ на управляющую команду открывается (OPEN), через случайный промежуток времени начинает оформление покупки (SERVICE), добавляя произвольное количество продуктов, по завершении покупки снова оказывается в состоянии OPEN. В ответ на управляющую команду закрытия касса снова IDLE. Управляюшая команда реализуется как JMS-сообщение, отправляемое EJB CRMonitorBean.
Кассы являются удаленными EJB-клиентами, использующими следующие компоненты:
- CRMonitorBean – stateless EJB – получает от касс сообщения об их состоянии через Message-Driven Bean CRMonitorBeanMDB, обновляет и выдает по запросу внутреннюю таблицу состояний касс, и периодически (на основе сервиса EJB Timer) вычисляет, какие кассы должны изменить состояние (закрыться или, наоборот, открыться). Эти вычисления основываются на данных о текущем количестве покупателей в супермаркете. Количество покупателей меняется, когда через CRMonitorBeanMDB приходит новый CustomerEvent.
- PurchaseManagerBean – stateful EJB – выполняет базовые этапы покупки: создание новой покупки (open()), добавление продукта (add()), завершение покупки (close()), отмена (abort()). Данные о покупке накапливаются в полях бина и сохраняются в БД (em.persist()) только в методе close(), однако поле «Количество» для покупаемых продуктов уменьшается сразу в методе add(), чтобы другие кассы не могли «взять больше, чем есть». Во время отмены покупки все «заранее взятое» количество «возвращается назад».
- ProductManagerBean – stateless EJB – выполняет CRUD-операции для продуктов (Product) и их категорий (Category)
- CustomerManagerBean – stateless EJB – выполняет CRUD-операции для клиентов(Customer)
Общая схема обмена сообщениями показана на диаграмме:
Кассы построены на основе Spring с учетом требований:
- Автоматическое определение того, клиентом какого сервера приложений (JBoss или Glassfish) является приложение, на основе библиотек в classpath
- Ожидание в случае недоступности сервера (число попыток и интервал между ними конфигурируются)
- Все основные компоненты, в том числе потоки выполнения (cashRegisterThread, customerTrackerThread, leftIdleCustomerTrackerThread), описываются в файле конфигурации Spring – applicationContext.xml
Основной файл конфигурации applicationContext.xml
Spring-бины основного файла конфигурации перечислены ниже:
Файл конфигурации тестового приложения testApplicationContext.xml
Ниже приведена ER-модель базы данных. БД отражает состав покупок, данные о покупателях, количество и распределение продуктов по категориям. В качестве сервера БД использовался MySql 5.0.
Доступ к БД выполняют EJB, вызывая операции EntityManager’а для JPA-сущностей: Category, Product, Purchase, Purchaseproduct, Customer.
Web-интерфейс для осуществления CRUD-операций с продуктами и их категориями выполнен в 2 вариантах, оба варианта – на базе MVC.
В первом варианте в качестве контроллера выступает simple.controllers.SimpleControllerServlet, подготавливающий данные и осуществляющий перенаправление на одну из jsp в зависимости от параметров запроса:
category-list.jsp
category-add-edit.jsp
product-list.jsp
product-add-edit.jsp
Страница product-list.jsp поддерживает периодическое обновление данных о количестве продуктов и покупок, отправляя XMLHTTPRequest – запросы, принимая JSON-данные и динамически обновляя ячейки таблицы, то есть основана на AJAX.
Второй вариант выполнен на основе Spring MVC 2.5 (без аннотаций) и использует библиотеку тегов displaytag для отображения таблицы с поддержкой многостраничности. Созданные jsp-страницы располагаются в WEB-INF/jsps/springmvc/, а классы контроллеров – в src/java/springmvc/controllers . В данном варианте динамическое обновление данных не реализовано.
Web-интерфейс для отслеживания статуса касс и логов (status.jsp) основан на AJAX (подобно странице со списком продуктов), а цветное изображение касс получает через XSLT-преобразование данных о состоянии касс в формате XML.