2013-12-04 3 views
1

У меня есть следующие Java-сервлет, который выполняет то, что я называю «Сложение Service»:Реализация услуг GWT RequestFactory для не-сущности просит

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); 
    } 
} 

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

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

// 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. Является ли мой "уровень" Стратегия правильно? Я упаковал все типы в правильные пакеты client/shared/server?
    • Я не думаю, что мои настройки верны, потому что AdditionService (в общих) ссылках DefaultAdditionService, который находится на сервере, чего он не должен делать. Общие типы должны иметь возможность жить как на клиенте и на сервере, но не имеют зависимости от либо ...
  2. Если ServiceProvider быть класс, который реализует RequestFactory, или он должен быть интерфейс, который расширяет это ? Если последний, где я могу определить ServiceProvider impl, и как мне связать его со всеми этими другими классами?
  3. О всех этих методах в ServiceProvider и DefaultAdditionService? Нужно ли мне реализовать все 20+ этих основных методов GWT? Или я использую API неправильно или не так просто, как я мог бы его использовать?
  4. Где здесь находится коэффициент локатора? Как?

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

+0

Почему не используется стандартный GWT-RPC? – Fedy2

+0

Спасибо @ Fedy2 - хорошо, я буду использовать RF для всех моих других сервисов, ориентированных на CRUD/сущность, для которых в основном используется RF. Но RF также предлагает коммуникацию с не-сущностью сервера (вот что мне нужно здесь, в этом вопросе), и я хотел бы оставаться в соответствии с остальной частью моей архитектуры. – IAmYourFaja

ответ

5

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

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

ServiceLocator выглядит что-то вроде (взятое из official docs):

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 также с локатором парами, поэтому РФ знает о том, что полагаться, когда дело доходит до отправки запроса вашего оказание услуг. Что-то вроде:

@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); 
} 

Ваша служба будет тогда простейшую вещь на Земле (нет необходимости расширять/реализовать что-либо RF-связанной):

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

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

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

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

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

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

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

Говоря о том, где поставить все:

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

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

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

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

Для справки, см:

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

+0

Спасибо, @ Андреа Босколо! (+1, и я бы хотел, чтобы я мог ответить на ваш ответ еще 10 раз!). Фантастический ответ, и это действительно сделало меня более ясным. Однако у меня есть только один крошечный вопрос для вас: – IAmYourFaja

+0

Если 'RequestContext' (например, my 'AdditionService') упакованы внутри общего уровня, который будет присутствовать как на уровне клиента, так и на сервере, тогда как это «законно» для аннотации '@ Service' ссылки на класс уровня сервера, например« DefaultAdditionService »? Я бы подумал, что когда GWT переходит на кросс-компиляцию исходного кода общего уровня, он будет жаловаться на существование типа, упакованного в 'com.myapp.server. *'. Идеи? Еще раз спасибо за такой замечательный ответ! – IAmYourFaja

+3

GWT не будет жаловаться на классы, на которые ссылаются только аннотации; но если вам нужны отдельные проекты/JAR, тогда у вас будет проблема с упаковкой (клиент зависит от совместного доступа, который зависит от сервера). Вы можете отключить зависимость «shared зависит от сервера», используя '@ ServiceName' и' @ ProxyForName' вместо '@ Service' и' @ ProxyFor'. Также обратите внимание, что вы можете использовать 'int' вместо' Integer' в вашем «объекте запроса». Вы также можете передать два аргумента типа «Integer» или «int» в метод 'sum', а не« объект запроса ». –