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
ServiceProvider
be a class that implementsRequestFactory
, or should it be an interface that extends it? If the latter, where do I define theServiceProvider
impl, and how do I link it back to all these other classes? - What about all these methods in
ServiceProvider
andDefaultAdditionService
? 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
ValueProxy
s), but you need something more:ServiceLocator
s (i.e., GWT 2.1.1).With
ServiceLocator
you can simply put your service implementation (like your servlet) into a real service instance, instead into an entity object (as you will use onlyValueProxy
s, with no staticgetXyz()
methods) as required by the RF protocol. Note the existence also ofLocator
s, used to externalize all those methods from your server-side entities: not needed if you useValueProxy
everywhere.A
ServiceLocator
looks something like (taken from official docs):You need to annotate your
DefaultAdditionService
also 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 yourRequestContext
you will get an error.To instantiate
RequestContext
s you need to extend theRequestFactory
interface, 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
EntityProxy
andValueProxy
) and server (the real objects, eitherEntity
values or simpleDTO
classes). 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 yourAdditionRequest
will be on your server side, whileAdditionRequestProxy
will be on your client side (see the note in theRequestContext
). Also note that, if you simply use primitive/boxed types as yourRequestContext
params or return types, you will not even need to createValueProxy
s at all, as they are default transportable.The last bit you need, is to wire the
RequestFactoryServlet
on yourweb.xml
. See the docs here. Note that you can extend it if you want to, say, play around with customExceptionHandler
s orServiceLayerDecorator
s, but you don't need to.Speaking about where to put everything:
Locator
s,ServiceLocator
s, service instances, domain objects, andRequestFactoryServlet
extensions, will be on your server-side;RequestContext
,RequestFactory
extensions and all your proxy types will be on the shared-side;RequestFactory
extension 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
;RequestContext
for your requests (annotated with service and locator values);RequestFactory
extension to return yourRequestContext
;RequestContext
(like simpleDTO
s), 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
Entity
s,ID
s,version
s and, of course, all the complex diff logic between client and server, when it comes toCRUD
operations.