Рубрика: Проекты


В рамках развития информационной системы управления салоном красоты «МойСалон», уже около 10 лет успешно существующей и внедренной в десятки салонов России, его автором была выдвинута идея создания web-версии программы на базе одного из AJAX-фреймворков. Преследуя исследовательские и познавательные интересы, я занялся созданием макета и для начала разработал приблизительный проект прототипа, максимально независимый от конкретной библиотеки. В качестве двух возможных библиотек были выбраны Vaadin и SmartGWT. Развитие прототипа продолжилось с последней, поскольку именно она обеспечивает наибольшее кеширование страниц на стороне пользователя, максимально разгружая сервер от частых запросов.
Ядром исходной информационной системы является приложение на C++/MFC. Начальной задачей прототипирования было выбрано создание аналога журнала клиентов, одного из нескольких доступных пользователю журналов. Интерфейс журнала клиентов представлен ниже:

Очевидно, что основной элемент интерфейса – это список клиентов, который можно редактировать, а наиболее сложный элемент – это множество форм, доступных через различные закладки. Стоит отметить, что остальные журналы в той или иной степени следуют этому шаблону интерфейса.
В основу разработки был положен шаблон MVP «Model-View-Presenter», представляющий собой усовершенствованный вариант Model-View-Controller, в котором Model избавлена от функций хранения состояния интерфейса, а также может не участвовать в качестве обязательного посредника View-Controller(Presenter).
Создание объектов Model, View, Presenter было основано на шаблонах Factory Method и Abstract Factory, как показано на диаграммах ниже.

Иерархия классов для View (JournalAppGWT и EmployeeJournalApp – начальные классы для SmartGWT и Vaadin, соответственно):

Аналогичный подход использовался для создания объекта-модели. Инициализация модели, представления и объекта Presenter в коде GWT выглядит так:

//Создать подходящую модель
IModelFactory modelFactory = AbstractModelFactory.createModelFactory(AppTypes.getCurrentImpl());
IModel model = modelFactory.createModel(AppTypes.getCurrentType());

//Создать главное View
IViewFactory viewFactory = AbstractViewFactory.createViewFactory(AppTypes.getCurrentImpl());
IJournalView mainView = viewFactory.createMainView(AppTypes.getCurrentType(), model);

//Создать главный Presenter
JournalAppPresenter journalPresenter = new JournalAppPresenter(mainView, model);

//Поместить интерфейс на страницу
RootPanel.get("journalContainer").add((Widget)mainView.asWidget());

В данном коде учитывается разделение объектов по типу журнала (AppTypes.getCurrentType()) и по типу реализации интерфейса, то есть используемой AJAX-библиотеки (AppTypes.getCurrentImpl()). Конкретные объекты модели и представления размещены в соответствующих пакетах, например (SG = Smart Gwt):
com.mysaloon.ui.smartgwt.client.model.EmployeeModelSG
com.mysaloon.ui.smartgwt.client.view.EmployeeJournalViewSG

Абстрактные интерфейсы для model и view:
com.mysaloon.ui.core.view.IJournalView<T>
com.mysaloon.ui.core.model.IModel<T>
- параметризованы типом сущностей, отображаемых в основном списке журнала.

За обработку событий отвечает Presenter. Он реализует интерфейс EventHandler и посылает запросы на обновление View при обработке соответствующих событий. Например:

private void registerForEvents() {
EventBus.getInstance().addHandler(this, DataChangeEvent.class);
//... подписка на другие типы событий ...
}

public void handleEvent(Event e) {
if (e instanceof DataChangeEvent) {
onDataChange((DataChangeEvent)e);
} else if ... ...

private void onDataChange(DataChangeEvent e) {
mainView.refreshListFromModel(selectedItemIndex);
itemsCount = model.getItemList().size();
}

Диаграмма для Presenter’а и связанных с ним классов:

Серверная часть приложения реализована для варианта SmartGWT при помощи средств GWT для создания сервлета, обрабатывающего клиентские запросы. Вот фрагмент клиентского кода:

public class ModelSG<T extends Persistent> implements IModel<T>{

protected JournalServiceAsync journalService = JournalServiceManager.getInstance();
List<T> itemList = new ArrayList<T>();
public void loadItemList() {
//Сгенерировать запрос к удаленному GWT-сервису
Command cmd = new           LoadListCommand(AppTypes.getCurrentType());
journalService.executeRequest(cmd, new    CommandResponseAdapter() {
public void onSuccess(CommandResponse result) {
itemList = new ArrayList<T>(
(Collection<T>)((CollectionResponseData)result.getResponse()).getCollection());
EventBus.getInstance().fireEvent(new DataChangeEvent());
}
});
}

И фрагмент серверного кода, обрабатывающий полученный инкапсулированный в объект-команду запрос:
public CommandResponse executeRequest(Command command)     throws IllegalArgumentException {
CommandResponse response = null;
EmployeeDAO employeeDao = new EmployeeDAO();
//Запрос загрузки списка
if (command instanceof LoadListCommand) {
LoadListCommand loadListCmd = (LoadListCommand)command;
switch (loadListCmd.getAppType()) {
case EMPLOYEE_JOURNAL:
List<Employee> employees = employeeDao.getEmployeeList();
response = new CommandResponse(command,
new CollectionResponseData(((Collection<Persistent>)
(new ArrayList<Persistent>(employees))
)));
break;
}
}
//Запрос возможности удаления записи
else if //... ... ...

EmployeeDAO реализовано при помощи Hibernate …

На этапе раннего развития startup-проекта pozapisi.ru я принял участие в разработке и создал «главную страницу»(dashboard) – сейчас она называется «Личный кабинет», написал систему уведомлений по почте и на дешборде о прошедших событиях (запись клиентов – создание, изменения и т.д.), несколько форм (в частности, начальную версию редактирования профиля). Использовал Stripes , JSTL, Hibernate, Freemarker (для шаблонов уведомлений).
Вот описание проекта и информация о разработчиках

Работающий проект можно посмотреть по адресу: 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 с учетом требований:

  1. Автоматическое определение того, клиентом какого сервера приложений (JBoss или Glassfish) является приложение, на основе библиотек в classpath
  2. Ожидание в случае недоступности сервера (число попыток и интервал между ними конфигурируются)
  3. Все основные компоненты, в том числе потоки выполнения (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.

© 2023 Персональная страница Юрия Яковлева Все права защищены
Локализация: Дизайн сайта