Реализация службы GWT RequestFactory для запросов не связанных с сущностью

У меня есть следующий Java-сервлет, который выполняет то, что я называю "Addition Service":

public class AdditionService extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        // The request will have 2 Integers inside its body that need to be
        // added together and returned in the response.
        Integer addend = extractAddendFromRequest(request);
        Integer augend = extractAugendFromRequest(request);

        Integer sum = addend + augend;

        PrintWriter writer = response.getWriter();
        writer.write(sum);
    }
}

Я пытаюсь заставить GWT RequestFactory сделать то же самое (добавить два числа на сервер приложений и вернуть сумму в качестве ответа), используя ValueProxy а также AdditionServiceи сталкиваюсь с несколькими проблемами.

Вот AdditionRequest (уровень клиента), который представляет собой объект значения, содержащий два целых числа, которые необходимо добавить:

// Please note the "tier" (client, shared, server) I have placed all of my Java classes in
// as you read through the code.
public class com.myapp.client.AdditionRequest {
    private Integer addend;
    private Integer augend;

    public AdditionRequest() {
        super();

        this.addend = 0;
        this.augend = 0;
    }

    // Getters & setters for addend/augend.
}

Следующий мой прокси (клиентский уровень):

@ProxyFor(value=AdditionRequest.class)
public interface com.myapp.client.AdditionRequestProxy extends ValueProxy {
    public Integer getAddend();
    public Integer getAugend();
    public void setAddend(Integer a);
    public void setAugend(Integer a);
}

Далее мой сервис API (на общем уровне):

@Service(value=DefaultAdditionService.class)
public interface com.myapp.shared.AdditionService extends RequestContext {
    Request<Integer> sum(AdditionRequest request);
}

Следующая фабрика моих запросов (общий уровень):

public class com.myapp.shared.ServiceProvider implements RequestFactory {
    public AdditionService getAdditionService() {
        return new DefaultAdditionService();
    }

    // ... but since I'm implementing RequestFactory, there's about a dozen
    // other methods GWT is forcing me to implement: find, getEventBus, fire, etc.
    // Do I really need to implement all these?
}

Наконец, где происходит волшебство (уровень сервера):

public class com.myapp.server.DefaultAdditionService implements AdditionService {
    @Override
    public Request<Integer> sum(AdditionRequest request) {
        Integer sum = request.getAddend() + request.getAugend();
        return sum;
    }

    // And because AdditionService extends RequestContext there's another bunch of
    // methods GWT is forcing me to implement here: append, create, isChanged, etc.
    // Do I really need to implement all these?
}

Вот мои вопросы:

  1. Правильна ли моя "уровневая" стратегия? Я упаковал все типы в правильных клиентских / общих / серверных пакетах?
    • Я не думаю, что мои настройки правильны, потому что AdditionService (в общих) ссылках DefaultAdditionService, который находится на сервере, что он не должен делать. Общие типы должны быть в состоянии жить как на клиенте, так и на сервере, но не должны зависеть ни от одного из них...
  2. Должен ServiceProvider быть классом, который реализует RequestFactoryили это должен быть интерфейс, расширяющий его? Если последнее, где я могу определить ServiceProvider импл, а как мне связать его со всеми этими другими классами?
  3. Как насчет всех этих методов в ServiceProvider а также DefaultAdditionService? Нужно ли реализовывать все 20+ этих основных методов GWT? Или я использую API неправильно или не так просто, как мог бы его использовать?
  4. Где здесь указатель услуг? Как?

Я думаю, что я близко, но мне нужна помощь, чтобы пересечь финишную черту здесь. Если бы кто-то мог взять мой код и настроить его, чтобы показать правильный способ использования RF/ValueProxies, я думаю, что это связало бы всю основу для меня... заранее спасибо!

1 ответ

Решение

Если вы хотите использовать RF как простой механизм RPC [*], вы можете (и вы правы: только ValueProxy s), но вам нужно нечто большее: ServiceLocator s (т. е. GWT 2.1.1).

С ServiceLocator вы можете просто поместить реализацию вашей службы (например, ваш сервлет) в реальный экземпляр службы, а не в объект сущности (так как вы будете использовать только ValueProxy с, без статического getXyz() методы) в соответствии с требованиями протокола РФ. Обратите внимание на существование также Locator s, используется для вывода всех этих методов из ваших серверных объектов: не требуется, если вы используете ValueProxy везде.

