Поддерживает ли GWT RequestFactory реализацию оптимистического управления параллелизмом?

В приложении GWT я представляю элементы, которые могут редактировать пользователи. Загрузка и сохранение элементов выполняется с помощью фабрики запросов GWT. Теперь я хочу добиться того, чтобы два пользователя одновременно редактировали элемент, и пользователь, сохранивший его первым, выиграл в стиле оптимистичного управления параллелизмом. Это означает, что когда второй пользователь сохраняет свои изменения, серверная часть фабрики запросов распознает, что версия или наличие элемента, хранящегося в серверной части, изменились с тех пор, как он был передан клиенту, а фабрика / серверная часть запросов каким-то образом предотвращает обновление элементов. /сохранено.

Я попытался реализовать это в сервисном методе, который используется для сохранения элементов, но это не сработает, потому что фабрика запросов передает элементы, только что полученные из бэкэнда, с примененными пользовательскими изменениями, что означает, что версии этих элементов являются текущими версиями из бэкэнда. и сравнивать бессмысленно.

Существуют ли какие-либо ловушки в обработке фабрики запросов, которые я мог бы использовать для достижения запрошенного поведения? Любые другие идеи? Или мне нужно использовать GWT-RPC вместо этого...


person Gandalf    schedule 08.10.2011    source источник


Ответы (2)


Нет: http://code.google.com/p/google-web-toolkit/issues/detail?id=6046

Пока предложенный API не будет реализован (EntityLocator, в комментарии №1, но мне непонятно, как информация о версии может быть восстановлена ​​из ее сериализованной формы), вам придется как-то отправить версию обратно на сервер.
Как я уже говорил в вопросе, этого нельзя сделать, просто сделав свойство версии доступным в прокси-сервере и установив его; но вы можете добавить еще одно свойство: его получение всегда будет возвращать null (или аналогичное несуществующее значение), так что установка на стороне клиента значения свойства "истинной" версии всегда приведет к изменение, которое гарантирует, что значение будет отправлено на сервер как часть «разницы свойств»; а на стороне сервера вы можете обрабатывать вещи либо в установщике (когда RequestFactory применяет «свойство diff» и вызывает установщик, если значение отличается от «истинной» версии, затем выдает исключение) или в сервисе методы (сравните версию, отправленную от клиента, которую вы получите от другого получателя, чем тот, который отображается на клиенте, поскольку он всегда должен возвращать null– с «истинной» версией объекта и вызывать ошибку, если они не совпадают).

Что-то типа:

@ProxyFor(MyEntity.class)
interface MyEntityProxy extends EntityProxy {
   String getServerVersion();
   String getClientVersion();
   void setClientVersion(String clientVersion);
   …
}

@Entity
class MyEntity {
   private String clientVersion;
   @Version private String serverVersion;

   public String getServerVersion() { return serverVersion; }
   public String getClientVersion() { return null; }
   public void setClientVersion(String clientVersion) {
      this.clientVersion = clientVersion;
   }

   public void checkVersion() {
      if (Objects.equal(serverVersion, clientVersion)) {
         throw new OptimisticConcurrencyException();
      }
   }
}

Обратите внимание, что я не проверял это, это чистая теория.

person Thomas Broyer    schedule 08.10.2011
comment
Спасибо за указание и совет :) Я попробую этот или комбинированный обходной путь id в отчете, если решение недоступно, прежде чем я начну использовать свое решение. Есть еще много вещей, связанных с Facebook, так что это может занять довольно много времени :D - person Gandalf; 08.10.2011

Мы придумали еще один обходной путь для оптимистичной блокировки в нашем приложении. Поскольку версию нельзя передать с помощью самого прокси (как объяснил Томас), мы передаем ее через HTTP GET в фабрику запросов.

На клиенте:

MyRequestFactory factory = GWT.create( MyRequestFactory.class );
RequestTransport transport = new DefaultRequestTransport() {
        @Override
        public String getRequestUrl() {
            return super.getRequestUrl() + "?version=" + getMyVersion();
        }
    };
factory.initialize(new SimpleEventBus(), transport);

На сервере создаем ServiceLayerDecorator и читаем версию из RequestFactoryServlet.getThreadLocalRequest():

public static class MyServiceLayerDecorator extends ServiceLayerDecorator {
  @Override
  public final <T> T loadDomainObject(final Class<T> clazz, final Object domainId) {
    HttpServletRequest threadLocalRequest = RequestFactoryServlet.getThreadLocalRequest();
    String clientVersion = threadLocalRequest.getParameter("version") );

    T domainObject = super.loadDomainObject(clazz, domainId);
    String serverVersion = ((HasVersion)domainObject).getVersion();

    if ( versionMismatch(serverVersion, clientVersion) )
      report("Version error!");         

    return domainObject;
  }
}

Преимущество состоит в том, что loadDomainObject() вызывается до того, как RF применит какие-либо изменения к объекту домена.

В нашем случае мы просто отслеживаем один объект, поэтому мы используем одну версию, но подход можно распространить на несколько объектов.

person Andrejs    schedule 08.05.2012
comment
Мне кажется, это очень хороший обходной путь. Единственное, что нужно решить, это автоматизировать поиск версии объекта, которая будет отправлена ​​на стороне клиента. - person Gandalf; 11.05.2012