
Основная область программирования для меня, это разработка программного обеспечения для автоматизации учёта в торговли. С возможностью использовать для этого сервлеты Java я столкнулся в 2009 году, когда вместе с последней, вышедшей для десктопа, версией Openbravo POS шёл модуль ресторана для PDA. Основной идей тогда для разработчиков Openbravo POS было, чтобы не усложнять десктопную версию приложения, вынести узкую бизнес-логику в отдельное небольшое приложение, главной изюминкой которого был компактный веб-интерфейс для доступа с любого устройства, а не только того, где может быть запущена Java c Swing. При этом тогда предполагалось, что сервлет не только будет работать с одной и той-же базой, что и десктоп версия, но и контейнер для него будет интегрирован в приложение на десктопе, после запуска которого пользователь автоматический получал доступ к POS в радиусе действия Wi-Fi сети. В рамках комьюнити данная идея дальнейшего развития не получила, но я с 2012 для своих клиентов заложенными тогда принципы продолжаю пользоваться, и в данной статье расскажу читателям, как используя Stripes Framework в связке с jQuery Mobile и ORMLite, получить инструмент для быстрой разработки небольших сервлетов ориентированных на мобильный веб.
Инструментарий
Обычно, когда ищешь инструмент разработки, то стараешься найти что-то универсальное, что можно изучив однажды использовать повторно на протяжении достаточно долгого времени. В 2012 года именно с этим я столкнулся, когда попытался переделать исходный код ресторанного модуля Openbravo POS. В его основе лежал фреймворк Struts 1, изучать написание XML-схем для которого меня совсем не радовало, но на удачу, как раз в тот момент, мне на глаза попался обзор технологий экосистемы Java от RebelLabs. Один из разделов этого обзора был посвящён популярности веб-фреймворков у разработчиков, в эту 10-ку с почётными 2% входил Stripes. С его освоения я и начал изучение основ разработки Java сервлетов. Также мне помогло то, что к тому моменту на Хабре в «Песочнице» уже была обзорная статья про этот фреймворк.
В Stripes всё подчинено логике реакции на контролеры(ActionBean), которые отвечают за получение доступа к модели данных для дальнейшего отображения пользователю через шаблоны страниц JSP. Так как мне нужен был мобильный веб-интерфейс, то для визуальной части я выбрал jQuery Mobile, главный плюс которого, это возможность практически не касаясь JavaScript использовать только HTML5 разметку для построения динамической реакции на действия контролера.
Третьим элементом, отвечающим за работу с моделью данных, мной сначала был выбран совсем простой Persist ORM/DAO, но когда его функционала стало недостаточно, я сменил его на более мощный ORMLite. Главным при выборе стала лёгкость использования уже существующей модели представления и отсутствия необходимости написания SQL-запросов обходясь, как и в случае Stripes, только аннотациями.
Модель-Представление-Контролер
Ключевым в Stripes является 100% ориентированность на концепцию MVC(Model-View-Controller). А главной целью создания данного фреймворка в 2005 году было создание более легковесной реализация MVC концепции для Java EE, чем была реализована в популярном на тот момент фреймворке Struts. К версии 1.5 в 2008 году эта цель авторами Stripes была полностью достигнута и данный фреймворк начал набирать популярность среди разработчиков.
При проектировании структуры исходного кода сервлета следует стараться не выходить за рамки парадигмы Модель-Представление-Контролер, что не очень сложно сделать для большинства задач при создании CRUD-приложений. Для примера, в исходном коде для справочника товаров, это будет выглядеть так:
Источник данных, исходная SQL-таблица
CREATE TABLE PRODUCTS ( ID VARCHAR(255) NOT NULL, NAME VARCHAR(255) NOT NULL, CODE VARCHAR(255) NOT NULL, PRICESELL DOUBLE NOT NULL, CATEGORY VARCHAR(255) NOT NULL, PRIMARY KEY (ID), CONSTRAINT PRODUCTS_FK_1 FOREIGN KEY (CATEGORY) REFERENCES CATEGORIES(ID) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE UNIQUE INDEX PRODUCTS_CODE_INX ON PRODUCTS(CODE); CREATE UNIQUE INDEX PRODUCTS_NAME_INX ON PRODUCTS(NAME);
Модель данных с аннотациями для ORMLite
@DatabaseTable(tableName = "PRODUCTS") public class Product { public static final String ID = "ID"; public static final String NAME = "NAME"; public static final String CODE = "CODE"; public static final String PRICESELL = "PRICESELL"; public static final String CATEGORY = "CATEGORY"; @DatabaseField(generatedId = true, columnName = ID) private UUID id; @DatabaseField(columnName = NAME, unique = true, canBeNull = false) private String name; @DatabaseField(columnName = CODE, unique = true, canBeNull = false) private String code; @DatabaseField(columnName = PRICESELL, canBeNull = false) private BigDecimal pricesell; @DatabaseField(foreign = true, columnName = CATEGORY, foreignColumnName = ProductCategory.ID, foreignAutoRefresh = true, canBeNull = false) private ProductCategory productCategory; // ... // Перечисления методов get и set. // ... }
В начале, с помощью ORMLite, задаётся структура модели данных и её связь с реляционной базой данных(значение полей
tableName и columnName). Затем через геттеры и сеттеры модель предоставляет доступ для остальных классов и методов сервлета.Контроллер Stripes для ввода/вывода данных
public class ProductCreateActionBean extends ProductBaseActionBean { private static final String PRODUCT_CREATE = "/WEB-INF/jsp/product_create.jsp"; @DefaultHandler public Resolution form() { // Открытие формы для заполнения. return new ForwardResolution(PRODUCT_CREATE); } public Resolution add() { // ... // После проверки полученных значений, создание записи в таблице. // ... } @ValidateNestedProperties({ @Validate(on = {"add"}, field = "name", required = true, trim = true, maxlength = 255), @Validate(on = {"add"}, field = "code", required = true, trim = true, minlength = 8, maxlength = 13), @Validate(on = {"add"}, field = "priceSell", required = true, converter = BigDecimalTypeConverter.class), @Validate(field = "productCategory.id", required = true, converter = UUIDTypeConverter.class) }) @Override public void setProduct(Product product) { super.setProduct(product); } }
Контроллер перехватывает данные передаваемые пользователем и обрабатывает их согласно заданных аннотаций. Например, выполняя
Resolution form(), получив значение ключевого поля productCategory.id, он пропускает его через UUIDTypeConverter, проверяя соответствует-ли оно допустимым для UUID критериям.Шаблон JSP с разметкой jQueryMobile и полями Stripes
<!-- Заглавие страницы --> <stripes:layout-component name="content"> <stripes:errors /> <stripes:messages /> <stripes:form action="/ProductCreate.action?add"> <div> <stripes:hidden name="product.productCategory.id" value="${actionBean.product.productCategory.id}"/> </div> <ul data-role="listview" data-inset="true"> <li class="ui-field-contain"> <stripes:label name="label.Product.name" for="productName" /> <input name="product.name" id="productName" type="text" data-clear-btn="true"> </li> <li class="ui-field-contain"> <stripes:label name="label.Product.code" for="productCode" /> <input name="product.code" id="productCode" type="text" data-clear-btn="true"> </li> <li class="ui-field-contain"> <stripes:label name="label.Product.price" for="productPrice"/> <input name="product.priceSell" id="productPrice" type="number" step="0.01" value="0.00" data-clear-btn="true"> </li> <li class="ui-body ui-body-b"> <fieldset class="ui-grid-a"> <div class="ui-block-a"> <sdynattr:submit name="add" data-theme="a"/> </div> <div class="ui-block-b"> <sdynattr:reset name="clear" data-theme="b"/> </div> </fieldset> </li> </ul> </stripes:form> </stripes:layout-component> <!-- Окончание страницы -->
В шаблоне страницы разметка jQuery Mobile также может служить для предварительной проверки вводимых данных, например в визуальной форме предлагая пользователю на мобильном устройстве разные клавиатуры для текста и чисел(в
input параметр type равен "text" или "number").Для чего подходит
В конце прошлого 2013 года RebelLabs был опубликован новый обзор перспектив развития фреймворков в будущем 2014 году. Stripes в данный обзор не вошёл, так как в основном внимание было уделено более полярным чем он фреймворкам, таким как Spring, Grails или Vaadin. Но мне кажется, что если совместно с ним использовать jQuery Mobile и ORMLite, то и он сейчас найдёт свою нишу в инструментарии разработчика на Java. По крайней мере в 4 из 7 предложенных RebelLabs сфер применения с ним можно чувствовать себя вполне уверенно:
- Приложения CRUD — об этом было выше, если всё делать последовательно и по одной схеме, то очень сложно сделать плохо. Главное при проектировании стараться каждое действие контроллера делать максимально полным, но не переполненным.
- Мобильные приложения — тут основная нагрузка лежит на jQuery Mobile, для Java программиста несомненный плюс, что кроме HTML5 можно не трогать JavaScript и CSS.
- Прототипы приложений — это основное для чего я использую эту связку, набрав с 10-ок сервлетов в портфолио, можно отдельные компоненты каждого из них легко между собой тасовать под разные бизнес-процессы.
- Портирование десктоп приложений — а с этого у меня всё и началось, и именно для такого применения я могу порекомендовать в первую очередь сплав этих трёх фрейморков.

Это итог, новый интерфейса с jQuery Mobile версии 1.4 в 2014 году, в сравнении с тем интерфейсом, что был в 2009 году у PDA модуля Openbravo POS.
В качестве примера практической реализации изложенного в статье, небольшой проект CRUD-сервлета для каталог товара: github.com/nordpos-mobi/product-catalog
