I have the following Java servlet that performs what I call the "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);
}
}
I am trying to get GWT's RequestFactory to do the same thing (adding two numbers on the app server and returning the sum as a response) using a ValueProxy and AdditionService, and am running into a few issues.
Here's the AdditionRequest (client tier) which is a value object holding two Integers to be added:
// 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.
}
Next my proxy (client tier):
@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);
}
Next my service API (in the shared tier):
@Service(value=DefaultAdditionService.class)
public interface com.myapp.shared.AdditionService extends RequestContext {
Request<Integer> sum(AdditionRequest request);
}
Next my request factory (shared tier):
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?
}
Finally where the magic happens (server tier):
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?
}
Here are my questions:
- Is my "tier" strategy correct? Have I packaged all the types in the correct client/shared/server packages?
- I don't think my setup is correct because
AdditionService(in shared) referencesDefaultAdditionService, which is on the server, which it shouldn't be doing. Shared types should be able to live both on the client and the server, but not have dependencies on either...
- I don't think my setup is correct because
- Should
ServiceProviderbe a class that implementsRequestFactory, or should it be an interface that extends it? If the latter, where do I define theServiceProviderimpl, and how do I link it back to all these other classes? - What about all these methods in
ServiceProviderandDefaultAdditionService? Do I need to implement all 20+ of these core GWT methods? Or am I using the API incorrectly or not as simply as I could be using it? - Where does service locator factor in here? How?
If you want to use RF as a simple RPC mechanism [*] you can (and you are right: only
ValueProxys), but you need something more:ServiceLocators (i.e., GWT 2.1.1).With
ServiceLocatoryou can simply put your service implementation (like your servlet) into a real service instance, instead into an entity object (as you will use onlyValueProxys, with no staticgetXyz()methods) as required by the RF protocol. Note the existence also ofLocators, used to externalize all those methods from your server-side entities: not needed if you useValueProxyeverywhere.A
ServiceLocatorlooks something like (taken from official docs):You need to annotate your
DefaultAdditionServicealso with a locator param, so RF knows on what to rely when it comes to dispatch your request to your service. Something like:Your service will then be the simplest possible thing on Earth (no need to extend/implement anything RF-related):
If you mispell
sum()or you do not implement a method declared in yourRequestContextyou will get an error.To instantiate
RequestContexts you need to extend theRequestFactoryinterface, with a public factory method forcom.myapp.shared.AdditionService. Something like:All your client calls will start from this. See the docs, if not already.
Now, RF works by totally separating the objects your want to pass from client (using
EntityProxyandValueProxy) and server (the real objects, eitherEntityvalues or simpleDTOclasses). You will use proxy types (i.e., interfaces whom implementations are automatically generated) everywhere in client/shared tier, and you use the relative domain object (the one referenced with@ProxyFor) only on server side. RF will take care of the rest. So yourAdditionRequestwill be on your server side, whileAdditionRequestProxywill be on your client side (see the note in theRequestContext). Also note that, if you simply use primitive/boxed types as yourRequestContextparams or return types, you will not even need to createValueProxys at all, as they are default transportable.The last bit you need, is to wire the
RequestFactoryServleton yourweb.xml. See the docs here. Note that you can extend it if you want to, say, play around with customExceptionHandlers orServiceLayerDecorators, but you don't need to.Speaking about where to put everything:
Locators,ServiceLocators, service instances, domain objects, andRequestFactoryServletextensions, will be on your server-side;RequestContext,RequestFactoryextensions and all your proxy types will be on the shared-side;RequestFactoryextension and use it to obtain the factory instance for your service requests.All in all... to create a simple RPC mechanism with RF, just:
ServiceLocator;RequestContextfor your requests (annotated with service and locator values);RequestFactoryextension to return yourRequestContext;RequestContext(like simpleDTOs), just create client proxy interfaces for them, annotated with@ProxyFor, and remember where to use each type;Much like that. Ok, I wrote too much and probably forgot something :)
For reference, see:
[*]: In this approach you shift your logic from data-oriented to service-oriented app. You give up using
Entitys,IDs,versions and, of course, all the complex diff logic between client and server, when it comes toCRUDoperations.