ServiceLocator выглядит примерно так (взято из официальных документов):

public class DefaultAdditionServiceLocator implements ServiceLocator {
  @Override
  public Object getInstance(Class<?> clazz) {
    try {
      return clazz.newInstance();
    } catch (InstantiationException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }
}

Вы должны аннотировать свой DefaultAdditionService также с параметром локатора, так что RF знает, на что положиться, когда дело доходит до отправки вашего запроса к вашим услугам. Что-то вроде:

@Service(value = DefaultAdditionService.class, locator = DefaultAdditionServiceLocator.class)
public interface com.myapp.shared.AdditionService extends RequestContext {
  // Note here, you need to use the proxy type of your AdditionRequest.
  Request<Integer> sum(AdditionRequestProxy request);
}

Тогда ваш сервис станет самой простой вещью на Земле (не нужно расширять / реализовывать что-либо, связанное с РЧ):

public class com.myapp.server.DefaultAdditionService {
  // The server-side AdditionRequest type.
  public Integer sum(AdditionRequest request) {
    Integer sum = request.getAddend() + request.getAugend();
    return sum;
  }
}

Если вы ошиблись sum() или вы не реализуете метод, объявленный в вашем RequestContext вы получите ошибку.

Для создания экземпляра RequestContext s вам нужно продлить RequestFactory интерфейс, с общедоступным методом фабрики для com.myapp.shared.AdditionService, Что-то вроде:

public interface AdditionServiceRequestFactory extends RequestFactory {
  public com.myapp.shared.AdditionService createAdditionServiceRequestContext();
}

Все ваши клиентские звонки начнутся с этого. Смотрите документы, если не уже.

Теперь RF работает, полностью отделяя объекты, которые вы хотите передать от клиента (используя EntityProxy а также ValueProxy) и сервер (реальные объекты, либо Entity значения или просто DTO классы). Вы будете использовать прокси-типы (т. Е. Интерфейсы, для которых автоматически создаются реализации) везде на уровне client / shared, и вы будете использовать относительный объект домена (тот, на который ссылается @ProxyFor) только на стороне сервера. РФ позаботится обо всем остальном. Так что ваши AdditionRequest будет на вашей стороне сервера, в то время как AdditionRequestProxy будет на вашей стороне клиента (см. примечание в RequestContext). Также обратите внимание, что если вы просто используете примитивные / коробочные типы в качестве RequestContext параметры или возвращаемые типы, вам даже не нужно создавать ValueProxy s вообще, так как они переносимы по умолчанию.

Последнее, что вам нужно, это подключить RequestFactoryServlet на ваше web.xml, Смотрите документы здесь. Обратите внимание, что вы можете расширить его, если хотите, скажем, поиграть с ExceptionHandler с или ServiceLayerDecorator с, но вам не нужно.

Говоря о том, куда все положить:

  • Locator s, ServiceLocator s, экземпляры служб, доменные объекты и RequestFactoryServlet расширения, будут на вашей стороне сервера;
  • RequestContext, RequestFactory расширения и все ваши типы прокси будут на общей стороне;
  • клиентская сторона инициализирует RequestFactory расширение и использовать его для получения экземпляра фабрики для ваших запросов на обслуживание.

В общем... чтобы создать простой механизм RPC с RF, просто:

  • создать свой сервис вместе с ServiceLocator;
  • создать RequestContext для ваших запросов (помечены значениями сервиса и локатора);
  • создать RequestFactory расширение, чтобы вернуть ваш RequestContext;
  • если вы хотите использовать больше, чем примитивные типы в вашем RequestContext (вроде просто DTO s), просто создайте для них клиентские прокси-интерфейсы с пометкой @ProxyFor и помните, где использовать каждый тип;
  • провод все.

Во многом так. Хорошо, я написал слишком много и, вероятно, что-то забыл:)

Для справки смотрите:

[*]: При таком подходе вы переносите логику с ориентированных на данные приложений на сервисы. Вы отказываетесь от использования Entity s, ID s, version s и, конечно, вся сложная логика сравнения между клиентом и сервером, когда дело доходит до CRUD операции.

Другие вопросы по тегам