Justin Deoliveira wrote:
Hi all,
Gabriel and I have been having good design discussions about the
dispatch system on 1.5.x. I think its time to bring it out publicly.
Woot! Public discussion
Gabriel,
Oh darn, inverse order email stack problem .... (turning things around the other
way around, and replying in order)
Gabriel Roldán wrote:
I'm working on the GetRecords operation for CSW. Got the kvp parsers and
the like, but I'm having a feeling that something is not as good.
Nifty, my sanity check should be that the GetRecords opperation should not need
to see any explict KVP code.
As I see it, both the kvp reading process and the xml request parsing
should produce the same "request" object.
Thinking about that. I would break things down at one level - so as to appear
more "sane" to the casual Java programmer.
Let me try to explain where I think we are right now
- XML Schema used to generate a
- Java Bean (aka the request object)
interface GetMap {
ReferencedEnvelope getBBox(); // required
List<Layer> getLayers(); // required
List<Style> getStyles(); // optional
...
String getHandle(); // user supplied used for error reports
}
This can be filled in via:
1) GTXML parser binding(s) - yeah!
2) WKT "bindings" (seems Justin did this based on java class?)
At the "end" of the day (ie during a request) we end up with one big Java Bean,
with full documentation from the original XML schema.
This has some consequences:
- anything that is not in the WKT must be "optiontional" (ie allowed to be null)
in the java bean
- hopefully if the request xml schema is good this will agree with the XSD idea
of optional
(ie we should be able to check what WKT is optional from the XSD)
We have a couple options on how to take this to our simpel "Pojo Service":
a) request bean approach:
interface WMS_1_3 {
Image getMap( GetMap request );
...
}
b) method reflect approach:
interface WMS_1_3 {
/* most direct request */
Image getMap( ReferencedEnvelope bbox, List<Layer> layers);
/* start mixing in optional parameters */
Image getMap( ReferencedEnvelope bbox, List<Layer> layers, List<Style>
style, ...., String handle );
...
}
So we have a couple implementation options (which Justin and Gabriel are trying
to sort out what will be easiest in the long term).
Reference:
a- receive request -> unpack request -> dispatch -> handle request -> package
result -> response
Implementation Options:
b- HTTP GET Request -> KVP Brindings -> create GetMap bean and set properties ->
WMS.getMap( GetMap bean)
c- HTTP GET Request -> KVP Brindings -> refelct on WMS service bean ->
WMS.getMap( bbox, layers, service, handle )
d- HTTP POST Request -> GTXML Bindings parses into a GetMap bean -> WMS.getMap(
GetMap bean )
e- HTTP POST Request -> GTXML Bindings parses into a GetMap bean -> reflect on
GetMapBean and "dispatch" to reflected method -> WMS.getMap( GetMap bean )
f- HTTP SOAP Request -> ???
g- JMS Message -> ???
Q&A on the details:
- do we need a GetMap bean? yes, to represent the Post request, also useful to
hold data for validating the request
- do we always need a "request" bean? probably not - simple KVP services should
be happy
So where is the conflict (ie what are justin and gabriel talking about?) It
comes down to option d) vs option e)
- do we just use the GetMap bean as a stepping stone (and ask the framework to
reflect on both bean and service?)
- do we just reflect on the services and see who can handle the GetMap bean
Note on Scope: The GetMap bean is actually specific to a specification (WMS1.0
GetMap bean != WMS1.3 GetMap bean). Framework handles "version negotiation" as
part of the GetCapabilities opperation we do not need to worry about this when
handling a request
My rough feeling is that Justin (ie framework) can take on any amount of pain to
make it easy for Gabriel (ie service writers) to work. I am really happy that
they are both sorting this out at the same time (it will keep both honest).
Lets's see how my rough understanding plays out in their email
Nowadays, the kvp reading sets
properties on the Operation and the xml request parsing produces a
"request object". This means that for each request attribute I have to
add a property to the operation inside each setter update the related
property on the request object.
Erk - perhaps we should *always* go through the bean (even if only to isolate
the .isValid() check)?
I know I'm being a bit lazy about the terminology used, it's just that
I'm still recovering from the trip
Heh, nobody is recovered.
The point is that I would like to avoid this duplication and thus let the
operation be cleaner. Do you know if that would be possible with the
current infrastructure?
Unsure, I think we can get their via reflection (and a few of Justin's braincells).
Justin replied:
The idea is that for a post, instead of the dispatcher directly calling
the operation with the request object, the dispatcher would inspect the
request object for its properties and use reflection to set the
associated properties on the operation. The operation never has to know
about the request object. Does that make sense?
I see, so my big overview is wrong I was hoping the "dispatcher" would play
with method arguments, it seems
that these may be handled as service attributes.
Thanks for this feedback, it is very much appreciated as this stuff is
still very young. It would be also good for use to have some of this
conversation on the list as well so that other people can start getting
exposed.-Justin
Gabriel Roldán wrote:
Making the dispatcher set the operation properties from the request object
sounds good, but still doesn't solves the burden of having to take care of each
operation parameter in two places, even if one of them is automatically handled
by the dispatcher, one have to maintain a name/type attribute consistency for
each one.
Trying to understand the duplication of effort ....
- is keeping the same name between method attribute (or is it operation
property) and request property the problem?
(I would ask that we take this pain as it provides a lazy way to document via
reference to original schema)
By the other side, it would be more elegant if an Operation were stateless,
and a lot clearer if it only has one operation method if a "value object"
parameter instead of having to populate the operation with multiple calls.
This is the "conflict" I had w/ the FROG project, it is much nicer to reuse your
service by making each reqeust/response isolated.
So:
- isolated: WMS.getMap( GetMap request )
- isolated: WMS.getMap( ReferenceBounding box, List layers, List styles, ...,
String handle )
- non isolated:
interface GetMapOpperation {
void setBBox( bbox );
void setLayers( List layers );
void setStyles( List styles );
Image opperate();
}
Something that could help (but hide) the problem is:
interface GetMapOpperation {
setRequest( GetMap request ); // unpack the request
Image opperate();
}
Something that would be "evil" is:
interface GetMapOpperation extends GetMap { // has defined the get/set methods
already
Image opperate();
}
This is, I would prefer to follow the value object pattern so an operation's
parameter is a single object encapsulating the request state.
I agree with the value object, and the single value object (so we can check
isValid), but I would like to offer isolation
by explicitly providing normal methods (making this API approachable to the
casual java programmer who is comparing
us to a SOAP toolkit and finding us wanting)
Is it making any sense? going too much against your original design idea?
would it be possible, using some spring magic, to create a request object from
the kvp readers, just like we create it through the xml parsers?
I understand, it is a tradeoff, I like the idea of going to the request object
to feed the framework, and then asking the "dispatcher" to do the reflection
crazyness at the method level.
Justin then punted us onto the email list ...
You make some great points. However, the intent with the request object
parsed from xml is that the user will probably have automatically
generated it from an xml schema, so that they don't have really have to
to maintain the request object.
Check.
If I understand it correctly, you are suggesting a solution from the
opposite angle. Instead of having all the properties on the operation,
make it on the request object and the operation only depends on the
request object. I thought about this solution as well.
I think we got the hang of what Gabriel was suggesting.
I do very much like that this makes the operation stateless. I also like
the original solution due to the fact that it is one less level of
abstraction. All the information is present on the operation and not
through the request object.
So how about my "middle ground", where the operation is stateless, the arguments
capture the elements from the request, and the bean is used as a carrier between
the parser and the dispatch system?
I know this does not promote "perfect" reuse but the following *should* be
considered in order to compete with the WSDL/SOAP solutions:
Option Gabriel:
interface WMS_1_3 {
GetCapabilities getCapabilities() throws ServiceException;
Image getMap( GetMap request ) throws ServiceException;
Object getInfo( GetInfo request ) throws ServiceException;
List getStyles( GetStyles request ) throws ServiceException;
List putStyles( PutStyles request) throws ServiceException;
}
Option Jody:
interface WMS_1_3 {
GetCapabilities getCapabilities() throws ServiceException;
Image getMap( ReferenceBounding box, List layers, List styles, ..., String
handle )
Object getInfo( ReferencePoint location, ... );
....
etc
}
What both gabriel and jody are missing is the "context" from the HTTP request
(ie client lists accepted formats as part of the HTTP request). Throwing this
into the parameters is not fun, attaching it to the request bean is okay,
setting these as properties makes stateful.
So I am a little torn about this. Perhaps Jody who helped me with the
initial design can provide some feedback.
Well here is a big whack of an email Help it is not ...
To help bring people into this discussion, I have started an article
outlining my vision of what developing services with geoserver should
look like. It is not complete, but outlines the design of the dispatch
system we are talking about.
Will be fun to compare this email to your article.
Let us see if we can get any more information.
Look at prior art, SOAP/WSDL and make sure we are "easier". Note easier can be
done with super class if needed.
Jody