Для разработчиков, которые много лет работали над веб-приложениями J2EE, знакомство с Flex покажется очень забавным и знакомым с простотой и мощью ActionScript и инфраструктуры пользовательского интерфейса, а также довольно утомительным и разочаровывающим, когда речь заходит о разработке логики ядра приложения и интеграция с сервером. В некотором смысле, разработка приложений Flex с широко используемыми средами, такими как Cairngorm и BlazeDS, включает в себя большое количество программного кода (бизнес-делегаты, фасады сервисов, преобразования между сущностями JPA и объектами-значениями, …) и напомнит вам о днях Struts и EJB2. Гранит Услуги данных
Проект был начат с (амбициозной) целью предоставления Flex такой же модели разработки, к которой мы привыкли в современных средах J2EE. Функциональность удаленного взаимодействия GDS была разработана с самого начала для поддержки сериализации отдельных объектов JPA / Hibernate и для простого подключения к наиболее важным инфраструктурам J2EE (EJB3, Spring, Seam, Guice). В большинстве случаев это устраняет необходимость в написании и обслуживании фасадов служб и объектов значений на уровне J2EE. Фактически это означает, что клиент Flex может использовать тот же набор серверных служб, что и классическое веб-приложение.
Другая повторяющаяся задача — создание классов модели ActionScript. GraniteDS предоставляет инструмент Gas3, который может автоматически генерировать классы модели ActionScript из модели данных Java. В последнем выпуске GraniteDS 1.1 этот процесс все еще улучшается с помощью плагина Granite Eclipse, который на лету создает необходимые классы ActionScript при создании или изменении объектов JPA в проекте Eclipse. Вам просто нужно написать свою модель данных JPA, и вы можете напрямую использовать сгенерированные классы AS3 на уровне Flex UI.
Это уже большой шаг к более простой интеграции серверов во Flex, но GDS 1.1 предлагает еще более простую модель программирования. На данный момент он нацелен только на JBoss Seam, но интеграция с Spring и EJB3 уже идет. Целью проекта Tide является создание такой же простоты для создания приложений Flex / AIR, которую JBoss Seam привнес в J2EE. Tide практически не требует настройки во время разработки и автоматизирует большую часть программного кода, обычно встречающегося, например, в бизнес-делегатах Cairngorm или в службах поиска.
В отличие от других сред Flex, целью которых является размещение всей бизнес-логики на клиенте, взаимодействия клиент-сервер Tide выполняются исключительно путем вызова методов на предоставляемых сервисах, что позволяет соблюдать границы транзакций, правила безопасности и проверки, определенные этими сервисами.
Tide в основном состоит из библиотеки Flex, которая обеспечивает функциональность, ориентированную на данные:
- Кэш объекта, гарантирующий, что все экземпляры управляемого объекта являются уникальными в контексте Tide. Это позволяет, в частности, поддерживать правильные привязки данных между вызовами удаленных служб.
- Механизм переноса коллекций, обеспечивающий прозрачную инициализацию отложенных коллекций.
- Компонент сбора Flex, интегрированный с компонентом постраничных запросов JBoss Seam, который может использоваться в качестве поставщика данных для компонентов Flex UI и поддерживает удаленную сортировку и фильтрацию.
- Полная интеграция с событиями Seam, как синхронными, так и асинхронными, позволяет клиенту Flex наблюдать за событиями, генерируемыми сервером.
- Валидаторы Flex интегрированы с валидатором Hibernate на стороне сервера, что позволяет проверять сообщения как на лету, так и после удаленных вызовов.
- Поддержка клиентов для шовных разговоров.
- Облегченная основанная на компонентах платформа Flex, которая глубоко интегрирована с другими функциями и может выгодно заменить Cairngorm или PureMVC.
Давайте посмотрим на очень простой пример, чтобы увидеть, как это работает.
Компонент Seam (просто извлеченный из образца бронирования Seam):
@Stateful
@Name("hotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class HotelSearchingAction implements HotelSearching {
@PersistenceContext
private EntityManager em;
private String searchString;
private int pageSize = 10;
private int page;
@DataModel
private List hotels;
public void find() {
page = 0;
queryHotels();
}
...
private void queryHotels() {
hotels = em.createQuery(
"select h from Hotel h where lower(h.name) like #{pattern} " +
"or lower(h.city) like #{pattern} or lower(h.zip) like #{pattern} " +
"or lower(h.address) like #{pattern}")
.setMaxResults(pageSize)
.setFirstResult( page * pageSize )
.getResultList();
}
...
public List getHotels() {
return this.hotels;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
@Factory(value="pattern", scope=ScopeType.EVENT)
public String getSearchPattern() {
return searchString == null ? "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
}
public String getSearchString() {
return searchString;
}
public void setSearchString(String searchString) {
this.searchString = searchString;
}
@Remove
public void destroy() {}
}
MXML-приложение:
<mx:Application creationComplete="init();">
<mx:Script>
[Bindable]
private var tideContext:Context = Seam.getInstance().getSeamContext();
// Components initialization in a static block
{
Seam.getInstance().addComponents([HotelsCtl]);
}
[Bindable] [In]
public var hotels:ArrayCollection;
private function init():void {
tideContext.mainAppUI = this; // Registers the application with Tide
}
private function search(searchString:String):void {
dispatchEvent(new TideUIEvent("search", searchString));
}
</mx:Script>
<mx:Panel>
<mx:TextInput id="fSearchString"/>
<mx:Button label="Search" click="search(fSearchString.text);/>
<mx:DataGrid id="dgHotels" dataProvider="{hotels}">
<mx:columns>
<mx:DataGridColumn headerText="Name" dataField="name"/>
<mx:DataGridColumn headerText="Address" dataField="address"/>
<mx:DataGridColumn headerText="City" dataField="city"/>
</mx:columns>
</mx:DataGrid>
</mx:Panel>
</mx:Application>
Компонент Tide Flex:
import mx.collections.ArrayCollection;
[Name("hotelsCtl")]
[Bindable]
public class HotelsCtl {
[In]
public var hotels:ArrayCollection;
[In]
public var hotelSearch:Object;
[Observer("searchForHotels")]
public function search(searchString:String):void {
hotelSearch.searchString = text;
hotelSearch.find();
}
}
Конечно, это слишком простой пример, но кода, который кажется ненужным, почти нет, хотя есть четкое разделение проблем между пользовательским интерфейсом, клиентским компонентом и удаленным сервисом. Создание приложений Flex вряд ли может быть проще.
Сегодня существует множество вариантов создания богатых интернет-приложений, каждое из которых имеет свои преимущества. Принимая решение о том, какой путь выбрать, вы хотите легко начать работу, не жертвуя при этом возможностью создания надежного, масштабируемого и обслуживаемого приложения. GraniteDS поддерживает этот баланс.
Больше на сайте GDS: http://www.graniteds.org .