Статьи

Flex для разработчиков J2EE: пример сервисов обработки данных Granite

Для разработчиков, которые много лет работали над веб-приложениями 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 .